From 77025d7c5871d380a71cc0500bba571a4551f6c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckgb488=E2=80=9D?= <“kannan_gb@comcast.comgit config --global user.name “kgb488”tesdtgit config --global user.email “kannan_gb@comcast.com> Date: Tue, 21 Feb 2023 21:49:36 +0530 Subject: [PATCH 01/47] statuses parity - table,details, new,delete --- .../src/app/api/statuses.service.ts | 71 +++++++++++ .../src/app/core/core.module.ts | 8 ++ .../status-details.component.html | 26 ++++ .../status-details.component.scss | 12 ++ .../status-details.component.spec.ts | 23 ++++ .../status-details.component.ts | 117 ++++++++++++++++++ .../status-details/status-details.module.ts | 36 ++++++ .../statuses-table.component.html | 9 ++ .../statuses-table.component.scss | 0 .../statuses-table.component.spec.ts | 23 ++++ .../statuses-table.component.ts | 63 ++++++++++ .../statuses-table/statuses-table.module.ts | 32 +++++ .../shared/navigation/navigation.service.ts | 6 + experimental/traffic-portal/src/styles.scss | 30 +++++ 14 files changed, 456 insertions(+) create mode 100644 experimental/traffic-portal/src/app/api/statuses.service.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts diff --git a/experimental/traffic-portal/src/app/api/statuses.service.ts b/experimental/traffic-portal/src/app/api/statuses.service.ts new file mode 100644 index 0000000000..3c0ee507eb --- /dev/null +++ b/experimental/traffic-portal/src/app/api/statuses.service.ts @@ -0,0 +1,71 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { APIService } from './base-api.service'; + +export interface StatusesModel { + description?: string; + id?: number; + lastUpdated?: Date; + name?: string; +} + +@Injectable({ + providedIn: 'root' +}) +export class StatusesService extends APIService { + + /** + * Injects the Angular HTTP client service into the parent constructor. + * @param http The Angular HTTP client service. + */ + constructor(http: HttpClient) { + super(http); + } + + public async getStatuses(idOrName: number | string): Promise; + public async getStatuses(): Promise>; + /** + * @param id Specify either the integral, unique identifier (number. + * @returns The requested status(s). + */ + public async getStatuses(id?: number | string): Promise | StatusesModel> { + const path = "statuses"; + if (id !== undefined) { + let statuses; + statuses = await this.get<[StatusesModel]>(path, undefined, { id: String(id) }).toPromise(); + if (statuses.length < 1) { + throw new Error(`no such statuses '${id}'`); + } + return statuses[0]; + } + return this.get>(path).toPromise(); + } + + /** + * Creating new Status. + * @param data containes name and description for the status. + * @returns The 'response' property of the TO status response. See TO API docs. + */ + public async createStatus(data: StatusesModel) { + const path = "statuses"; + return this.post(path, data).toPromise(); + } + + /** + * Updates status. + * @param data containes name and description for the status., unique identifier thereof. + * @param id The Status ID + */ + public async updateStatus(data: StatusesModel, id: number): Promise { + const path = `statuses/${id}`; + return this.put(path, data).toPromise(); + } + + /** + * Deletes an existing Status. + * @param id The Status ID + */ + public async deleteStatus(id: number): Promise { + return this.delete(`statuses/${id}`).toPromise(); + } +} diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index 84a171c083..cbe830e2bf 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -74,6 +74,14 @@ export const ROUTES: Routes = [ { component: TenantDetailsComponent, path: "tenants/:id"}, { component: PhysLocDetailComponent, path: "phys-locs/:id" }, { component: PhysLocTableComponent, path: "phys-locs" }, + { + path: 'statuses', + loadChildren: () => import('./statuses/statuses-table/statuses-table.module').then(m => m.StatusesTableModule) + }, + { + path: 'statuses/:id', + loadChildren: () => import('./statuses/status-details/status-details.module').then(m => m.StatusDetailsModule) + } ].map(r => ({...r, canActivate: [AuthenticatedGuard]})); /** diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html new file mode 100644 index 0000000000..ce6fa818f4 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -0,0 +1,26 @@ + + + +
+ + + Name + + + + Description + + + + + + + + +
+
\ No newline at end of file diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss new file mode 100644 index 0000000000..56d187902e --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss @@ -0,0 +1,12 @@ +mat-card { + margin: 1em auto; + width: 95%; + min-width: 350px; + + mat-card-content { + display: grid; + grid-template-columns: 1fr; + grid-row-gap: 2em; + margin: 1em auto 10px; + } +} \ No newline at end of file diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts new file mode 100644 index 0000000000..784dd6f1a0 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StatusDetailsComponent } from './status-details.component'; + +describe('StatusDetailsComponent', () => { + let component: StatusDetailsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ StatusDetailsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(StatusDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts new file mode 100644 index 0000000000..4f43132332 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -0,0 +1,117 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { StatusesModel, StatusesService } from 'src/app/api/statuses.service'; +import { DecisionDialogComponent, DecisionDialogData } from 'src/app/shared/dialogs/decision-dialog/decision-dialog.component'; + +@Component({ + selector: 'tp-status-details', + templateUrl: './status-details.component.html', + styleUrls: ['./status-details.component.scss'] +}) +export class StatusDetailsComponent implements OnInit { + + id: string | null = null; + statusDetails: StatusesModel | null = null; + statusDetailsForm!: FormGroup; + loading = false; + submitting = false; + submitted = false; + + constructor( + private route: ActivatedRoute, + private router: Router, + private fb: FormBuilder, + private readonly dialog: MatDialog, + private statusesService: StatusesService) { } + + ngOnInit(): void { + // Form is built here + this.statusDetailsForm = this.fb.group({ + name: ['', Validators.required], + description: ['', Validators.required], + }); + + // Getting id from the route + this.id = this.route.snapshot.paramMap.get('id'); + + // we check whether params is a number if not we shall assume user wants to add a new status. + if (!this.isNew) { + this.loading = true; + this.statusDetailsForm.addControl('id', new FormControl('')); + this.statusDetailsForm.addControl('lastUpdated', new FormControl('')); + this.getStatusDetails(); + } + } + + /* + * Reloads the servers table data. + * @param id is the id passed in route for this page if this is a edit view. + */ + async getStatusDetails(): Promise { + const id = this.id as string + this.statusDetails = await this.statusesService.getStatuses(id); + const data = { + name: this.statusDetails.name, + description: this.statusDetails.description, + lastUpdated: new Date(), + id: this.statusDetails.id + } as any; + this.statusDetailsForm.patchValue(data); + this.loading = false; + } + + // On submitting the form we check for whether we are performing Create or Edit + onSubmit() { + if (this.isNew) { + this.createStatus() + + } else { + this.updateStatus(); + } + } + + // For Creating a new status + createStatus() { + this.statusesService.createStatus(this.statusDetailsForm.value).then((res: any) => { + if (res) { + this.id = res?.id + this.router.navigate([`/core/statuses/${this.id}`]); + } + }) + } + + // For updating the Status + updateStatus() { + this.statusesService.updateStatus(this.statusDetailsForm.value, Number(this.id)); + } + + // Deleteting status + async deleteStatus() { + const ref = this.dialog.open(DecisionDialogComponent, { + data: { + message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails?.name}'.`, + title: `Delete Status: ${this.statusDetails?.name}` + } + }); + + if (await ref.afterClosed().toPromise()) { + const id = Number(this.id); + this.statusesService.deleteStatus(id).then(() => { + this.router.navigate([`/core/statuses`]); + }) + } + + } + + // Title for the page + get title(): string { + return this.isNew ? 'Add New Status' : 'Edit Status'; + } + + // Checking for params to ensure given id is a number + get isNew() { + return this.id === 'new' && isNaN(Number(this.id)); + } +} diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts new file mode 100644 index 0000000000..ca8dc21f54 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts @@ -0,0 +1,36 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { StatusDetailsComponent } from './status-details.component'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import {MatGridListModule} from '@angular/material/grid-list'; +import { MatCardModule } from '@angular/material/card'; +import { SharedModule } from 'src/app/shared/shared.module'; +import { MatButtonModule } from '@angular/material/button'; + +const StatusDetailRouting = RouterModule.forChild([ + { + path: '', + component: StatusDetailsComponent + } +]); + +@NgModule({ + declarations: [ + StatusDetailsComponent + ], + imports: [ + CommonModule, + StatusDetailRouting, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatGridListModule, + MatCardModule, + MatButtonModule, + SharedModule + ] +}) +export class StatusDetailsModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html new file mode 100644 index 0000000000..41a5ef8252 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html @@ -0,0 +1,9 @@ + +
+ +
+ + +
\ No newline at end of file diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts new file mode 100644 index 0000000000..bedd6c46ad --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StatusesTableComponent } from './statuses-table.component'; + +describe('StatusesTableComponent', () => { + let component: StatusesTableComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ StatusesTableComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(StatusesTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts new file mode 100644 index 0000000000..6097b743c9 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -0,0 +1,63 @@ +import { Component, OnInit } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { StatusesModel, StatusesService } from 'src/app/api/statuses.service'; +import { ContextMenuItem } from 'src/app/shared/generic-table/generic-table.component'; + +@Component({ + selector: 'tp-statuses-table', + templateUrl: './statuses-table.component.html', + styleUrls: ['./statuses-table.component.scss'] +}) +export class StatusesTableComponent implements OnInit { + + statuses: any | null = null; + columnDefs = [ + { + field: "name", + headerName: "Name", + hide: false + }, + { + field: "description", + headerName: "Description", + hide: false + }]; + + /** The current search text. */ + public searchText = ""; + + /** Definitions for the context menu items (which act on user data). */ + public contextMenuItems: Array> = [ + { + href: (u: StatusesModel): string => `${u.id}`, + name: "View Status Details" + }, + { + href: (): string => `new`, + name: "Create New Status" + } + ]; + + /** Emits changes to the fuzzy search text. */ + public fuzzySubject = new BehaviorSubject(""); + constructor( + private statusesService: StatusesService + ) { } + + ngOnInit(): void { + this.getStatuses(); + } + + /** Reloads the servers table data. */ + async getStatuses(): Promise { + this.statuses = await this.statusesService.getStatuses(); + } + + /** + * Updates the "search" query parameter in the URL every time the search + * text input changes. + */ + public updateURL(): void { + this.fuzzySubject.next(this.searchText); + } +} diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts new file mode 100644 index 0000000000..bf210b2d3e --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -0,0 +1,32 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { StatusesTableComponent } from './statuses-table.component'; +import { RouterModule } from '@angular/router'; +import { SharedModule } from 'src/app/shared/shared.module'; +import { MatCardModule } from '@angular/material/card'; +import { StatusesService } from 'src/app/api/statuses.service'; +import { FormsModule } from '@angular/forms'; + +const StatusesTableRouting = RouterModule.forChild([ + { + path: '', + component: StatusesTableComponent + } +]); + +@NgModule({ + declarations: [ + StatusesTableComponent + ], + imports: [ + CommonModule, + StatusesTableRouting, + FormsModule, + MatCardModule, + SharedModule + ], + providers:[ + StatusesService + ] +}) +export class StatusesTableModule { } diff --git a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts index c1168c315f..0fd9db48d4 100644 --- a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts +++ b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts @@ -143,6 +143,12 @@ export class NavigationService { name: "Tenants" }], name: "Users" + }, { + children: [{ + href: "/core/statuses", + name: "Statuses" + }], + name: "Configure" }, { children: [{ href: "/core/change-logs", diff --git a/experimental/traffic-portal/src/styles.scss b/experimental/traffic-portal/src/styles.scss index cf3064cd55..70a755f396 100644 --- a/experimental/traffic-portal/src/styles.scss +++ b/experimental/traffic-portal/src/styles.scss @@ -152,3 +152,33 @@ button { bottom: 16px; right: 16px; } +// Breadcrumbs +/* Style the list */ +ul.breadcrumb { + padding: 5px 0px 10px 0px; + margin-bottom: 1.5em; + list-style: none; + border-bottom: 1px solid #efefef; + } + + /* Display list items side by side */ + ul.breadcrumb li { + display: inline; + font-size: 16px; + } + + /* Add a slash symbol (/) before/behind each list item */ + ul.breadcrumb li+li:before { + padding: 8px; + content: "/\00a0"; + } + + /* Add a color to all links inside the list */ + ul.breadcrumb li a { + text-decoration: none; + } + + /* Add a color on mouse-over */ + ul.breadcrumb li a:hover { + text-decoration: underline; + } \ No newline at end of file From 26211f529749d1485112d5388420708b24512f6a Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 27 Feb 2023 12:27:00 +0530 Subject: [PATCH 02/47] monu statuses moved under servers --- .../src/app/shared/navigation/navigation.service.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts index 0fd9db48d4..4ace2b1d86 100644 --- a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts +++ b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts @@ -117,7 +117,12 @@ export class NavigationService { }, { href: "/core/phys-locs", name: "Physical Locations" - }, { + }, + { + href: "/core/statuses", + name: "Statuses" + }, + { children: [{ href: "/core/cache-groups", name: "Cache Groups" @@ -143,12 +148,6 @@ export class NavigationService { name: "Tenants" }], name: "Users" - }, { - children: [{ - href: "/core/statuses", - name: "Statuses" - }], - name: "Configure" }, { children: [{ href: "/core/change-logs", From 057fa66955bfea0c9aa4c132ccd751ff709c90f2 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 27 Feb 2023 12:29:08 +0530 Subject: [PATCH 03/47] breadcrumbs removed,license added,model from lib --- .../src/app/api/statuses.service.ts | 28 ++++++---------- .../status-details.component.html | 18 ++++++++--- .../status-details.component.scss | 13 ++++++++ .../status-details.component.spec.ts | 13 ++++++++ .../status-details.component.ts | 18 +++++++++-- .../status-details/status-details.module.ts | 17 ++++++++++ .../statuses-table.component.html | 13 ++++++++ .../statuses-table.component.scss | 13 ++++++++ .../statuses-table.component.spec.ts | 13 ++++++++ .../statuses-table.component.ts | 26 +++++++++++---- .../statuses-table/statuses-table.module.ts | 13 ++++++++ experimental/traffic-portal/src/styles.scss | 32 +------------------ 12 files changed, 156 insertions(+), 61 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/statuses.service.ts b/experimental/traffic-portal/src/app/api/statuses.service.ts index 3c0ee507eb..4b180f1b73 100644 --- a/experimental/traffic-portal/src/app/api/statuses.service.ts +++ b/experimental/traffic-portal/src/app/api/statuses.service.ts @@ -1,17 +1,9 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { ResponseStatus } from 'trafficops-types'; import { APIService } from './base-api.service'; -export interface StatusesModel { - description?: string; - id?: number; - lastUpdated?: Date; - name?: string; -} - -@Injectable({ - providedIn: 'root' -}) +@Injectable() export class StatusesService extends APIService { /** @@ -22,23 +14,23 @@ export class StatusesService extends APIService { super(http); } - public async getStatuses(idOrName: number | string): Promise; - public async getStatuses(): Promise>; + public async getStatuses(idOrName: number | string): Promise; + public async getStatuses(): Promise>; /** * @param id Specify either the integral, unique identifier (number. * @returns The requested status(s). */ - public async getStatuses(id?: number | string): Promise | StatusesModel> { + public async getStatuses(id?: number | string): Promise | ResponseStatus> { const path = "statuses"; if (id !== undefined) { let statuses; - statuses = await this.get<[StatusesModel]>(path, undefined, { id: String(id) }).toPromise(); + statuses = await this.get<[ResponseStatus]>(path, undefined, { id: String(id) }).toPromise(); if (statuses.length < 1) { throw new Error(`no such statuses '${id}'`); } return statuses[0]; } - return this.get>(path).toPromise(); + return this.get>(path).toPromise(); } /** @@ -46,9 +38,9 @@ export class StatusesService extends APIService { * @param data containes name and description for the status. * @returns The 'response' property of the TO status response. See TO API docs. */ - public async createStatus(data: StatusesModel) { + public async createStatus(data: ResponseStatus) { const path = "statuses"; - return this.post(path, data).toPromise(); + return this.post(path, data).toPromise(); } /** @@ -56,7 +48,7 @@ export class StatusesService extends APIService { * @param data containes name and description for the status., unique identifier thereof. * @param id The Status ID */ - public async updateStatus(data: StatusesModel, id: number): Promise { + public async updateStatus(data: ResponseStatus, id: number): Promise { const path = `statuses/${id}`; return this.put(path, data).toPromise(); } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index ce6fa818f4..6fd09df714 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -1,8 +1,18 @@ + - +

{{title}}

diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss index 56d187902e..d04bcfa38d 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss @@ -1,3 +1,16 @@ +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ mat-card { margin: 1em auto; width: 95%; diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 784dd6f1a0..f001ac751b 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -1,3 +1,16 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { StatusDetailsComponent } from './status-details.component'; diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 4f43132332..bd51f32a06 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -1,9 +1,23 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; -import { StatusesModel, StatusesService } from 'src/app/api/statuses.service'; +import { StatusesService } from 'src/app/api/statuses.service'; import { DecisionDialogComponent, DecisionDialogData } from 'src/app/shared/dialogs/decision-dialog/decision-dialog.component'; +import { ResponseStatus } from 'trafficops-types'; @Component({ selector: 'tp-status-details', @@ -13,7 +27,7 @@ import { DecisionDialogComponent, DecisionDialogData } from 'src/app/shared/dial export class StatusDetailsComponent implements OnInit { id: string | null = null; - statusDetails: StatusesModel | null = null; + statusDetails: ResponseStatus | null = null; statusDetailsForm!: FormGroup; loading = false; submitting = false; diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts index ca8dc21f54..bae1f035ca 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts @@ -1,3 +1,16 @@ +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { StatusDetailsComponent } from './status-details.component'; @@ -9,6 +22,7 @@ import {MatGridListModule} from '@angular/material/grid-list'; import { MatCardModule } from '@angular/material/card'; import { SharedModule } from 'src/app/shared/shared.module'; import { MatButtonModule } from '@angular/material/button'; +import { StatusesService } from 'src/app/api/statuses.service'; const StatusDetailRouting = RouterModule.forChild([ { @@ -31,6 +45,9 @@ const StatusDetailRouting = RouterModule.forChild([ MatCardModule, MatButtonModule, SharedModule + ], + providers:[ + StatusesService ] }) export class StatusDetailsModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html index 41a5ef8252..16f44cdac8 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html @@ -1,3 +1,16 @@ +
> = [ + public contextMenuItems: Array> = [ { - href: (u: StatusesModel): string => `${u.id}`, + href: (u: ResponseStatus): string => `${u.id}`, name: "View Status Details" }, { @@ -41,7 +55,7 @@ export class StatusesTableComponent implements OnInit { /** Emits changes to the fuzzy search text. */ public fuzzySubject = new BehaviorSubject(""); constructor( - private statusesService: StatusesService + private serverService: ServerService, ) { } ngOnInit(): void { @@ -50,7 +64,7 @@ export class StatusesTableComponent implements OnInit { /** Reloads the servers table data. */ async getStatuses(): Promise { - this.statuses = await this.statusesService.getStatuses(); + this.statuses = await this.serverService.getStatuses(); } /** diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts index bf210b2d3e..0121f45e1f 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -1,3 +1,16 @@ +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { StatusesTableComponent } from './statuses-table.component'; diff --git a/experimental/traffic-portal/src/styles.scss b/experimental/traffic-portal/src/styles.scss index 70a755f396..8a15bf53dd 100644 --- a/experimental/traffic-portal/src/styles.scss +++ b/experimental/traffic-portal/src/styles.scss @@ -151,34 +151,4 @@ button { position: fixed; bottom: 16px; right: 16px; -} -// Breadcrumbs -/* Style the list */ -ul.breadcrumb { - padding: 5px 0px 10px 0px; - margin-bottom: 1.5em; - list-style: none; - border-bottom: 1px solid #efefef; - } - - /* Display list items side by side */ - ul.breadcrumb li { - display: inline; - font-size: 16px; - } - - /* Add a slash symbol (/) before/behind each list item */ - ul.breadcrumb li+li:before { - padding: 8px; - content: "/\00a0"; - } - - /* Add a color to all links inside the list */ - ul.breadcrumb li a { - text-decoration: none; - } - - /* Add a color on mouse-over */ - ul.breadcrumb li a:hover { - text-decoration: underline; - } \ No newline at end of file +} \ No newline at end of file From 07869be262185b37a5c8d1710a9358ba6061aedb Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:59:35 +0530 Subject: [PATCH 04/47] lint fixes --- .../src/app/api/statuses.service.ts | 13 +- .../src/app/core/core.module.ts | 8 +- .../status-details.component.html | 10 +- .../status-details.component.scss | 4 +- .../status-details.component.ts | 221 +++++++++--------- .../status-details/status-details.module.ts | 68 +++--- .../statuses-table.component.html | 2 +- .../statuses-table.component.scss | 2 +- .../statuses-table.component.ts | 105 ++++----- .../statuses-table/statuses-table.module.ts | 52 +++-- .../shared/navigation/navigation.service.ts | 2 +- 11 files changed, 249 insertions(+), 238 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/statuses.service.ts b/experimental/traffic-portal/src/app/api/statuses.service.ts index 4b180f1b73..1cb3c24201 100644 --- a/experimental/traffic-portal/src/app/api/statuses.service.ts +++ b/experimental/traffic-portal/src/app/api/statuses.service.ts @@ -1,13 +1,15 @@ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { ResponseStatus } from 'trafficops-types'; -import { APIService } from './base-api.service'; +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { ResponseStatus } from "trafficops-types"; + +import { APIService } from "./base-api.service"; @Injectable() export class StatusesService extends APIService { /** * Injects the Angular HTTP client service into the parent constructor. + * * @param http The Angular HTTP client service. */ constructor(http: HttpClient) { @@ -35,6 +37,7 @@ export class StatusesService extends APIService { /** * Creating new Status. + * * @param data containes name and description for the status. * @returns The 'response' property of the TO status response. See TO API docs. */ @@ -45,6 +48,7 @@ export class StatusesService extends APIService { /** * Updates status. + * * @param data containes name and description for the status., unique identifier thereof. * @param id The Status ID */ @@ -55,6 +59,7 @@ export class StatusesService extends APIService { /** * Deletes an existing Status. + * * @param id The Status ID */ public async deleteStatus(id: number): Promise { diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index cbe830e2bf..3b3abe1f81 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -75,12 +75,12 @@ export const ROUTES: Routes = [ { component: PhysLocDetailComponent, path: "phys-locs/:id" }, { component: PhysLocTableComponent, path: "phys-locs" }, { - path: 'statuses', - loadChildren: () => import('./statuses/statuses-table/statuses-table.module').then(m => m.StatusesTableModule) + path: "statuses", + loadChildren: async () => import("./statuses/statuses-table/statuses-table.module").then(m => m.StatusesTableModule) }, { - path: 'statuses/:id', - loadChildren: () => import('./statuses/status-details/status-details.module').then(m => m.StatusDetailsModule) + path: "statuses/:id", + loadChildren: async () => import("./statuses/status-details/status-details.module").then(m => m.StatusDetailsModule) } ].map(r => ({...r, canActivate: [AuthenticatedGuard]})); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 6fd09df714..34e3d3f109 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -16,13 +16,13 @@

{{title}}

- + Name - + - + Description - + @@ -33,4 +33,4 @@

{{title}}

- \ No newline at end of file + diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss index d04bcfa38d..0a24e3195e 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss @@ -19,7 +19,7 @@ mat-card { mat-card-content { display: grid; grid-template-columns: 1fr; - grid-row-gap: 2em; + row-gap: 2em; margin: 1em auto 10px; } -} \ No newline at end of file +} diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index bd51f32a06..60b5969532 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -11,121 +11,122 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Component, OnInit } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import { MatDialog } from '@angular/material/dialog'; -import { ActivatedRoute, Router } from '@angular/router'; -import { StatusesService } from 'src/app/api/statuses.service'; -import { DecisionDialogComponent, DecisionDialogData } from 'src/app/shared/dialogs/decision-dialog/decision-dialog.component'; -import { ResponseStatus } from 'trafficops-types'; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; +import { MatDialog } from "@angular/material/dialog"; +import { ActivatedRoute, Router } from "@angular/router"; +import { ResponseStatus } from "trafficops-types"; + +import { StatusesService } from "src/app/api/statuses.service"; +import { DecisionDialogComponent, DecisionDialogData } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; @Component({ - selector: 'tp-status-details', - templateUrl: './status-details.component.html', - styleUrls: ['./status-details.component.scss'] + selector: "tp-status-details", + templateUrl: "./status-details.component.html", + styleUrls: ["./status-details.component.scss"] }) export class StatusDetailsComponent implements OnInit { - id: string | null = null; - statusDetails: ResponseStatus | null = null; - statusDetailsForm!: FormGroup; - loading = false; - submitting = false; - submitted = false; - - constructor( - private route: ActivatedRoute, - private router: Router, - private fb: FormBuilder, - private readonly dialog: MatDialog, - private statusesService: StatusesService) { } - - ngOnInit(): void { - // Form is built here - this.statusDetailsForm = this.fb.group({ - name: ['', Validators.required], - description: ['', Validators.required], - }); - - // Getting id from the route - this.id = this.route.snapshot.paramMap.get('id'); - - // we check whether params is a number if not we shall assume user wants to add a new status. - if (!this.isNew) { - this.loading = true; - this.statusDetailsForm.addControl('id', new FormControl('')); - this.statusDetailsForm.addControl('lastUpdated', new FormControl('')); - this.getStatusDetails(); - } - } - - /* - * Reloads the servers table data. + id: string | null = null; + statusDetails: ResponseStatus | null = null; + statusDetailsForm!: FormGroup; + loading = false; + submitting = false; + submitted = false; + + constructor( + private readonly route: ActivatedRoute, + private readonly router: Router, + private readonly fb: FormBuilder, + private readonly dialog: MatDialog, + private readonly statusesService: StatusesService) { } + + ngOnInit(): void { + // Form is built here + this.statusDetailsForm = this.fb.group({ + name: ["", Validators.required], + description: ["", Validators.required], + }); + + // Getting id from the route + this.id = this.route.snapshot.paramMap.get("id"); + + // we check whether params is a number if not we shall assume user wants to add a new status. + if (!this.isNew) { + this.loading = true; + this.statusDetailsForm.addControl("id", new FormControl("")); + this.statusDetailsForm.addControl("lastUpdated", new FormControl("")); + this.getStatusDetails(); + } + } + + /* + * Reloads the servers table data. * @param id is the id passed in route for this page if this is a edit view. */ - async getStatusDetails(): Promise { - const id = this.id as string - this.statusDetails = await this.statusesService.getStatuses(id); - const data = { - name: this.statusDetails.name, - description: this.statusDetails.description, - lastUpdated: new Date(), - id: this.statusDetails.id - } as any; - this.statusDetailsForm.patchValue(data); - this.loading = false; - } - - // On submitting the form we check for whether we are performing Create or Edit - onSubmit() { - if (this.isNew) { - this.createStatus() - - } else { - this.updateStatus(); - } - } - - // For Creating a new status - createStatus() { - this.statusesService.createStatus(this.statusDetailsForm.value).then((res: any) => { - if (res) { - this.id = res?.id - this.router.navigate([`/core/statuses/${this.id}`]); - } - }) - } - - // For updating the Status - updateStatus() { - this.statusesService.updateStatus(this.statusDetailsForm.value, Number(this.id)); - } - - // Deleteting status - async deleteStatus() { - const ref = this.dialog.open(DecisionDialogComponent, { - data: { - message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails?.name}'.`, - title: `Delete Status: ${this.statusDetails?.name}` - } - }); - - if (await ref.afterClosed().toPromise()) { - const id = Number(this.id); - this.statusesService.deleteStatus(id).then(() => { - this.router.navigate([`/core/statuses`]); - }) - } - - } - - // Title for the page - get title(): string { - return this.isNew ? 'Add New Status' : 'Edit Status'; - } - - // Checking for params to ensure given id is a number - get isNew() { - return this.id === 'new' && isNaN(Number(this.id)); - } + async getStatusDetails(): Promise { + const id = this.id as string; // id Type 'null' is not assignable to type 'string' + this.statusDetails = await this.statusesService.getStatuses(id); + const data:ResponseStatus = { + name: this.statusDetails.name, + description: this.statusDetails.description, + lastUpdated: new Date(), + id: this.statusDetails.id + } + this.statusDetailsForm.patchValue(data); + this.loading = false; + } + + // On submitting the form we check for whether we are performing Create or Edit + onSubmit() { + if (this.isNew) { + this.createStatus(); + + } else { + this.updateStatus(); + } + } + + // For Creating a new status + createStatus() { + this.statusesService.createStatus(this.statusDetailsForm.value).then((res: any) => { + if (res) { + this.id = res?.id; + this.router.navigate([`/core/statuses/${this.id}`]); + } + }); + } + + // For updating the Status + updateStatus() { + this.statusesService.updateStatus(this.statusDetailsForm.value, Number(this.id)); + } + + // Deleteting status + async deleteStatus() { + const ref = this.dialog.open(DecisionDialogComponent, { + data: { + message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails?.name}'.`, + title: `Delete Status: ${this.statusDetails?.name}` + } + }); + + if (await ref.afterClosed().toPromise()) { + const id = Number(this.id); + this.statusesService.deleteStatus(id).then(() => { + this.router.navigate(["/core/statuses"]); + }); + } + + } + + // Title for the page + get title(): string { + return this.isNew ? "Add New Status" : "Edit Status"; + } + + // Checking for params to ensure given id is a number + get isNew() { + return this.id === "new" && isNaN(Number(this.id)); + } } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts index bae1f035ca..3871f328a7 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts @@ -11,43 +11,45 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { StatusDetailsComponent } from './status-details.component'; -import { RouterModule } from '@angular/router'; -import { ReactiveFormsModule } from '@angular/forms'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import {MatGridListModule} from '@angular/material/grid-list'; -import { MatCardModule } from '@angular/material/card'; -import { SharedModule } from 'src/app/shared/shared.module'; -import { MatButtonModule } from '@angular/material/button'; -import { StatusesService } from 'src/app/api/statuses.service'; +import { CommonModule } from "@angular/common"; +import { NgModule } from "@angular/core"; +import { ReactiveFormsModule } from "@angular/forms"; +import { MatButtonModule } from "@angular/material/button"; +import { MatCardModule } from "@angular/material/card"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import {MatGridListModule} from "@angular/material/grid-list"; +import { MatInputModule } from "@angular/material/input"; +import { RouterModule } from "@angular/router"; + +import { StatusesService } from "src/app/api/statuses.service"; +import { SharedModule } from "src/app/shared/shared.module"; + +import { StatusDetailsComponent } from "./status-details.component"; const StatusDetailRouting = RouterModule.forChild([ - { - path: '', - component: StatusDetailsComponent - } + { + path: "", + component: StatusDetailsComponent + } ]); @NgModule({ - declarations: [ - StatusDetailsComponent - ], - imports: [ - CommonModule, - StatusDetailRouting, - ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - MatGridListModule, - MatCardModule, - MatButtonModule, - SharedModule - ], - providers:[ - StatusesService - ] + declarations: [ + StatusDetailsComponent + ], + imports: [ + CommonModule, + StatusDetailRouting, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatGridListModule, + MatCardModule, + MatButtonModule, + SharedModule + ], + providers:[ + StatusesService + ] }) export class StatusDetailsModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html index 16f44cdac8..9ca9f8d8c9 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html @@ -19,4 +19,4 @@ - \ No newline at end of file + diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss index 8daa74ec61..ebe77042d3 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss @@ -10,4 +10,4 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -*/ \ No newline at end of file +*/ diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 51c2d97e33..7c5616c0da 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -11,67 +11,68 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Component, OnInit } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; -import { ServerService } from 'src/app/api'; -import { ContextMenuItem } from 'src/app/shared/generic-table/generic-table.component'; -import { ResponseStatus } from 'trafficops-types'; +import { Component, OnInit } from "@angular/core"; +import { BehaviorSubject } from "rxjs"; +import { ResponseStatus } from "trafficops-types"; + +import { ServerService } from "src/app/api"; +import { ContextMenuItem } from "src/app/shared/generic-table/generic-table.component"; @Component({ - selector: 'tp-statuses-table', - templateUrl: './statuses-table.component.html', - styleUrls: ['./statuses-table.component.scss'] + selector: "tp-statuses-table", + templateUrl: "./statuses-table.component.html", + styleUrls: ["./statuses-table.component.scss"] }) export class StatusesTableComponent implements OnInit { - statuses: ResponseStatus[] | null = null; - columnDefs = [ - { - field: "name", - headerName: "Name", - hide: false - }, - { - field: "description", - headerName: "Description", - hide: false - }]; + statuses: ResponseStatus[] | null = null; + columnDefs = [ + { + field: "name", + headerName: "Name", + hide: false + }, + { + field: "description", + headerName: "Description", + hide: false + }]; - /** The current search text. */ - public searchText = ""; + /** The current search text. */ + public searchText = ""; - /** Definitions for the context menu items (which act on user data). */ - public contextMenuItems: Array> = [ - { - href: (u: ResponseStatus): string => `${u.id}`, - name: "View Status Details" - }, - { - href: (): string => `new`, - name: "Create New Status" - } - ]; + /** Definitions for the context menu items (which act on user data). */ + public contextMenuItems: Array> = [ + { + href: (u: ResponseStatus): string => `${u.id}`, + name: "View Status Details" + }, + { + href: (): string => "new", + name: "Create New Status" + } + ]; - /** Emits changes to the fuzzy search text. */ - public fuzzySubject = new BehaviorSubject(""); - constructor( - private serverService: ServerService, - ) { } + /** Emits changes to the fuzzy search text. */ + public fuzzySubject = new BehaviorSubject(""); + constructor( + private readonly serverService: ServerService, + ) { } - ngOnInit(): void { - this.getStatuses(); - } + ngOnInit(): void { + this.getStatuses(); + } - /** Reloads the servers table data. */ - async getStatuses(): Promise { - this.statuses = await this.serverService.getStatuses(); - } + /** Reloads the servers table data. */ + async getStatuses(): Promise { + this.statuses = await this.serverService.getStatuses(); + } - /** - * Updates the "search" query parameter in the URL every time the search - * text input changes. - */ - public updateURL(): void { - this.fuzzySubject.next(this.searchText); - } + /** + * Updates the "search" query parameter in the URL every time the search + * text input changes. + */ + public updateURL(): void { + this.fuzzySubject.next(this.searchText); + } } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts index 0121f45e1f..35676e170c 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -11,35 +11,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { StatusesTableComponent } from './statuses-table.component'; -import { RouterModule } from '@angular/router'; -import { SharedModule } from 'src/app/shared/shared.module'; -import { MatCardModule } from '@angular/material/card'; -import { StatusesService } from 'src/app/api/statuses.service'; -import { FormsModule } from '@angular/forms'; +import { CommonModule } from "@angular/common"; +import { NgModule } from "@angular/core"; +import { FormsModule } from "@angular/forms"; +import { MatCardModule } from "@angular/material/card"; +import { RouterModule } from "@angular/router"; + +import { StatusesService } from "src/app/api/statuses.service"; +import { SharedModule } from "src/app/shared/shared.module"; + +import { StatusesTableComponent } from "./statuses-table.component"; const StatusesTableRouting = RouterModule.forChild([ - { - path: '', - component: StatusesTableComponent - } + { + path: "", + component: StatusesTableComponent + } ]); @NgModule({ - declarations: [ - StatusesTableComponent - ], - imports: [ - CommonModule, - StatusesTableRouting, - FormsModule, - MatCardModule, - SharedModule - ], - providers:[ - StatusesService - ] + declarations: [ + StatusesTableComponent + ], + imports: [ + CommonModule, + StatusesTableRouting, + FormsModule, + MatCardModule, + SharedModule + ], + providers:[ + StatusesService + ] }) export class StatusesTableModule { } diff --git a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts index 4ace2b1d86..e7e44b2e54 100644 --- a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts +++ b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts @@ -121,7 +121,7 @@ export class NavigationService { { href: "/core/statuses", name: "Statuses" - }, + }, { children: [{ href: "/core/cache-groups", From 4bcdc44393037e498ef496698dba0034f01c46e5 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 27 Feb 2023 19:21:48 +0530 Subject: [PATCH 05/47] lint error fixes --- .../status-details.component.spec.ts | 34 +++++++++---------- .../status-details.component.ts | 6 ++-- .../statuses-table.component.spec.ts | 34 +++++++++---------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index f001ac751b..8904d53fd7 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -11,26 +11,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { StatusDetailsComponent } from './status-details.component'; +import { StatusDetailsComponent } from "./status-details.component"; -describe('StatusDetailsComponent', () => { - let component: StatusDetailsComponent; - let fixture: ComponentFixture; +describe("StatusDetailsComponent", () => { + let component: StatusDetailsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ StatusDetailsComponent ] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ StatusDetailsComponent ] + }) + .compileComponents(); - fixture = TestBed.createComponent(StatusDetailsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(StatusDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it("should create", () => { + expect(component).toBeTruthy(); + }); }); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 60b5969532..1750ed9092 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -65,14 +65,14 @@ export class StatusDetailsComponent implements OnInit { * @param id is the id passed in route for this page if this is a edit view. */ async getStatusDetails(): Promise { - const id = this.id as string; // id Type 'null' is not assignable to type 'string' + const id = this.id as string; // id Type 'null' is not assignable to type 'string' this.statusDetails = await this.statusesService.getStatuses(id); - const data:ResponseStatus = { + const data: ResponseStatus = { name: this.statusDetails.name, description: this.statusDetails.description, lastUpdated: new Date(), id: this.statusDetails.id - } + }; this.statusDetailsForm.patchValue(data); this.loading = false; } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index 78ea76d00d..8d294d12d9 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -11,26 +11,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { StatusesTableComponent } from './statuses-table.component'; +import { StatusesTableComponent } from "./statuses-table.component"; -describe('StatusesTableComponent', () => { - let component: StatusesTableComponent; - let fixture: ComponentFixture; +describe("StatusesTableComponent", () => { + let component: StatusesTableComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ StatusesTableComponent ] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ StatusesTableComponent ] + }) + .compileComponents(); - fixture = TestBed.createComponent(StatusesTableComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(StatusesTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it("should create", () => { + expect(component).toBeTruthy(); + }); }); From 2c9c9cb5ae9c63e338a68eba7a5ffb59cd83289e Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 28 Feb 2023 18:32:21 +0530 Subject: [PATCH 06/47] statuses test cases added --- .../status-details.component.spec.ts | 66 +++++++++++++++++-- .../statuses-table.component.spec.ts | 28 +++++++- .../statuses-table.component.ts | 2 +- .../statuses-table/statuses-table.module.ts | 4 +- 4 files changed, 91 insertions(+), 9 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 8904d53fd7..9100f4a1d2 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -11,20 +11,54 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ComponentFixture, TestBed } from "@angular/core/testing"; - +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; +import { ReactiveFormsModule } from "@angular/forms"; +import { MatButtonModule } from "@angular/material/button"; +import { MatCardModule } from "@angular/material/card"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatGridListModule } from "@angular/material/grid-list"; +import { MatInputModule } from "@angular/material/input"; +import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/testing"; +import { Router } from "@angular/router"; +import { RouterTestingModule } from "@angular/router/testing"; +import { StatusesService } from "src/app/api/statuses.service"; +import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; +import { SharedModule } from "src/app/shared/shared.module"; import { StatusDetailsComponent } from "./status-details.component"; +const status = { id: 1, name: 'test', description: 'test', lastUpdated: new Date('02/02/2023') }; + describe("StatusDetailsComponent", () => { let component: StatusDetailsComponent; let fixture: ComponentFixture; + let router: Router; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ StatusDetailsComponent ] + imports: [ + HttpClientTestingModule, + RouterTestingModule.withRoutes([ + { path: 'core/statuses/:id', component: StatusDetailsComponent } + ]), + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatGridListModule, + MatCardModule, + MatButtonModule, + SharedModule + ], + declarations: [StatusDetailsComponent, DecisionDialogComponent], + providers: [StatusesService] }) .compileComponents(); - + TestBed.overrideModule(BrowserDynamicTestingModule, { + set: { + entryComponents: [DecisionDialogComponent] + } + }); + router = TestBed.inject(Router); fixture = TestBed.createComponent(StatusDetailsComponent); component = fixture.componentInstance; fixture.detectChanges(); @@ -33,4 +67,28 @@ describe("StatusDetailsComponent", () => { it("should create", () => { expect(component).toBeTruthy(); }); + + it("submits a update status request", fakeAsync(() => { + const service = TestBed.inject(StatusesService); + component.statusDetailsForm.setValue(status); + spyOn(service, 'updateStatus').and.returnValue(Promise.resolve(status)); + component.updateStatus(); + + service.updateStatus(component.statusDetailsForm.value, 1).then((result) => { + expect(result).toEqual(status); + }) + })); + + it("submits a status creation request", fakeAsync(() => { + const service = TestBed.inject(StatusesService); + component.statusDetailsForm.setValue(status); + spyOn(service, 'createStatus').and.returnValue(Promise.resolve(status)); + component.createStatus(); + + service.createStatus(component.statusDetailsForm.value).then((result) => { + expect(result).toEqual(status); + router.navigate(['/core/statuses/1']); + }) + })); + }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index 8d294d12d9..f35dd28445 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -11,17 +11,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; +import { FormsModule } from "@angular/forms"; +import { MatCardModule } from "@angular/material/card"; +import { RouterTestingModule } from "@angular/router/testing"; +import { ServerService } from "src/app/api"; +import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; +const statuses = [{id:1,name:'test',description:'test',lastUpdated:new Date('02/02/2023')}]; describe("StatusesTableComponent", () => { let component: StatusesTableComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ StatusesTableComponent ] + imports:[ + RouterTestingModule, + HttpClientTestingModule, + FormsModule, + MatCardModule, + SharedModule + ], + declarations: [ StatusesTableComponent ], + providers:[ServerService] }) .compileComponents(); @@ -33,4 +48,13 @@ describe("StatusesTableComponent", () => { it("should create", () => { expect(component).toBeTruthy(); }); + + it("should get all statuses from getStatuses",fakeAsync(()=>{ + const service = fixture.debugElement.injector.get(ServerService); + spyOn(service, 'getStatuses').and.returnValue(Promise.resolve(statuses)); + + service.getStatuses().then((result)=>{ + expect(result).toEqual(statuses); + }) + })) }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 7c5616c0da..7cfbbe1ca2 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -25,7 +25,7 @@ import { ContextMenuItem } from "src/app/shared/generic-table/generic-table.comp }) export class StatusesTableComponent implements OnInit { - statuses: ResponseStatus[] | null = null; + statuses: ResponseStatus[] = []; columnDefs = [ { field: "name", diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts index 35676e170c..e08ee31e11 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -16,8 +16,8 @@ import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; import { RouterModule } from "@angular/router"; +import { ServerService } from "src/app/api"; -import { StatusesService } from "src/app/api/statuses.service"; import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; @@ -41,7 +41,7 @@ const StatusesTableRouting = RouterModule.forChild([ SharedModule ], providers:[ - StatusesService + ServerService ] }) export class StatusesTableModule { } From 9b2c389da4a272ef1817bf964eb9238c5e27302f Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 6 Mar 2023 11:50:39 +0530 Subject: [PATCH 07/47] lint fixes --- .../status-details.component.spec.ts | 16 +++++++++------- .../statuses-table.component.spec.ts | 11 ++++++----- .../statuses-table/statuses-table.module.ts | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 9100f4a1d2..3ecdf7f83b 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -22,12 +22,14 @@ import { MatInputModule } from "@angular/material/input"; import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/testing"; import { Router } from "@angular/router"; import { RouterTestingModule } from "@angular/router/testing"; + import { StatusesService } from "src/app/api/statuses.service"; import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; import { SharedModule } from "src/app/shared/shared.module"; + import { StatusDetailsComponent } from "./status-details.component"; -const status = { id: 1, name: 'test', description: 'test', lastUpdated: new Date('02/02/2023') }; +const status = { id: 1, name: "test", description: "test", lastUpdated: new Date("02/02/2023") }; describe("StatusDetailsComponent", () => { let component: StatusDetailsComponent; @@ -39,7 +41,7 @@ describe("StatusDetailsComponent", () => { imports: [ HttpClientTestingModule, RouterTestingModule.withRoutes([ - { path: 'core/statuses/:id', component: StatusDetailsComponent } + { path: "core/statuses/:id", component: StatusDetailsComponent } ]), ReactiveFormsModule, MatFormFieldModule, @@ -71,24 +73,24 @@ describe("StatusDetailsComponent", () => { it("submits a update status request", fakeAsync(() => { const service = TestBed.inject(StatusesService); component.statusDetailsForm.setValue(status); - spyOn(service, 'updateStatus').and.returnValue(Promise.resolve(status)); + spyOn(service, "updateStatus").and.returnValue(Promise.resolve(status)); component.updateStatus(); service.updateStatus(component.statusDetailsForm.value, 1).then((result) => { expect(result).toEqual(status); - }) + }); })); it("submits a status creation request", fakeAsync(() => { const service = TestBed.inject(StatusesService); component.statusDetailsForm.setValue(status); - spyOn(service, 'createStatus').and.returnValue(Promise.resolve(status)); + spyOn(service, "createStatus").and.returnValue(Promise.resolve(status)); component.createStatus(); service.createStatus(component.statusDetailsForm.value).then((result) => { expect(result).toEqual(status); - router.navigate(['/core/statuses/1']); - }) + router.navigate(["/core/statuses/1"]); + }); })); }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index f35dd28445..7a76590e6c 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -16,12 +16,13 @@ import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; import { RouterTestingModule } from "@angular/router/testing"; + import { ServerService } from "src/app/api"; import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; -const statuses = [{id:1,name:'test',description:'test',lastUpdated:new Date('02/02/2023')}]; +const statuses = [{id:1,name:"test",description:"test",lastUpdated:new Date("02/02/2023")}]; describe("StatusesTableComponent", () => { let component: StatusesTableComponent; let fixture: ComponentFixture; @@ -51,10 +52,10 @@ describe("StatusesTableComponent", () => { it("should get all statuses from getStatuses",fakeAsync(()=>{ const service = fixture.debugElement.injector.get(ServerService); - spyOn(service, 'getStatuses').and.returnValue(Promise.resolve(statuses)); - + spyOn(service, "getStatuses").and.returnValue(Promise.resolve(statuses)); + service.getStatuses().then((result)=>{ expect(result).toEqual(statuses); - }) - })) + }); + })); }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts index e08ee31e11..59c15fb148 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -16,8 +16,8 @@ import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; import { RouterModule } from "@angular/router"; -import { ServerService } from "src/app/api"; +import { ServerService } from "src/app/api"; import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; From 9e4a8fbe5f6d955bb7773ba6c12a02f6217efdb8 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 6 Mar 2023 21:00:56 +0530 Subject: [PATCH 08/47] added last line --- experimental/traffic-portal/src/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/traffic-portal/src/styles.scss b/experimental/traffic-portal/src/styles.scss index 8a15bf53dd..cf3064cd55 100644 --- a/experimental/traffic-portal/src/styles.scss +++ b/experimental/traffic-portal/src/styles.scss @@ -151,4 +151,4 @@ button { position: fixed; bottom: 16px; right: 16px; -} \ No newline at end of file +} From b5ed0316f27c2945b4e9e43a6d41d78a2c554275 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 6 Mar 2023 21:10:48 +0530 Subject: [PATCH 09/47] lazy load and statuses service removed --- .../src/app/api/statuses.service.ts | 68 ------------------- .../src/app/core/core.module.ts | 20 +++--- .../status-details/status-details.module.ts | 55 --------------- .../statuses-table/statuses-table.module.ts | 47 ------------- 4 files changed, 10 insertions(+), 180 deletions(-) delete mode 100644 experimental/traffic-portal/src/app/api/statuses.service.ts delete mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts delete mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts diff --git a/experimental/traffic-portal/src/app/api/statuses.service.ts b/experimental/traffic-portal/src/app/api/statuses.service.ts deleted file mode 100644 index 1cb3c24201..0000000000 --- a/experimental/traffic-portal/src/app/api/statuses.service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { HttpClient } from "@angular/common/http"; -import { Injectable } from "@angular/core"; -import { ResponseStatus } from "trafficops-types"; - -import { APIService } from "./base-api.service"; - -@Injectable() -export class StatusesService extends APIService { - - /** - * Injects the Angular HTTP client service into the parent constructor. - * - * @param http The Angular HTTP client service. - */ - constructor(http: HttpClient) { - super(http); - } - - public async getStatuses(idOrName: number | string): Promise; - public async getStatuses(): Promise>; - /** - * @param id Specify either the integral, unique identifier (number. - * @returns The requested status(s). - */ - public async getStatuses(id?: number | string): Promise | ResponseStatus> { - const path = "statuses"; - if (id !== undefined) { - let statuses; - statuses = await this.get<[ResponseStatus]>(path, undefined, { id: String(id) }).toPromise(); - if (statuses.length < 1) { - throw new Error(`no such statuses '${id}'`); - } - return statuses[0]; - } - return this.get>(path).toPromise(); - } - - /** - * Creating new Status. - * - * @param data containes name and description for the status. - * @returns The 'response' property of the TO status response. See TO API docs. - */ - public async createStatus(data: ResponseStatus) { - const path = "statuses"; - return this.post(path, data).toPromise(); - } - - /** - * Updates status. - * - * @param data containes name and description for the status., unique identifier thereof. - * @param id The Status ID - */ - public async updateStatus(data: ResponseStatus, id: number): Promise { - const path = `statuses/${id}`; - return this.put(path, data).toPromise(); - } - - /** - * Deletes an existing Status. - * - * @param id The Status ID - */ - public async deleteStatus(id: number): Promise { - return this.delete(`statuses/${id}`).toPromise(); - } -} diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index 3b3abe1f81..fec9dafba8 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -18,6 +18,7 @@ */ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; +import { ReactiveFormsModule } from "@angular/forms"; import { RouterModule, type Routes } from "@angular/router"; import { AppUIModule } from "../app.ui.module"; @@ -47,6 +48,8 @@ import { PhysLocTableComponent } from "./servers/phys-loc/table/phys-loc-table.c import { ServerDetailsComponent } from "./servers/server-details/server-details.component"; import { ServersTableComponent } from "./servers/servers-table/servers-table.component"; import { UpdateStatusComponent } from "./servers/update-status/update-status.component"; +import { StatusDetailsComponent } from "./statuses/status-details/status-details.component"; +import { StatusesTableComponent } from "./statuses/statuses-table/statuses-table.component"; import { TenantDetailsComponent } from "./users/tenants/tenant-details/tenant-details.component"; import { TenantsComponent } from "./users/tenants/tenants.component"; import { UserDetailsComponent } from "./users/user-details/user-details.component"; @@ -74,14 +77,8 @@ export const ROUTES: Routes = [ { component: TenantDetailsComponent, path: "tenants/:id"}, { component: PhysLocDetailComponent, path: "phys-locs/:id" }, { component: PhysLocTableComponent, path: "phys-locs" }, - { - path: "statuses", - loadChildren: async () => import("./statuses/statuses-table/statuses-table.module").then(m => m.StatusesTableModule) - }, - { - path: "statuses/:id", - loadChildren: async () => import("./statuses/status-details/status-details.module").then(m => m.StatusDetailsModule) - } + { component: StatusesTableComponent, path: "statuses" }, + { component: StatusDetailsComponent, path: "statuses/:id" }, ].map(r => ({...r, canActivate: [AuthenticatedGuard]})); /** @@ -115,14 +112,17 @@ export const ROUTES: Routes = [ DivisionDetailComponent, RegionsTableComponent, RegionDetailComponent, - CacheGroupDetailsComponent + CacheGroupDetailsComponent, + StatusesTableComponent, + StatusDetailsComponent ], exports: [], imports: [ SharedModule, AppUIModule, CommonModule, - RouterModule.forChild(ROUTES) + RouterModule.forChild(ROUTES), + ReactiveFormsModule ] }) export class CoreModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts deleted file mode 100644 index 3871f328a7..0000000000 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; -import { ReactiveFormsModule } from "@angular/forms"; -import { MatButtonModule } from "@angular/material/button"; -import { MatCardModule } from "@angular/material/card"; -import { MatFormFieldModule } from "@angular/material/form-field"; -import {MatGridListModule} from "@angular/material/grid-list"; -import { MatInputModule } from "@angular/material/input"; -import { RouterModule } from "@angular/router"; - -import { StatusesService } from "src/app/api/statuses.service"; -import { SharedModule } from "src/app/shared/shared.module"; - -import { StatusDetailsComponent } from "./status-details.component"; - -const StatusDetailRouting = RouterModule.forChild([ - { - path: "", - component: StatusDetailsComponent - } -]); - -@NgModule({ - declarations: [ - StatusDetailsComponent - ], - imports: [ - CommonModule, - StatusDetailRouting, - ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - MatGridListModule, - MatCardModule, - MatButtonModule, - SharedModule - ], - providers:[ - StatusesService - ] -}) -export class StatusDetailsModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts deleted file mode 100644 index 59c15fb148..0000000000 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; -import { FormsModule } from "@angular/forms"; -import { MatCardModule } from "@angular/material/card"; -import { RouterModule } from "@angular/router"; - -import { ServerService } from "src/app/api"; -import { SharedModule } from "src/app/shared/shared.module"; - -import { StatusesTableComponent } from "./statuses-table.component"; - -const StatusesTableRouting = RouterModule.forChild([ - { - path: "", - component: StatusesTableComponent - } -]); - -@NgModule({ - declarations: [ - StatusesTableComponent - ], - imports: [ - CommonModule, - StatusesTableRouting, - FormsModule, - MatCardModule, - SharedModule - ], - providers:[ - ServerService - ] -}) -export class StatusesTableModule { } From defc015effd6bcbd7ddbd09eb215f1787d8eeccd Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 6 Mar 2023 21:11:18 +0530 Subject: [PATCH 10/47] lint error and comments addressed --- .../src/app/api/server.service.ts | 32 +++- .../status-details.component.html | 5 +- .../status-details.component.spec.ts | 25 ++-- .../status-details.component.ts | 139 ++++++++++++------ .../statuses-table.component.spec.ts | 8 +- .../statuses-table.component.ts | 35 ++++- 6 files changed, 169 insertions(+), 75 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/server.service.ts b/experimental/traffic-portal/src/app/api/server.service.ts index 4411056d03..54662f7d92 100644 --- a/experimental/traffic-portal/src/app/api/server.service.ts +++ b/experimental/traffic-portal/src/app/api/server.service.ts @@ -118,7 +118,8 @@ export class ServerService extends APIService { let ret; switch (typeof idOrName) { case "number": - ret = this.get<[ResponseStatus]>(path, {params: {id: String(idOrName)}}).toPromise(); + const response = await this.get<[ResponseStatus]>(path, undefined, { id: String(idOrName) }).toPromise(); + ret = response[0]; break; case "string": ret = this.get<[ResponseStatus]>(path, {params: {name: idOrName}}).toPromise(); @@ -186,4 +187,33 @@ export class ServerService extends APIService { return this.put(`servers/${id}/status`, {offlineReason, status}).toPromise(); } + + /** + * Creating new Status. + * + * @param data containes name and description for the status. + * @returns The 'response' property of the TO status response. See TO API docs. + */ + public async createStatus(data: ResponseStatus): Promise { + return this.post("statuses", data).toPromise(); + } + + /** + * Updates status Details. + * + * @param data containes name and description for the status., unique identifier thereof. + * @param id The Status ID + */ + public async updateStatusDetail(data: ResponseStatus, id: number): Promise { + return this.put(`statuses/${id}`, data).toPromise(); + } + + /** + * Deletes an existing Status. + * + * @param id The Status ID + */ + public async deleteStatus(id: number): Promise { + return this.delete(`statuses/${id}`).toPromise(); + } } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 34e3d3f109..3d303bf1ef 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -12,17 +12,16 @@ limitations under the License. --> -

{{title}}

Name - + Description - + diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 3ecdf7f83b..b1c0216eeb 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -12,7 +12,7 @@ * limitations under the License. */ import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { ReactiveFormsModule } from "@angular/forms"; import { MatButtonModule } from "@angular/material/button"; import { MatCardModule } from "@angular/material/card"; @@ -23,13 +23,13 @@ import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/t import { Router } from "@angular/router"; import { RouterTestingModule } from "@angular/router/testing"; -import { StatusesService } from "src/app/api/statuses.service"; +import { ServerService } from "src/app/api"; import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; import { SharedModule } from "src/app/shared/shared.module"; import { StatusDetailsComponent } from "./status-details.component"; -const status = { id: 1, name: "test", description: "test", lastUpdated: new Date("02/02/2023") }; +const status = { description: "test", id: 1,lastUpdated: new Date("02/02/2023"), name: "test"}; describe("StatusDetailsComponent", () => { let component: StatusDetailsComponent; @@ -38,10 +38,11 @@ describe("StatusDetailsComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ + declarations: [StatusDetailsComponent, DecisionDialogComponent], imports: [ HttpClientTestingModule, RouterTestingModule.withRoutes([ - { path: "core/statuses/:id", component: StatusDetailsComponent } + { component: StatusDetailsComponent, path: "core/statuses/:id" } ]), ReactiveFormsModule, MatFormFieldModule, @@ -51,8 +52,7 @@ describe("StatusDetailsComponent", () => { MatButtonModule, SharedModule ], - declarations: [StatusDetailsComponent, DecisionDialogComponent], - providers: [StatusesService] + providers: [ServerService] }) .compileComponents(); TestBed.overrideModule(BrowserDynamicTestingModule, { @@ -70,20 +70,21 @@ describe("StatusDetailsComponent", () => { expect(component).toBeTruthy(); }); - it("submits a update status request", fakeAsync(() => { - const service = TestBed.inject(StatusesService); + it("submits a update status request", (() => { + const service = TestBed.inject(ServerService); component.statusDetailsForm.setValue(status); - spyOn(service, "updateStatus").and.returnValue(Promise.resolve(status)); + spyOn(service, "updateStatusDetail").and.returnValue(Promise.resolve(status)); component.updateStatus(); - service.updateStatus(component.statusDetailsForm.value, 1).then((result) => { + service.updateStatus(component.statusDetailsForm.value, "1").then((result) => { expect(result).toEqual(status); }); })); - it("submits a status creation request", fakeAsync(() => { - const service = TestBed.inject(StatusesService); + it("submits a status creation request", (() => { + const service = TestBed.inject(ServerService); component.statusDetailsForm.setValue(status); + spyOn(service, "createStatus").and.returnValue(Promise.resolve(status)); component.createStatus(); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 1750ed9092..13509542b4 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -12,40 +12,61 @@ * limitations under the License. */ import { Component, OnInit } from "@angular/core"; -import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; +import { FormControl, FormGroup, Validators } from "@angular/forms"; import { MatDialog } from "@angular/material/dialog"; import { ActivatedRoute, Router } from "@angular/router"; import { ResponseStatus } from "trafficops-types"; -import { StatusesService } from "src/app/api/statuses.service"; +import { ServerService } from "src/app/api"; import { DecisionDialogComponent, DecisionDialogData } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; +import { NavigationService } from "src/app/shared/navigation/navigation.service"; +/** + * StatusDetailsComponent is the controller for a status "details" page. + */ @Component({ + providers: [ServerService], selector: "tp-status-details", + styleUrls: ["./status-details.component.scss"], templateUrl: "./status-details.component.html", - styleUrls: ["./status-details.component.scss"] }) export class StatusDetailsComponent implements OnInit { - id: string | null = null; - statusDetails: ResponseStatus | null = null; - statusDetailsForm!: FormGroup; - loading = false; - submitting = false; - submitted = false; + /** Status ID expected from the route param using which we identify whether we are creating new status or load existing status */ + public id: string | number | null = null; + + /** All details of status requested */ + public statusDetails: ResponseStatus | null = null; + + /** Reactive form intialized to creat / edit status details */ + public statusDetailsForm!: FormGroup; + /** Loader status for the actions */ + public loading = false; + + /** + * Constructor. + * + * @param serverService The Servers API which is used to provide row data. + * @param route A reference to the route of this view which is used to get the 'id' query parameter of status. + * @param router Angular router + * @param dialog Dialog manager + * @param fb Form builder + * @param navSvc Manages the header + */ constructor( + private readonly serverService: ServerService, private readonly route: ActivatedRoute, private readonly router: Router, - private readonly fb: FormBuilder, private readonly dialog: MatDialog, - private readonly statusesService: StatusesService) { } - - ngOnInit(): void { - // Form is built here - this.statusDetailsForm = this.fb.group({ - name: ["", Validators.required], - description: ["", Validators.required], + private readonly navSvc: NavigationService, + ) { } + + /** Initializes table data, loading it from Traffic Ops. */ + public ngOnInit(): void { + this.statusDetailsForm = new FormGroup({ + description: new FormControl("",Validators.required), + name: new FormControl("",Validators.required), }); // Getting id from the route @@ -57,28 +78,38 @@ export class StatusDetailsComponent implements OnInit { this.statusDetailsForm.addControl("id", new FormControl("")); this.statusDetailsForm.addControl("lastUpdated", new FormControl("")); this.getStatusDetails(); + } else { + this.navSvc.headerTitle.next("New Status"); } } - /* - * Reloads the servers table data. - * @param id is the id passed in route for this page if this is a edit view. - */ - async getStatusDetails(): Promise { - const id = this.id as string; // id Type 'null' is not assignable to type 'string' - this.statusDetails = await this.statusesService.getStatuses(id); + /** + * Reloads the servers table data. + * + * @param id is the id passed in route for this page if this is a edit view. + */ + public async getStatusDetails(): Promise { + const id = Number(this.id) ; // id Type 'null' is not assignable to type 'string' + this.statusDetails = await this.serverService.getStatuses(id); const data: ResponseStatus = { - name: this.statusDetails.name, description: this.statusDetails.description, + id: this.statusDetails.id, lastUpdated: new Date(), - id: this.statusDetails.id + name: this.statusDetails.name }; + + // Set page title with status ID + this.navSvc.headerTitle.next(`Status #${data.id}`); + + // Patch the form with existing data we got from service requested above. this.statusDetailsForm.patchValue(data); this.loading = false; } - // On submitting the form we check for whether we are performing Create or Edit - onSubmit() { + /** + * On submitting the form we check for whether we are performing Create or Edit + */ + public onSubmit(): void { if (this.isNew) { this.createStatus(); @@ -87,23 +118,30 @@ export class StatusDetailsComponent implements OnInit { } } - // For Creating a new status - createStatus() { - this.statusesService.createStatus(this.statusDetailsForm.value).then((res: any) => { + /** + * For Creating a new status + */ + public createStatus(): void { + this.serverService.createStatus(this.statusDetailsForm.value).then((res: ResponseStatus) => { if (res) { - this.id = res?.id; + this.id = (res?.id); this.router.navigate([`/core/statuses/${this.id}`]); + this.navSvc.headerTitle.next(`Status #${this.id}`); } }); } - // For updating the Status - updateStatus() { - this.statusesService.updateStatus(this.statusDetailsForm.value, Number(this.id)); + /** + * For updating the Status + */ + public updateStatus(): void { + this.serverService.updateStatusDetail(this.statusDetailsForm.value, Number(this.id)); } - // Deleteting status - async deleteStatus() { + /** + * Deleteting status + */ + public async deleteStatus(): Promise { const ref = this.dialog.open(DecisionDialogComponent, { data: { message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails?.name}'.`, @@ -111,22 +149,27 @@ export class StatusDetailsComponent implements OnInit { } }); - if (await ref.afterClosed().toPromise()) { - const id = Number(this.id); - this.statusesService.deleteStatus(id).then(() => { - this.router.navigate(["/core/statuses"]); - }); - } - + ref.afterClosed().subscribe(result => { + if (result) { + const id = Number(this.id); + this.serverService.deleteStatus(id).then(() => { + this.router.navigate(["/core/statuses"]); + }); + } + }); } - // Title for the page - get title(): string { + /** + * Title for the page + */ + public get title(): string { return this.isNew ? "Add New Status" : "Edit Status"; } - // Checking for params to ensure given id is a number - get isNew() { + /** + * Checking for params to ensure given id is a number + */ + public get isNew(): boolean { return this.id === "new" && isNaN(Number(this.id)); } } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index 7a76590e6c..df4506bc3d 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -12,7 +12,7 @@ * limitations under the License. */ import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; import { RouterTestingModule } from "@angular/router/testing"; @@ -22,13 +22,14 @@ import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; -const statuses = [{id:1,name:"test",description:"test",lastUpdated:new Date("02/02/2023")}]; +const statuses = [{description: "test", id: 1,lastUpdated: new Date("02/02/2023"), name: "test"}]; describe("StatusesTableComponent", () => { let component: StatusesTableComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ + declarations: [ StatusesTableComponent ], imports:[ RouterTestingModule, HttpClientTestingModule, @@ -36,7 +37,6 @@ describe("StatusesTableComponent", () => { MatCardModule, SharedModule ], - declarations: [ StatusesTableComponent ], providers:[ServerService] }) .compileComponents(); @@ -50,7 +50,7 @@ describe("StatusesTableComponent", () => { expect(component).toBeTruthy(); }); - it("should get all statuses from getStatuses",fakeAsync(()=>{ + it("should get all statuses from getStatuses",(()=>{ const service = fixture.debugElement.injector.get(ServerService); spyOn(service, "getStatuses").and.returnValue(Promise.resolve(statuses)); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 7cfbbe1ca2..57576a4410 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -17,16 +17,25 @@ import { ResponseStatus } from "trafficops-types"; import { ServerService } from "src/app/api"; import { ContextMenuItem } from "src/app/shared/generic-table/generic-table.component"; +import { NavigationService } from "src/app/shared/navigation/navigation.service"; +/** + * StatusesTableComponent is the controller for the statuses page - which + * principally contains a table. + */ @Component({ + providers:[ ServerService ], selector: "tp-statuses-table", + styleUrls: ["./statuses-table.component.scss"], templateUrl: "./statuses-table.component.html", - styleUrls: ["./statuses-table.component.scss"] }) export class StatusesTableComponent implements OnInit { - statuses: ResponseStatus[] = []; - columnDefs = [ + /** All of the statues which should appear in the table. */ + public statuses: ResponseStatus[] = []; + + /** Definitions of the table's columns according to the ag-grid API */ + public columnDefs = [ { field: "name", headerName: "Name", @@ -41,7 +50,7 @@ export class StatusesTableComponent implements OnInit { /** The current search text. */ public searchText = ""; - /** Definitions for the context menu items (which act on user data). */ + /** Definitions for the context menu items (which act on statuses data). */ public contextMenuItems: Array> = [ { href: (u: ResponseStatus): string => `${u.id}`, @@ -55,16 +64,28 @@ export class StatusesTableComponent implements OnInit { /** Emits changes to the fuzzy search text. */ public fuzzySubject = new BehaviorSubject(""); + + /** + * Constructs the component with its required injections. + * + * @param serverService The Servers API which is used to provide row data. + * @param navSvc Manages the header + */ constructor( private readonly serverService: ServerService, - ) { } + private readonly navSvc: NavigationService, + ) { + this.fuzzySubject = new BehaviorSubject(""); + this.navSvc.headerTitle.next("Statuses"); + } - ngOnInit(): void { + /** Initializes table data, loading it from Traffic Ops. */ + public ngOnInit(): void { this.getStatuses(); } /** Reloads the servers table data. */ - async getStatuses(): Promise { + public async getStatuses(): Promise { this.statuses = await this.serverService.getStatuses(); } From 97dac47c8b011d0a3e3d5c7e740cf7ec2e3e7225 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 7 Mar 2023 07:16:13 +0530 Subject: [PATCH 11/47] removed reactive forms module import --- experimental/traffic-portal/src/app/core/core.module.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index fec9dafba8..062e380c12 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -18,7 +18,6 @@ */ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { ReactiveFormsModule } from "@angular/forms"; import { RouterModule, type Routes } from "@angular/router"; import { AppUIModule } from "../app.ui.module"; @@ -121,8 +120,7 @@ export const ROUTES: Routes = [ SharedModule, AppUIModule, CommonModule, - RouterModule.forChild(ROUTES), - ReactiveFormsModule + RouterModule.forChild(ROUTES) ] }) export class CoreModule { } From 97036191bd9b44337adc7d02be04c7553756da5b Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:09:12 +0530 Subject: [PATCH 12/47] comments addressed --- .../src/app/api/server.service.ts | 10 +-- .../src/app/api/testing/server.service.ts | 58 ++++++++++++++++- .../status-details.component.html | 4 +- .../status-details.component.ts | 62 ++++++------------- 4 files changed, 83 insertions(+), 51 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/server.service.ts b/experimental/traffic-portal/src/app/api/server.service.ts index 54662f7d92..f8c06e429d 100644 --- a/experimental/traffic-portal/src/app/api/server.service.ts +++ b/experimental/traffic-portal/src/app/api/server.service.ts @@ -14,7 +14,7 @@ import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; -import type { RequestServer, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; +import type { RequestServer, RequestStatus, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; import { APIService } from "./base-api.service"; @@ -194,8 +194,8 @@ export class ServerService extends APIService { * @param data containes name and description for the status. * @returns The 'response' property of the TO status response. See TO API docs. */ - public async createStatus(data: ResponseStatus): Promise { - return this.post("statuses", data).toPromise(); + public async createStatus(payload: RequestStatus): Promise { + return this.post("statuses", payload).toPromise(); } /** @@ -204,8 +204,8 @@ export class ServerService extends APIService { * @param data containes name and description for the status., unique identifier thereof. * @param id The Status ID */ - public async updateStatusDetail(data: ResponseStatus, id: number): Promise { - return this.put(`statuses/${id}`, data).toPromise(); + public async updateStatusDetail(payload: ResponseStatus, id: number): Promise { + return this.put(`statuses/${id}`, payload).toPromise(); } /** diff --git a/experimental/traffic-portal/src/app/api/testing/server.service.ts b/experimental/traffic-portal/src/app/api/testing/server.service.ts index a33ed5bb64..68136ddff4 100644 --- a/experimental/traffic-portal/src/app/api/testing/server.service.ts +++ b/experimental/traffic-portal/src/app/api/testing/server.service.ts @@ -13,9 +13,10 @@ */ import { Injectable } from "@angular/core"; -import type { RequestServer, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; +import type { RequestServer, RequestStatus, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; import { CDNService, PhysicalLocationService, ProfileService, TypeService } from ".."; +import { HttpResponse } from "@angular/common/http"; /** * Generates a `Servercheck` for a given `server`. @@ -47,7 +48,7 @@ export class ServerService { public servers = new Array(); - private readonly statuses = [ + private statuses = [ { description: "Sever is administrative down and does not receive traffic.", id: 4, @@ -87,6 +88,7 @@ export class ServerService { ]; private idCounter = 1; + private statusIdCounter = 6; constructor( private readonly cdnService: CDNService, @@ -302,4 +304,56 @@ export class ServerService { srv.statusId = status.id; srv.offlineReason = offlineReason ?? null; } + + /** + * Creates a status. + * + * @param status The status details (name & description) to create. + * @returns The status as created and returned by the API. + */ + public async createStatus(status: RequestStatus): Promise { + const newStatus = { + ...status, + id: ++this.statusIdCounter, + lastUpdated: new Date() + } as { description: string; id: number; lastUpdated: Date; name: string; }; + this.statuses.push(newStatus); + return newStatus; + } + + /** + * Updates status Details. + * + * @param data containes name and description for the status., unique identifier thereof. + * @param id The Status ID + */ + public async updateStatusDetail(payload: ResponseStatus, id: number): Promise { + const index = this.statuses.findIndex(u => u.id === id); + if (index < 0) { + throw new Error(`no such status with id: ${id}`); + } + const updated = { + ...payload, + lastUpdated: new Date() + } as { description: string; id: number; lastUpdated: Date; name: string; }; + this.statuses[index] = updated; + + return updated; + } + + + /** + * Deletes a Status. + * + * @param id The ID of the Status to delete. + * @returns The response. + */ + public async deleteStatus(id: number): Promise | null> { + const idx = this.statuses.findIndex(j => j.id === id); + if (idx < 0) { + throw new Error(`no such status: #${id}`); + } + this.statuses.splice(idx, 1); + return new HttpResponse({ body: { alerts: [{ level: "success", text: "Successfully logged in." }] }, status: 200 }) + } } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 3d303bf1ef..7846bf31fc 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -25,10 +25,10 @@ - + diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 13509542b4..4d7fbbf0a7 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -35,14 +35,17 @@ export class StatusDetailsComponent implements OnInit { /** Status ID expected from the route param using which we identify whether we are creating new status or load existing status */ public id: string | number | null = null; + /** Loader status for the actions */ + public loading = false; + /** All details of status requested */ - public statusDetails: ResponseStatus | null = null; + public statusDetails!: ResponseStatus; /** Reactive form intialized to creat / edit status details */ - public statusDetailsForm!: FormGroup; - - /** Loader status for the actions */ - public loading = false; + public statusDetailsForm: FormGroup = new FormGroup({ + description: new FormControl("", Validators.required), + name: new FormControl("", Validators.required), + }); /** * Constructor. @@ -60,20 +63,16 @@ export class StatusDetailsComponent implements OnInit { private readonly router: Router, private readonly dialog: MatDialog, private readonly navSvc: NavigationService, - ) { } + ) { + // Getting id from the route + this.id = this.route.snapshot.paramMap.get("id"); + } /** Initializes table data, loading it from Traffic Ops. */ public ngOnInit(): void { - this.statusDetailsForm = new FormGroup({ - description: new FormControl("",Validators.required), - name: new FormControl("",Validators.required), - }); - - // Getting id from the route - this.id = this.route.snapshot.paramMap.get("id"); // we check whether params is a number if not we shall assume user wants to add a new status. - if (!this.isNew) { + if (this.id !== "new") { this.loading = true; this.statusDetailsForm.addControl("id", new FormControl("")); this.statusDetailsForm.addControl("lastUpdated", new FormControl("")); @@ -89,20 +88,14 @@ export class StatusDetailsComponent implements OnInit { * @param id is the id passed in route for this page if this is a edit view. */ public async getStatusDetails(): Promise { - const id = Number(this.id) ; // id Type 'null' is not assignable to type 'string' + const id = Number(this.id); // id Type 'null' is not assignable to type 'string' this.statusDetails = await this.serverService.getStatuses(id); - const data: ResponseStatus = { - description: this.statusDetails.description, - id: this.statusDetails.id, - lastUpdated: new Date(), - name: this.statusDetails.name - }; - // Set page title with status ID - this.navSvc.headerTitle.next(`Status #${data.id}`); + // Set page title with status Name + this.navSvc.headerTitle.next(`Status #${this.statusDetails.name}`); // Patch the form with existing data we got from service requested above. - this.statusDetailsForm.patchValue(data); + this.statusDetailsForm.patchValue(this.statusDetails); this.loading = false; } @@ -110,7 +103,7 @@ export class StatusDetailsComponent implements OnInit { * On submitting the form we check for whether we are performing Create or Edit */ public onSubmit(): void { - if (this.isNew) { + if (this.id === "new") { this.createStatus(); } else { @@ -124,9 +117,8 @@ export class StatusDetailsComponent implements OnInit { public createStatus(): void { this.serverService.createStatus(this.statusDetailsForm.value).then((res: ResponseStatus) => { if (res) { - this.id = (res?.id); - this.router.navigate([`/core/statuses/${this.id}`]); - this.navSvc.headerTitle.next(`Status #${this.id}`); + this.statusDetails = res; + this.router.navigate(["/core/statuses"]); } }); } @@ -158,18 +150,4 @@ export class StatusDetailsComponent implements OnInit { } }); } - - /** - * Title for the page - */ - public get title(): string { - return this.isNew ? "Add New Status" : "Edit Status"; - } - - /** - * Checking for params to ensure given id is a number - */ - public get isNew(): boolean { - return this.id === "new" && isNaN(Number(this.id)); - } } From dadee3fbb4d7075f47947efbd09d6507d4d72a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckgb488=E2=80=9D?= <“kannan_gb@comcast.comgit config --global user.name “kgb488”tesdtgit config --global user.email “kannan_gb@comcast.com> Date: Tue, 21 Feb 2023 21:49:36 +0530 Subject: [PATCH 13/47] statuses parity - table,details, new,delete --- .../src/app/api/statuses.service.ts | 71 +++++++++++ .../src/app/core/core.module.ts | 8 ++ .../status-details.component.html | 26 ++++ .../status-details.component.scss | 12 ++ .../status-details.component.spec.ts | 23 ++++ .../status-details.component.ts | 117 ++++++++++++++++++ .../status-details/status-details.module.ts | 36 ++++++ .../statuses-table.component.html | 9 ++ .../statuses-table.component.scss | 0 .../statuses-table.component.spec.ts | 23 ++++ .../statuses-table.component.ts | 63 ++++++++++ .../statuses-table/statuses-table.module.ts | 32 +++++ .../shared/navigation/navigation.service.ts | 6 + experimental/traffic-portal/src/styles.scss | 30 +++++ 14 files changed, 456 insertions(+) create mode 100644 experimental/traffic-portal/src/app/api/statuses.service.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts create mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts diff --git a/experimental/traffic-portal/src/app/api/statuses.service.ts b/experimental/traffic-portal/src/app/api/statuses.service.ts new file mode 100644 index 0000000000..3c0ee507eb --- /dev/null +++ b/experimental/traffic-portal/src/app/api/statuses.service.ts @@ -0,0 +1,71 @@ +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { APIService } from './base-api.service'; + +export interface StatusesModel { + description?: string; + id?: number; + lastUpdated?: Date; + name?: string; +} + +@Injectable({ + providedIn: 'root' +}) +export class StatusesService extends APIService { + + /** + * Injects the Angular HTTP client service into the parent constructor. + * @param http The Angular HTTP client service. + */ + constructor(http: HttpClient) { + super(http); + } + + public async getStatuses(idOrName: number | string): Promise; + public async getStatuses(): Promise>; + /** + * @param id Specify either the integral, unique identifier (number. + * @returns The requested status(s). + */ + public async getStatuses(id?: number | string): Promise | StatusesModel> { + const path = "statuses"; + if (id !== undefined) { + let statuses; + statuses = await this.get<[StatusesModel]>(path, undefined, { id: String(id) }).toPromise(); + if (statuses.length < 1) { + throw new Error(`no such statuses '${id}'`); + } + return statuses[0]; + } + return this.get>(path).toPromise(); + } + + /** + * Creating new Status. + * @param data containes name and description for the status. + * @returns The 'response' property of the TO status response. See TO API docs. + */ + public async createStatus(data: StatusesModel) { + const path = "statuses"; + return this.post(path, data).toPromise(); + } + + /** + * Updates status. + * @param data containes name and description for the status., unique identifier thereof. + * @param id The Status ID + */ + public async updateStatus(data: StatusesModel, id: number): Promise { + const path = `statuses/${id}`; + return this.put(path, data).toPromise(); + } + + /** + * Deletes an existing Status. + * @param id The Status ID + */ + public async deleteStatus(id: number): Promise { + return this.delete(`statuses/${id}`).toPromise(); + } +} diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index 84a171c083..cbe830e2bf 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -74,6 +74,14 @@ export const ROUTES: Routes = [ { component: TenantDetailsComponent, path: "tenants/:id"}, { component: PhysLocDetailComponent, path: "phys-locs/:id" }, { component: PhysLocTableComponent, path: "phys-locs" }, + { + path: 'statuses', + loadChildren: () => import('./statuses/statuses-table/statuses-table.module').then(m => m.StatusesTableModule) + }, + { + path: 'statuses/:id', + loadChildren: () => import('./statuses/status-details/status-details.module').then(m => m.StatusDetailsModule) + } ].map(r => ({...r, canActivate: [AuthenticatedGuard]})); /** diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html new file mode 100644 index 0000000000..ce6fa818f4 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -0,0 +1,26 @@ + + + +
+ + + Name + + + + Description + + + + + + + + +
+
\ No newline at end of file diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss new file mode 100644 index 0000000000..56d187902e --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss @@ -0,0 +1,12 @@ +mat-card { + margin: 1em auto; + width: 95%; + min-width: 350px; + + mat-card-content { + display: grid; + grid-template-columns: 1fr; + grid-row-gap: 2em; + margin: 1em auto 10px; + } +} \ No newline at end of file diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts new file mode 100644 index 0000000000..784dd6f1a0 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StatusDetailsComponent } from './status-details.component'; + +describe('StatusDetailsComponent', () => { + let component: StatusDetailsComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ StatusDetailsComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(StatusDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts new file mode 100644 index 0000000000..4f43132332 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -0,0 +1,117 @@ +import { Component, OnInit } from '@angular/core'; +import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { StatusesModel, StatusesService } from 'src/app/api/statuses.service'; +import { DecisionDialogComponent, DecisionDialogData } from 'src/app/shared/dialogs/decision-dialog/decision-dialog.component'; + +@Component({ + selector: 'tp-status-details', + templateUrl: './status-details.component.html', + styleUrls: ['./status-details.component.scss'] +}) +export class StatusDetailsComponent implements OnInit { + + id: string | null = null; + statusDetails: StatusesModel | null = null; + statusDetailsForm!: FormGroup; + loading = false; + submitting = false; + submitted = false; + + constructor( + private route: ActivatedRoute, + private router: Router, + private fb: FormBuilder, + private readonly dialog: MatDialog, + private statusesService: StatusesService) { } + + ngOnInit(): void { + // Form is built here + this.statusDetailsForm = this.fb.group({ + name: ['', Validators.required], + description: ['', Validators.required], + }); + + // Getting id from the route + this.id = this.route.snapshot.paramMap.get('id'); + + // we check whether params is a number if not we shall assume user wants to add a new status. + if (!this.isNew) { + this.loading = true; + this.statusDetailsForm.addControl('id', new FormControl('')); + this.statusDetailsForm.addControl('lastUpdated', new FormControl('')); + this.getStatusDetails(); + } + } + + /* + * Reloads the servers table data. + * @param id is the id passed in route for this page if this is a edit view. + */ + async getStatusDetails(): Promise { + const id = this.id as string + this.statusDetails = await this.statusesService.getStatuses(id); + const data = { + name: this.statusDetails.name, + description: this.statusDetails.description, + lastUpdated: new Date(), + id: this.statusDetails.id + } as any; + this.statusDetailsForm.patchValue(data); + this.loading = false; + } + + // On submitting the form we check for whether we are performing Create or Edit + onSubmit() { + if (this.isNew) { + this.createStatus() + + } else { + this.updateStatus(); + } + } + + // For Creating a new status + createStatus() { + this.statusesService.createStatus(this.statusDetailsForm.value).then((res: any) => { + if (res) { + this.id = res?.id + this.router.navigate([`/core/statuses/${this.id}`]); + } + }) + } + + // For updating the Status + updateStatus() { + this.statusesService.updateStatus(this.statusDetailsForm.value, Number(this.id)); + } + + // Deleteting status + async deleteStatus() { + const ref = this.dialog.open(DecisionDialogComponent, { + data: { + message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails?.name}'.`, + title: `Delete Status: ${this.statusDetails?.name}` + } + }); + + if (await ref.afterClosed().toPromise()) { + const id = Number(this.id); + this.statusesService.deleteStatus(id).then(() => { + this.router.navigate([`/core/statuses`]); + }) + } + + } + + // Title for the page + get title(): string { + return this.isNew ? 'Add New Status' : 'Edit Status'; + } + + // Checking for params to ensure given id is a number + get isNew() { + return this.id === 'new' && isNaN(Number(this.id)); + } +} diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts new file mode 100644 index 0000000000..ca8dc21f54 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts @@ -0,0 +1,36 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { StatusDetailsComponent } from './status-details.component'; +import { RouterModule } from '@angular/router'; +import { ReactiveFormsModule } from '@angular/forms'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import {MatGridListModule} from '@angular/material/grid-list'; +import { MatCardModule } from '@angular/material/card'; +import { SharedModule } from 'src/app/shared/shared.module'; +import { MatButtonModule } from '@angular/material/button'; + +const StatusDetailRouting = RouterModule.forChild([ + { + path: '', + component: StatusDetailsComponent + } +]); + +@NgModule({ + declarations: [ + StatusDetailsComponent + ], + imports: [ + CommonModule, + StatusDetailRouting, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatGridListModule, + MatCardModule, + MatButtonModule, + SharedModule + ] +}) +export class StatusDetailsModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html new file mode 100644 index 0000000000..41a5ef8252 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html @@ -0,0 +1,9 @@ + +
+ +
+ + +
\ No newline at end of file diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts new file mode 100644 index 0000000000..bedd6c46ad --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StatusesTableComponent } from './statuses-table.component'; + +describe('StatusesTableComponent', () => { + let component: StatusesTableComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ StatusesTableComponent ] + }) + .compileComponents(); + + fixture = TestBed.createComponent(StatusesTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts new file mode 100644 index 0000000000..6097b743c9 --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -0,0 +1,63 @@ +import { Component, OnInit } from '@angular/core'; +import { BehaviorSubject } from 'rxjs'; +import { StatusesModel, StatusesService } from 'src/app/api/statuses.service'; +import { ContextMenuItem } from 'src/app/shared/generic-table/generic-table.component'; + +@Component({ + selector: 'tp-statuses-table', + templateUrl: './statuses-table.component.html', + styleUrls: ['./statuses-table.component.scss'] +}) +export class StatusesTableComponent implements OnInit { + + statuses: any | null = null; + columnDefs = [ + { + field: "name", + headerName: "Name", + hide: false + }, + { + field: "description", + headerName: "Description", + hide: false + }]; + + /** The current search text. */ + public searchText = ""; + + /** Definitions for the context menu items (which act on user data). */ + public contextMenuItems: Array> = [ + { + href: (u: StatusesModel): string => `${u.id}`, + name: "View Status Details" + }, + { + href: (): string => `new`, + name: "Create New Status" + } + ]; + + /** Emits changes to the fuzzy search text. */ + public fuzzySubject = new BehaviorSubject(""); + constructor( + private statusesService: StatusesService + ) { } + + ngOnInit(): void { + this.getStatuses(); + } + + /** Reloads the servers table data. */ + async getStatuses(): Promise { + this.statuses = await this.statusesService.getStatuses(); + } + + /** + * Updates the "search" query parameter in the URL every time the search + * text input changes. + */ + public updateURL(): void { + this.fuzzySubject.next(this.searchText); + } +} diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts new file mode 100644 index 0000000000..bf210b2d3e --- /dev/null +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -0,0 +1,32 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { StatusesTableComponent } from './statuses-table.component'; +import { RouterModule } from '@angular/router'; +import { SharedModule } from 'src/app/shared/shared.module'; +import { MatCardModule } from '@angular/material/card'; +import { StatusesService } from 'src/app/api/statuses.service'; +import { FormsModule } from '@angular/forms'; + +const StatusesTableRouting = RouterModule.forChild([ + { + path: '', + component: StatusesTableComponent + } +]); + +@NgModule({ + declarations: [ + StatusesTableComponent + ], + imports: [ + CommonModule, + StatusesTableRouting, + FormsModule, + MatCardModule, + SharedModule + ], + providers:[ + StatusesService + ] +}) +export class StatusesTableModule { } diff --git a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts index c1168c315f..0fd9db48d4 100644 --- a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts +++ b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts @@ -143,6 +143,12 @@ export class NavigationService { name: "Tenants" }], name: "Users" + }, { + children: [{ + href: "/core/statuses", + name: "Statuses" + }], + name: "Configure" }, { children: [{ href: "/core/change-logs", diff --git a/experimental/traffic-portal/src/styles.scss b/experimental/traffic-portal/src/styles.scss index cf3064cd55..70a755f396 100644 --- a/experimental/traffic-portal/src/styles.scss +++ b/experimental/traffic-portal/src/styles.scss @@ -152,3 +152,33 @@ button { bottom: 16px; right: 16px; } +// Breadcrumbs +/* Style the list */ +ul.breadcrumb { + padding: 5px 0px 10px 0px; + margin-bottom: 1.5em; + list-style: none; + border-bottom: 1px solid #efefef; + } + + /* Display list items side by side */ + ul.breadcrumb li { + display: inline; + font-size: 16px; + } + + /* Add a slash symbol (/) before/behind each list item */ + ul.breadcrumb li+li:before { + padding: 8px; + content: "/\00a0"; + } + + /* Add a color to all links inside the list */ + ul.breadcrumb li a { + text-decoration: none; + } + + /* Add a color on mouse-over */ + ul.breadcrumb li a:hover { + text-decoration: underline; + } \ No newline at end of file From 406c0530f2adb4183504815832079b3b38a7f1e0 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 27 Feb 2023 12:27:00 +0530 Subject: [PATCH 14/47] monu statuses moved under servers --- .../src/app/shared/navigation/navigation.service.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts index 0fd9db48d4..4ace2b1d86 100644 --- a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts +++ b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts @@ -117,7 +117,12 @@ export class NavigationService { }, { href: "/core/phys-locs", name: "Physical Locations" - }, { + }, + { + href: "/core/statuses", + name: "Statuses" + }, + { children: [{ href: "/core/cache-groups", name: "Cache Groups" @@ -143,12 +148,6 @@ export class NavigationService { name: "Tenants" }], name: "Users" - }, { - children: [{ - href: "/core/statuses", - name: "Statuses" - }], - name: "Configure" }, { children: [{ href: "/core/change-logs", From 8727ceafff484ee089c02dd8dbe367e536bffd8e Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 27 Feb 2023 12:29:08 +0530 Subject: [PATCH 15/47] breadcrumbs removed,license added,model from lib --- .../src/app/api/statuses.service.ts | 28 ++++++---------- .../status-details.component.html | 18 ++++++++--- .../status-details.component.scss | 13 ++++++++ .../status-details.component.spec.ts | 13 ++++++++ .../status-details.component.ts | 18 +++++++++-- .../status-details/status-details.module.ts | 17 ++++++++++ .../statuses-table.component.html | 13 ++++++++ .../statuses-table.component.scss | 13 ++++++++ .../statuses-table.component.spec.ts | 13 ++++++++ .../statuses-table.component.ts | 26 +++++++++++---- .../statuses-table/statuses-table.module.ts | 13 ++++++++ experimental/traffic-portal/src/styles.scss | 32 +------------------ 12 files changed, 156 insertions(+), 61 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/statuses.service.ts b/experimental/traffic-portal/src/app/api/statuses.service.ts index 3c0ee507eb..4b180f1b73 100644 --- a/experimental/traffic-portal/src/app/api/statuses.service.ts +++ b/experimental/traffic-portal/src/app/api/statuses.service.ts @@ -1,17 +1,9 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; +import { ResponseStatus } from 'trafficops-types'; import { APIService } from './base-api.service'; -export interface StatusesModel { - description?: string; - id?: number; - lastUpdated?: Date; - name?: string; -} - -@Injectable({ - providedIn: 'root' -}) +@Injectable() export class StatusesService extends APIService { /** @@ -22,23 +14,23 @@ export class StatusesService extends APIService { super(http); } - public async getStatuses(idOrName: number | string): Promise; - public async getStatuses(): Promise>; + public async getStatuses(idOrName: number | string): Promise; + public async getStatuses(): Promise>; /** * @param id Specify either the integral, unique identifier (number. * @returns The requested status(s). */ - public async getStatuses(id?: number | string): Promise | StatusesModel> { + public async getStatuses(id?: number | string): Promise | ResponseStatus> { const path = "statuses"; if (id !== undefined) { let statuses; - statuses = await this.get<[StatusesModel]>(path, undefined, { id: String(id) }).toPromise(); + statuses = await this.get<[ResponseStatus]>(path, undefined, { id: String(id) }).toPromise(); if (statuses.length < 1) { throw new Error(`no such statuses '${id}'`); } return statuses[0]; } - return this.get>(path).toPromise(); + return this.get>(path).toPromise(); } /** @@ -46,9 +38,9 @@ export class StatusesService extends APIService { * @param data containes name and description for the status. * @returns The 'response' property of the TO status response. See TO API docs. */ - public async createStatus(data: StatusesModel) { + public async createStatus(data: ResponseStatus) { const path = "statuses"; - return this.post(path, data).toPromise(); + return this.post(path, data).toPromise(); } /** @@ -56,7 +48,7 @@ export class StatusesService extends APIService { * @param data containes name and description for the status., unique identifier thereof. * @param id The Status ID */ - public async updateStatus(data: StatusesModel, id: number): Promise { + public async updateStatus(data: ResponseStatus, id: number): Promise { const path = `statuses/${id}`; return this.put(path, data).toPromise(); } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index ce6fa818f4..6fd09df714 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -1,8 +1,18 @@ + - +

{{title}}

diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss index 56d187902e..d04bcfa38d 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss @@ -1,3 +1,16 @@ +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ mat-card { margin: 1em auto; width: 95%; diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 784dd6f1a0..f001ac751b 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -1,3 +1,16 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { StatusDetailsComponent } from './status-details.component'; diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 4f43132332..bd51f32a06 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -1,9 +1,23 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; import { MatDialog } from '@angular/material/dialog'; import { ActivatedRoute, Router } from '@angular/router'; -import { StatusesModel, StatusesService } from 'src/app/api/statuses.service'; +import { StatusesService } from 'src/app/api/statuses.service'; import { DecisionDialogComponent, DecisionDialogData } from 'src/app/shared/dialogs/decision-dialog/decision-dialog.component'; +import { ResponseStatus } from 'trafficops-types'; @Component({ selector: 'tp-status-details', @@ -13,7 +27,7 @@ import { DecisionDialogComponent, DecisionDialogData } from 'src/app/shared/dial export class StatusDetailsComponent implements OnInit { id: string | null = null; - statusDetails: StatusesModel | null = null; + statusDetails: ResponseStatus | null = null; statusDetailsForm!: FormGroup; loading = false; submitting = false; diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts index ca8dc21f54..bae1f035ca 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts @@ -1,3 +1,16 @@ +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { StatusDetailsComponent } from './status-details.component'; @@ -9,6 +22,7 @@ import {MatGridListModule} from '@angular/material/grid-list'; import { MatCardModule } from '@angular/material/card'; import { SharedModule } from 'src/app/shared/shared.module'; import { MatButtonModule } from '@angular/material/button'; +import { StatusesService } from 'src/app/api/statuses.service'; const StatusDetailRouting = RouterModule.forChild([ { @@ -31,6 +45,9 @@ const StatusDetailRouting = RouterModule.forChild([ MatCardModule, MatButtonModule, SharedModule + ], + providers:[ + StatusesService ] }) export class StatusDetailsModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html index 41a5ef8252..16f44cdac8 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html @@ -1,3 +1,16 @@ +
> = [ + public contextMenuItems: Array> = [ { - href: (u: StatusesModel): string => `${u.id}`, + href: (u: ResponseStatus): string => `${u.id}`, name: "View Status Details" }, { @@ -41,7 +55,7 @@ export class StatusesTableComponent implements OnInit { /** Emits changes to the fuzzy search text. */ public fuzzySubject = new BehaviorSubject(""); constructor( - private statusesService: StatusesService + private serverService: ServerService, ) { } ngOnInit(): void { @@ -50,7 +64,7 @@ export class StatusesTableComponent implements OnInit { /** Reloads the servers table data. */ async getStatuses(): Promise { - this.statuses = await this.statusesService.getStatuses(); + this.statuses = await this.serverService.getStatuses(); } /** diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts index bf210b2d3e..0121f45e1f 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -1,3 +1,16 @@ +/* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { StatusesTableComponent } from './statuses-table.component'; diff --git a/experimental/traffic-portal/src/styles.scss b/experimental/traffic-portal/src/styles.scss index 70a755f396..8a15bf53dd 100644 --- a/experimental/traffic-portal/src/styles.scss +++ b/experimental/traffic-portal/src/styles.scss @@ -151,34 +151,4 @@ button { position: fixed; bottom: 16px; right: 16px; -} -// Breadcrumbs -/* Style the list */ -ul.breadcrumb { - padding: 5px 0px 10px 0px; - margin-bottom: 1.5em; - list-style: none; - border-bottom: 1px solid #efefef; - } - - /* Display list items side by side */ - ul.breadcrumb li { - display: inline; - font-size: 16px; - } - - /* Add a slash symbol (/) before/behind each list item */ - ul.breadcrumb li+li:before { - padding: 8px; - content: "/\00a0"; - } - - /* Add a color to all links inside the list */ - ul.breadcrumb li a { - text-decoration: none; - } - - /* Add a color on mouse-over */ - ul.breadcrumb li a:hover { - text-decoration: underline; - } \ No newline at end of file +} \ No newline at end of file From c0a237ae0ee52c5ebfa0adbb56b3ff5289307f5f Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 27 Feb 2023 14:59:35 +0530 Subject: [PATCH 16/47] lint fixes --- .../src/app/api/statuses.service.ts | 13 +- .../src/app/core/core.module.ts | 8 +- .../status-details.component.html | 10 +- .../status-details.component.scss | 4 +- .../status-details.component.ts | 221 +++++++++--------- .../status-details/status-details.module.ts | 68 +++--- .../statuses-table.component.html | 2 +- .../statuses-table.component.scss | 2 +- .../statuses-table.component.ts | 105 ++++----- .../statuses-table/statuses-table.module.ts | 52 +++-- .../shared/navigation/navigation.service.ts | 2 +- 11 files changed, 249 insertions(+), 238 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/statuses.service.ts b/experimental/traffic-portal/src/app/api/statuses.service.ts index 4b180f1b73..1cb3c24201 100644 --- a/experimental/traffic-portal/src/app/api/statuses.service.ts +++ b/experimental/traffic-portal/src/app/api/statuses.service.ts @@ -1,13 +1,15 @@ -import { HttpClient } from '@angular/common/http'; -import { Injectable } from '@angular/core'; -import { ResponseStatus } from 'trafficops-types'; -import { APIService } from './base-api.service'; +import { HttpClient } from "@angular/common/http"; +import { Injectable } from "@angular/core"; +import { ResponseStatus } from "trafficops-types"; + +import { APIService } from "./base-api.service"; @Injectable() export class StatusesService extends APIService { /** * Injects the Angular HTTP client service into the parent constructor. + * * @param http The Angular HTTP client service. */ constructor(http: HttpClient) { @@ -35,6 +37,7 @@ export class StatusesService extends APIService { /** * Creating new Status. + * * @param data containes name and description for the status. * @returns The 'response' property of the TO status response. See TO API docs. */ @@ -45,6 +48,7 @@ export class StatusesService extends APIService { /** * Updates status. + * * @param data containes name and description for the status., unique identifier thereof. * @param id The Status ID */ @@ -55,6 +59,7 @@ export class StatusesService extends APIService { /** * Deletes an existing Status. + * * @param id The Status ID */ public async deleteStatus(id: number): Promise { diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index cbe830e2bf..3b3abe1f81 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -75,12 +75,12 @@ export const ROUTES: Routes = [ { component: PhysLocDetailComponent, path: "phys-locs/:id" }, { component: PhysLocTableComponent, path: "phys-locs" }, { - path: 'statuses', - loadChildren: () => import('./statuses/statuses-table/statuses-table.module').then(m => m.StatusesTableModule) + path: "statuses", + loadChildren: async () => import("./statuses/statuses-table/statuses-table.module").then(m => m.StatusesTableModule) }, { - path: 'statuses/:id', - loadChildren: () => import('./statuses/status-details/status-details.module').then(m => m.StatusDetailsModule) + path: "statuses/:id", + loadChildren: async () => import("./statuses/status-details/status-details.module").then(m => m.StatusDetailsModule) } ].map(r => ({...r, canActivate: [AuthenticatedGuard]})); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 6fd09df714..34e3d3f109 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -16,13 +16,13 @@

{{title}}

- + Name - + - + Description - + @@ -33,4 +33,4 @@

{{title}}

- \ No newline at end of file + diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss index d04bcfa38d..0a24e3195e 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.scss @@ -19,7 +19,7 @@ mat-card { mat-card-content { display: grid; grid-template-columns: 1fr; - grid-row-gap: 2em; + row-gap: 2em; margin: 1em auto 10px; } -} \ No newline at end of file +} diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index bd51f32a06..60b5969532 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -11,121 +11,122 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Component, OnInit } from '@angular/core'; -import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'; -import { MatDialog } from '@angular/material/dialog'; -import { ActivatedRoute, Router } from '@angular/router'; -import { StatusesService } from 'src/app/api/statuses.service'; -import { DecisionDialogComponent, DecisionDialogData } from 'src/app/shared/dialogs/decision-dialog/decision-dialog.component'; -import { ResponseStatus } from 'trafficops-types'; +import { Component, OnInit } from "@angular/core"; +import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; +import { MatDialog } from "@angular/material/dialog"; +import { ActivatedRoute, Router } from "@angular/router"; +import { ResponseStatus } from "trafficops-types"; + +import { StatusesService } from "src/app/api/statuses.service"; +import { DecisionDialogComponent, DecisionDialogData } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; @Component({ - selector: 'tp-status-details', - templateUrl: './status-details.component.html', - styleUrls: ['./status-details.component.scss'] + selector: "tp-status-details", + templateUrl: "./status-details.component.html", + styleUrls: ["./status-details.component.scss"] }) export class StatusDetailsComponent implements OnInit { - id: string | null = null; - statusDetails: ResponseStatus | null = null; - statusDetailsForm!: FormGroup; - loading = false; - submitting = false; - submitted = false; - - constructor( - private route: ActivatedRoute, - private router: Router, - private fb: FormBuilder, - private readonly dialog: MatDialog, - private statusesService: StatusesService) { } - - ngOnInit(): void { - // Form is built here - this.statusDetailsForm = this.fb.group({ - name: ['', Validators.required], - description: ['', Validators.required], - }); - - // Getting id from the route - this.id = this.route.snapshot.paramMap.get('id'); - - // we check whether params is a number if not we shall assume user wants to add a new status. - if (!this.isNew) { - this.loading = true; - this.statusDetailsForm.addControl('id', new FormControl('')); - this.statusDetailsForm.addControl('lastUpdated', new FormControl('')); - this.getStatusDetails(); - } - } - - /* - * Reloads the servers table data. + id: string | null = null; + statusDetails: ResponseStatus | null = null; + statusDetailsForm!: FormGroup; + loading = false; + submitting = false; + submitted = false; + + constructor( + private readonly route: ActivatedRoute, + private readonly router: Router, + private readonly fb: FormBuilder, + private readonly dialog: MatDialog, + private readonly statusesService: StatusesService) { } + + ngOnInit(): void { + // Form is built here + this.statusDetailsForm = this.fb.group({ + name: ["", Validators.required], + description: ["", Validators.required], + }); + + // Getting id from the route + this.id = this.route.snapshot.paramMap.get("id"); + + // we check whether params is a number if not we shall assume user wants to add a new status. + if (!this.isNew) { + this.loading = true; + this.statusDetailsForm.addControl("id", new FormControl("")); + this.statusDetailsForm.addControl("lastUpdated", new FormControl("")); + this.getStatusDetails(); + } + } + + /* + * Reloads the servers table data. * @param id is the id passed in route for this page if this is a edit view. */ - async getStatusDetails(): Promise { - const id = this.id as string - this.statusDetails = await this.statusesService.getStatuses(id); - const data = { - name: this.statusDetails.name, - description: this.statusDetails.description, - lastUpdated: new Date(), - id: this.statusDetails.id - } as any; - this.statusDetailsForm.patchValue(data); - this.loading = false; - } - - // On submitting the form we check for whether we are performing Create or Edit - onSubmit() { - if (this.isNew) { - this.createStatus() - - } else { - this.updateStatus(); - } - } - - // For Creating a new status - createStatus() { - this.statusesService.createStatus(this.statusDetailsForm.value).then((res: any) => { - if (res) { - this.id = res?.id - this.router.navigate([`/core/statuses/${this.id}`]); - } - }) - } - - // For updating the Status - updateStatus() { - this.statusesService.updateStatus(this.statusDetailsForm.value, Number(this.id)); - } - - // Deleteting status - async deleteStatus() { - const ref = this.dialog.open(DecisionDialogComponent, { - data: { - message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails?.name}'.`, - title: `Delete Status: ${this.statusDetails?.name}` - } - }); - - if (await ref.afterClosed().toPromise()) { - const id = Number(this.id); - this.statusesService.deleteStatus(id).then(() => { - this.router.navigate([`/core/statuses`]); - }) - } - - } - - // Title for the page - get title(): string { - return this.isNew ? 'Add New Status' : 'Edit Status'; - } - - // Checking for params to ensure given id is a number - get isNew() { - return this.id === 'new' && isNaN(Number(this.id)); - } + async getStatusDetails(): Promise { + const id = this.id as string; // id Type 'null' is not assignable to type 'string' + this.statusDetails = await this.statusesService.getStatuses(id); + const data:ResponseStatus = { + name: this.statusDetails.name, + description: this.statusDetails.description, + lastUpdated: new Date(), + id: this.statusDetails.id + } + this.statusDetailsForm.patchValue(data); + this.loading = false; + } + + // On submitting the form we check for whether we are performing Create or Edit + onSubmit() { + if (this.isNew) { + this.createStatus(); + + } else { + this.updateStatus(); + } + } + + // For Creating a new status + createStatus() { + this.statusesService.createStatus(this.statusDetailsForm.value).then((res: any) => { + if (res) { + this.id = res?.id; + this.router.navigate([`/core/statuses/${this.id}`]); + } + }); + } + + // For updating the Status + updateStatus() { + this.statusesService.updateStatus(this.statusDetailsForm.value, Number(this.id)); + } + + // Deleteting status + async deleteStatus() { + const ref = this.dialog.open(DecisionDialogComponent, { + data: { + message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails?.name}'.`, + title: `Delete Status: ${this.statusDetails?.name}` + } + }); + + if (await ref.afterClosed().toPromise()) { + const id = Number(this.id); + this.statusesService.deleteStatus(id).then(() => { + this.router.navigate(["/core/statuses"]); + }); + } + + } + + // Title for the page + get title(): string { + return this.isNew ? "Add New Status" : "Edit Status"; + } + + // Checking for params to ensure given id is a number + get isNew() { + return this.id === "new" && isNaN(Number(this.id)); + } } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts index bae1f035ca..3871f328a7 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts @@ -11,43 +11,45 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { StatusDetailsComponent } from './status-details.component'; -import { RouterModule } from '@angular/router'; -import { ReactiveFormsModule } from '@angular/forms'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatInputModule } from '@angular/material/input'; -import {MatGridListModule} from '@angular/material/grid-list'; -import { MatCardModule } from '@angular/material/card'; -import { SharedModule } from 'src/app/shared/shared.module'; -import { MatButtonModule } from '@angular/material/button'; -import { StatusesService } from 'src/app/api/statuses.service'; +import { CommonModule } from "@angular/common"; +import { NgModule } from "@angular/core"; +import { ReactiveFormsModule } from "@angular/forms"; +import { MatButtonModule } from "@angular/material/button"; +import { MatCardModule } from "@angular/material/card"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import {MatGridListModule} from "@angular/material/grid-list"; +import { MatInputModule } from "@angular/material/input"; +import { RouterModule } from "@angular/router"; + +import { StatusesService } from "src/app/api/statuses.service"; +import { SharedModule } from "src/app/shared/shared.module"; + +import { StatusDetailsComponent } from "./status-details.component"; const StatusDetailRouting = RouterModule.forChild([ - { - path: '', - component: StatusDetailsComponent - } + { + path: "", + component: StatusDetailsComponent + } ]); @NgModule({ - declarations: [ - StatusDetailsComponent - ], - imports: [ - CommonModule, - StatusDetailRouting, - ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - MatGridListModule, - MatCardModule, - MatButtonModule, - SharedModule - ], - providers:[ - StatusesService - ] + declarations: [ + StatusDetailsComponent + ], + imports: [ + CommonModule, + StatusDetailRouting, + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatGridListModule, + MatCardModule, + MatButtonModule, + SharedModule + ], + providers:[ + StatusesService + ] }) export class StatusDetailsModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html index 16f44cdac8..9ca9f8d8c9 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html @@ -19,4 +19,4 @@ - \ No newline at end of file + diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss index 8daa74ec61..ebe77042d3 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.scss @@ -10,4 +10,4 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. -*/ \ No newline at end of file +*/ diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 51c2d97e33..7c5616c0da 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -11,67 +11,68 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Component, OnInit } from '@angular/core'; -import { BehaviorSubject } from 'rxjs'; -import { ServerService } from 'src/app/api'; -import { ContextMenuItem } from 'src/app/shared/generic-table/generic-table.component'; -import { ResponseStatus } from 'trafficops-types'; +import { Component, OnInit } from "@angular/core"; +import { BehaviorSubject } from "rxjs"; +import { ResponseStatus } from "trafficops-types"; + +import { ServerService } from "src/app/api"; +import { ContextMenuItem } from "src/app/shared/generic-table/generic-table.component"; @Component({ - selector: 'tp-statuses-table', - templateUrl: './statuses-table.component.html', - styleUrls: ['./statuses-table.component.scss'] + selector: "tp-statuses-table", + templateUrl: "./statuses-table.component.html", + styleUrls: ["./statuses-table.component.scss"] }) export class StatusesTableComponent implements OnInit { - statuses: ResponseStatus[] | null = null; - columnDefs = [ - { - field: "name", - headerName: "Name", - hide: false - }, - { - field: "description", - headerName: "Description", - hide: false - }]; + statuses: ResponseStatus[] | null = null; + columnDefs = [ + { + field: "name", + headerName: "Name", + hide: false + }, + { + field: "description", + headerName: "Description", + hide: false + }]; - /** The current search text. */ - public searchText = ""; + /** The current search text. */ + public searchText = ""; - /** Definitions for the context menu items (which act on user data). */ - public contextMenuItems: Array> = [ - { - href: (u: ResponseStatus): string => `${u.id}`, - name: "View Status Details" - }, - { - href: (): string => `new`, - name: "Create New Status" - } - ]; + /** Definitions for the context menu items (which act on user data). */ + public contextMenuItems: Array> = [ + { + href: (u: ResponseStatus): string => `${u.id}`, + name: "View Status Details" + }, + { + href: (): string => "new", + name: "Create New Status" + } + ]; - /** Emits changes to the fuzzy search text. */ - public fuzzySubject = new BehaviorSubject(""); - constructor( - private serverService: ServerService, - ) { } + /** Emits changes to the fuzzy search text. */ + public fuzzySubject = new BehaviorSubject(""); + constructor( + private readonly serverService: ServerService, + ) { } - ngOnInit(): void { - this.getStatuses(); - } + ngOnInit(): void { + this.getStatuses(); + } - /** Reloads the servers table data. */ - async getStatuses(): Promise { - this.statuses = await this.serverService.getStatuses(); - } + /** Reloads the servers table data. */ + async getStatuses(): Promise { + this.statuses = await this.serverService.getStatuses(); + } - /** - * Updates the "search" query parameter in the URL every time the search - * text input changes. - */ - public updateURL(): void { - this.fuzzySubject.next(this.searchText); - } + /** + * Updates the "search" query parameter in the URL every time the search + * text input changes. + */ + public updateURL(): void { + this.fuzzySubject.next(this.searchText); + } } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts index 0121f45e1f..35676e170c 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -11,35 +11,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { StatusesTableComponent } from './statuses-table.component'; -import { RouterModule } from '@angular/router'; -import { SharedModule } from 'src/app/shared/shared.module'; -import { MatCardModule } from '@angular/material/card'; -import { StatusesService } from 'src/app/api/statuses.service'; -import { FormsModule } from '@angular/forms'; +import { CommonModule } from "@angular/common"; +import { NgModule } from "@angular/core"; +import { FormsModule } from "@angular/forms"; +import { MatCardModule } from "@angular/material/card"; +import { RouterModule } from "@angular/router"; + +import { StatusesService } from "src/app/api/statuses.service"; +import { SharedModule } from "src/app/shared/shared.module"; + +import { StatusesTableComponent } from "./statuses-table.component"; const StatusesTableRouting = RouterModule.forChild([ - { - path: '', - component: StatusesTableComponent - } + { + path: "", + component: StatusesTableComponent + } ]); @NgModule({ - declarations: [ - StatusesTableComponent - ], - imports: [ - CommonModule, - StatusesTableRouting, - FormsModule, - MatCardModule, - SharedModule - ], - providers:[ - StatusesService - ] + declarations: [ + StatusesTableComponent + ], + imports: [ + CommonModule, + StatusesTableRouting, + FormsModule, + MatCardModule, + SharedModule + ], + providers:[ + StatusesService + ] }) export class StatusesTableModule { } diff --git a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts index 4ace2b1d86..e7e44b2e54 100644 --- a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts +++ b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts @@ -121,7 +121,7 @@ export class NavigationService { { href: "/core/statuses", name: "Statuses" - }, + }, { children: [{ href: "/core/cache-groups", From 64669c7344cceb5e687d61d20ecfc334ddf35b68 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 27 Feb 2023 19:21:48 +0530 Subject: [PATCH 17/47] lint error fixes --- .../status-details.component.spec.ts | 34 +++++++++---------- .../status-details.component.ts | 6 ++-- .../statuses-table.component.spec.ts | 34 +++++++++---------- 3 files changed, 37 insertions(+), 37 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index f001ac751b..8904d53fd7 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -11,26 +11,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { StatusDetailsComponent } from './status-details.component'; +import { StatusDetailsComponent } from "./status-details.component"; -describe('StatusDetailsComponent', () => { - let component: StatusDetailsComponent; - let fixture: ComponentFixture; +describe("StatusDetailsComponent", () => { + let component: StatusDetailsComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ StatusDetailsComponent ] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ StatusDetailsComponent ] + }) + .compileComponents(); - fixture = TestBed.createComponent(StatusDetailsComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(StatusDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it("should create", () => { + expect(component).toBeTruthy(); + }); }); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 60b5969532..1750ed9092 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -65,14 +65,14 @@ export class StatusDetailsComponent implements OnInit { * @param id is the id passed in route for this page if this is a edit view. */ async getStatusDetails(): Promise { - const id = this.id as string; // id Type 'null' is not assignable to type 'string' + const id = this.id as string; // id Type 'null' is not assignable to type 'string' this.statusDetails = await this.statusesService.getStatuses(id); - const data:ResponseStatus = { + const data: ResponseStatus = { name: this.statusDetails.name, description: this.statusDetails.description, lastUpdated: new Date(), id: this.statusDetails.id - } + }; this.statusDetailsForm.patchValue(data); this.loading = false; } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index 78ea76d00d..8d294d12d9 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -11,26 +11,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { StatusesTableComponent } from './statuses-table.component'; +import { StatusesTableComponent } from "./statuses-table.component"; -describe('StatusesTableComponent', () => { - let component: StatusesTableComponent; - let fixture: ComponentFixture; +describe("StatusesTableComponent", () => { + let component: StatusesTableComponent; + let fixture: ComponentFixture; - beforeEach(async () => { - await TestBed.configureTestingModule({ - declarations: [ StatusesTableComponent ] - }) - .compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ StatusesTableComponent ] + }) + .compileComponents(); - fixture = TestBed.createComponent(StatusesTableComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); + fixture = TestBed.createComponent(StatusesTableComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); - it('should create', () => { - expect(component).toBeTruthy(); - }); + it("should create", () => { + expect(component).toBeTruthy(); + }); }); From e26344aae887fba66fee5ae57e16f1a8f0cc9845 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 28 Feb 2023 18:32:21 +0530 Subject: [PATCH 18/47] statuses test cases added --- .../status-details.component.spec.ts | 66 +++++++++++++++++-- .../statuses-table.component.spec.ts | 28 +++++++- .../statuses-table.component.ts | 2 +- .../statuses-table/statuses-table.module.ts | 4 +- 4 files changed, 91 insertions(+), 9 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 8904d53fd7..9100f4a1d2 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -11,20 +11,54 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ComponentFixture, TestBed } from "@angular/core/testing"; - +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; +import { ReactiveFormsModule } from "@angular/forms"; +import { MatButtonModule } from "@angular/material/button"; +import { MatCardModule } from "@angular/material/card"; +import { MatFormFieldModule } from "@angular/material/form-field"; +import { MatGridListModule } from "@angular/material/grid-list"; +import { MatInputModule } from "@angular/material/input"; +import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/testing"; +import { Router } from "@angular/router"; +import { RouterTestingModule } from "@angular/router/testing"; +import { StatusesService } from "src/app/api/statuses.service"; +import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; +import { SharedModule } from "src/app/shared/shared.module"; import { StatusDetailsComponent } from "./status-details.component"; +const status = { id: 1, name: 'test', description: 'test', lastUpdated: new Date('02/02/2023') }; + describe("StatusDetailsComponent", () => { let component: StatusDetailsComponent; let fixture: ComponentFixture; + let router: Router; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ StatusDetailsComponent ] + imports: [ + HttpClientTestingModule, + RouterTestingModule.withRoutes([ + { path: 'core/statuses/:id', component: StatusDetailsComponent } + ]), + ReactiveFormsModule, + MatFormFieldModule, + MatInputModule, + MatGridListModule, + MatCardModule, + MatButtonModule, + SharedModule + ], + declarations: [StatusDetailsComponent, DecisionDialogComponent], + providers: [StatusesService] }) .compileComponents(); - + TestBed.overrideModule(BrowserDynamicTestingModule, { + set: { + entryComponents: [DecisionDialogComponent] + } + }); + router = TestBed.inject(Router); fixture = TestBed.createComponent(StatusDetailsComponent); component = fixture.componentInstance; fixture.detectChanges(); @@ -33,4 +67,28 @@ describe("StatusDetailsComponent", () => { it("should create", () => { expect(component).toBeTruthy(); }); + + it("submits a update status request", fakeAsync(() => { + const service = TestBed.inject(StatusesService); + component.statusDetailsForm.setValue(status); + spyOn(service, 'updateStatus').and.returnValue(Promise.resolve(status)); + component.updateStatus(); + + service.updateStatus(component.statusDetailsForm.value, 1).then((result) => { + expect(result).toEqual(status); + }) + })); + + it("submits a status creation request", fakeAsync(() => { + const service = TestBed.inject(StatusesService); + component.statusDetailsForm.setValue(status); + spyOn(service, 'createStatus').and.returnValue(Promise.resolve(status)); + component.createStatus(); + + service.createStatus(component.statusDetailsForm.value).then((result) => { + expect(result).toEqual(status); + router.navigate(['/core/statuses/1']); + }) + })); + }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index 8d294d12d9..f35dd28445 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -11,17 +11,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { HttpClientTestingModule } from "@angular/common/http/testing"; +import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; +import { FormsModule } from "@angular/forms"; +import { MatCardModule } from "@angular/material/card"; +import { RouterTestingModule } from "@angular/router/testing"; +import { ServerService } from "src/app/api"; +import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; +const statuses = [{id:1,name:'test',description:'test',lastUpdated:new Date('02/02/2023')}]; describe("StatusesTableComponent", () => { let component: StatusesTableComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ StatusesTableComponent ] + imports:[ + RouterTestingModule, + HttpClientTestingModule, + FormsModule, + MatCardModule, + SharedModule + ], + declarations: [ StatusesTableComponent ], + providers:[ServerService] }) .compileComponents(); @@ -33,4 +48,13 @@ describe("StatusesTableComponent", () => { it("should create", () => { expect(component).toBeTruthy(); }); + + it("should get all statuses from getStatuses",fakeAsync(()=>{ + const service = fixture.debugElement.injector.get(ServerService); + spyOn(service, 'getStatuses').and.returnValue(Promise.resolve(statuses)); + + service.getStatuses().then((result)=>{ + expect(result).toEqual(statuses); + }) + })) }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 7c5616c0da..7cfbbe1ca2 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -25,7 +25,7 @@ import { ContextMenuItem } from "src/app/shared/generic-table/generic-table.comp }) export class StatusesTableComponent implements OnInit { - statuses: ResponseStatus[] | null = null; + statuses: ResponseStatus[] = []; columnDefs = [ { field: "name", diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts index 35676e170c..e08ee31e11 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -16,8 +16,8 @@ import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; import { RouterModule } from "@angular/router"; +import { ServerService } from "src/app/api"; -import { StatusesService } from "src/app/api/statuses.service"; import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; @@ -41,7 +41,7 @@ const StatusesTableRouting = RouterModule.forChild([ SharedModule ], providers:[ - StatusesService + ServerService ] }) export class StatusesTableModule { } From 34edeb297e9972a41907d59b9ce9fd0ee985f135 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 6 Mar 2023 11:50:39 +0530 Subject: [PATCH 19/47] lint fixes --- .../status-details.component.spec.ts | 16 +++++++++------- .../statuses-table.component.spec.ts | 11 ++++++----- .../statuses-table/statuses-table.module.ts | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 9100f4a1d2..3ecdf7f83b 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -22,12 +22,14 @@ import { MatInputModule } from "@angular/material/input"; import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/testing"; import { Router } from "@angular/router"; import { RouterTestingModule } from "@angular/router/testing"; + import { StatusesService } from "src/app/api/statuses.service"; import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; import { SharedModule } from "src/app/shared/shared.module"; + import { StatusDetailsComponent } from "./status-details.component"; -const status = { id: 1, name: 'test', description: 'test', lastUpdated: new Date('02/02/2023') }; +const status = { id: 1, name: "test", description: "test", lastUpdated: new Date("02/02/2023") }; describe("StatusDetailsComponent", () => { let component: StatusDetailsComponent; @@ -39,7 +41,7 @@ describe("StatusDetailsComponent", () => { imports: [ HttpClientTestingModule, RouterTestingModule.withRoutes([ - { path: 'core/statuses/:id', component: StatusDetailsComponent } + { path: "core/statuses/:id", component: StatusDetailsComponent } ]), ReactiveFormsModule, MatFormFieldModule, @@ -71,24 +73,24 @@ describe("StatusDetailsComponent", () => { it("submits a update status request", fakeAsync(() => { const service = TestBed.inject(StatusesService); component.statusDetailsForm.setValue(status); - spyOn(service, 'updateStatus').and.returnValue(Promise.resolve(status)); + spyOn(service, "updateStatus").and.returnValue(Promise.resolve(status)); component.updateStatus(); service.updateStatus(component.statusDetailsForm.value, 1).then((result) => { expect(result).toEqual(status); - }) + }); })); it("submits a status creation request", fakeAsync(() => { const service = TestBed.inject(StatusesService); component.statusDetailsForm.setValue(status); - spyOn(service, 'createStatus').and.returnValue(Promise.resolve(status)); + spyOn(service, "createStatus").and.returnValue(Promise.resolve(status)); component.createStatus(); service.createStatus(component.statusDetailsForm.value).then((result) => { expect(result).toEqual(status); - router.navigate(['/core/statuses/1']); - }) + router.navigate(["/core/statuses/1"]); + }); })); }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index f35dd28445..7a76590e6c 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -16,12 +16,13 @@ import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; import { RouterTestingModule } from "@angular/router/testing"; + import { ServerService } from "src/app/api"; import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; -const statuses = [{id:1,name:'test',description:'test',lastUpdated:new Date('02/02/2023')}]; +const statuses = [{id:1,name:"test",description:"test",lastUpdated:new Date("02/02/2023")}]; describe("StatusesTableComponent", () => { let component: StatusesTableComponent; let fixture: ComponentFixture; @@ -51,10 +52,10 @@ describe("StatusesTableComponent", () => { it("should get all statuses from getStatuses",fakeAsync(()=>{ const service = fixture.debugElement.injector.get(ServerService); - spyOn(service, 'getStatuses').and.returnValue(Promise.resolve(statuses)); - + spyOn(service, "getStatuses").and.returnValue(Promise.resolve(statuses)); + service.getStatuses().then((result)=>{ expect(result).toEqual(statuses); - }) - })) + }); + })); }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts index e08ee31e11..59c15fb148 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts @@ -16,8 +16,8 @@ import { NgModule } from "@angular/core"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; import { RouterModule } from "@angular/router"; -import { ServerService } from "src/app/api"; +import { ServerService } from "src/app/api"; import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; From 869aea5cba8fb98e956d73754c7e48722eab478f Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 6 Mar 2023 21:00:56 +0530 Subject: [PATCH 20/47] added last line --- experimental/traffic-portal/src/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/traffic-portal/src/styles.scss b/experimental/traffic-portal/src/styles.scss index 8a15bf53dd..cf3064cd55 100644 --- a/experimental/traffic-portal/src/styles.scss +++ b/experimental/traffic-portal/src/styles.scss @@ -151,4 +151,4 @@ button { position: fixed; bottom: 16px; right: 16px; -} \ No newline at end of file +} From c234a3721e8e8ab2d4d0601f22a7020ce6440209 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 6 Mar 2023 21:10:48 +0530 Subject: [PATCH 21/47] lazy load and statuses service removed --- .../src/app/api/statuses.service.ts | 68 ------------------- .../src/app/core/core.module.ts | 20 +++--- .../status-details/status-details.module.ts | 55 --------------- .../statuses-table/statuses-table.module.ts | 47 ------------- 4 files changed, 10 insertions(+), 180 deletions(-) delete mode 100644 experimental/traffic-portal/src/app/api/statuses.service.ts delete mode 100644 experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts delete mode 100644 experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts diff --git a/experimental/traffic-portal/src/app/api/statuses.service.ts b/experimental/traffic-portal/src/app/api/statuses.service.ts deleted file mode 100644 index 1cb3c24201..0000000000 --- a/experimental/traffic-portal/src/app/api/statuses.service.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { HttpClient } from "@angular/common/http"; -import { Injectable } from "@angular/core"; -import { ResponseStatus } from "trafficops-types"; - -import { APIService } from "./base-api.service"; - -@Injectable() -export class StatusesService extends APIService { - - /** - * Injects the Angular HTTP client service into the parent constructor. - * - * @param http The Angular HTTP client service. - */ - constructor(http: HttpClient) { - super(http); - } - - public async getStatuses(idOrName: number | string): Promise; - public async getStatuses(): Promise>; - /** - * @param id Specify either the integral, unique identifier (number. - * @returns The requested status(s). - */ - public async getStatuses(id?: number | string): Promise | ResponseStatus> { - const path = "statuses"; - if (id !== undefined) { - let statuses; - statuses = await this.get<[ResponseStatus]>(path, undefined, { id: String(id) }).toPromise(); - if (statuses.length < 1) { - throw new Error(`no such statuses '${id}'`); - } - return statuses[0]; - } - return this.get>(path).toPromise(); - } - - /** - * Creating new Status. - * - * @param data containes name and description for the status. - * @returns The 'response' property of the TO status response. See TO API docs. - */ - public async createStatus(data: ResponseStatus) { - const path = "statuses"; - return this.post(path, data).toPromise(); - } - - /** - * Updates status. - * - * @param data containes name and description for the status., unique identifier thereof. - * @param id The Status ID - */ - public async updateStatus(data: ResponseStatus, id: number): Promise { - const path = `statuses/${id}`; - return this.put(path, data).toPromise(); - } - - /** - * Deletes an existing Status. - * - * @param id The Status ID - */ - public async deleteStatus(id: number): Promise { - return this.delete(`statuses/${id}`).toPromise(); - } -} diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index 3b3abe1f81..fec9dafba8 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -18,6 +18,7 @@ */ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; +import { ReactiveFormsModule } from "@angular/forms"; import { RouterModule, type Routes } from "@angular/router"; import { AppUIModule } from "../app.ui.module"; @@ -47,6 +48,8 @@ import { PhysLocTableComponent } from "./servers/phys-loc/table/phys-loc-table.c import { ServerDetailsComponent } from "./servers/server-details/server-details.component"; import { ServersTableComponent } from "./servers/servers-table/servers-table.component"; import { UpdateStatusComponent } from "./servers/update-status/update-status.component"; +import { StatusDetailsComponent } from "./statuses/status-details/status-details.component"; +import { StatusesTableComponent } from "./statuses/statuses-table/statuses-table.component"; import { TenantDetailsComponent } from "./users/tenants/tenant-details/tenant-details.component"; import { TenantsComponent } from "./users/tenants/tenants.component"; import { UserDetailsComponent } from "./users/user-details/user-details.component"; @@ -74,14 +77,8 @@ export const ROUTES: Routes = [ { component: TenantDetailsComponent, path: "tenants/:id"}, { component: PhysLocDetailComponent, path: "phys-locs/:id" }, { component: PhysLocTableComponent, path: "phys-locs" }, - { - path: "statuses", - loadChildren: async () => import("./statuses/statuses-table/statuses-table.module").then(m => m.StatusesTableModule) - }, - { - path: "statuses/:id", - loadChildren: async () => import("./statuses/status-details/status-details.module").then(m => m.StatusDetailsModule) - } + { component: StatusesTableComponent, path: "statuses" }, + { component: StatusDetailsComponent, path: "statuses/:id" }, ].map(r => ({...r, canActivate: [AuthenticatedGuard]})); /** @@ -115,14 +112,17 @@ export const ROUTES: Routes = [ DivisionDetailComponent, RegionsTableComponent, RegionDetailComponent, - CacheGroupDetailsComponent + CacheGroupDetailsComponent, + StatusesTableComponent, + StatusDetailsComponent ], exports: [], imports: [ SharedModule, AppUIModule, CommonModule, - RouterModule.forChild(ROUTES) + RouterModule.forChild(ROUTES), + ReactiveFormsModule ] }) export class CoreModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts deleted file mode 100644 index 3871f328a7..0000000000 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.module.ts +++ /dev/null @@ -1,55 +0,0 @@ -/* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; -import { ReactiveFormsModule } from "@angular/forms"; -import { MatButtonModule } from "@angular/material/button"; -import { MatCardModule } from "@angular/material/card"; -import { MatFormFieldModule } from "@angular/material/form-field"; -import {MatGridListModule} from "@angular/material/grid-list"; -import { MatInputModule } from "@angular/material/input"; -import { RouterModule } from "@angular/router"; - -import { StatusesService } from "src/app/api/statuses.service"; -import { SharedModule } from "src/app/shared/shared.module"; - -import { StatusDetailsComponent } from "./status-details.component"; - -const StatusDetailRouting = RouterModule.forChild([ - { - path: "", - component: StatusDetailsComponent - } -]); - -@NgModule({ - declarations: [ - StatusDetailsComponent - ], - imports: [ - CommonModule, - StatusDetailRouting, - ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - MatGridListModule, - MatCardModule, - MatButtonModule, - SharedModule - ], - providers:[ - StatusesService - ] -}) -export class StatusDetailsModule { } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts deleted file mode 100644 index 59c15fb148..0000000000 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.module.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ -import { CommonModule } from "@angular/common"; -import { NgModule } from "@angular/core"; -import { FormsModule } from "@angular/forms"; -import { MatCardModule } from "@angular/material/card"; -import { RouterModule } from "@angular/router"; - -import { ServerService } from "src/app/api"; -import { SharedModule } from "src/app/shared/shared.module"; - -import { StatusesTableComponent } from "./statuses-table.component"; - -const StatusesTableRouting = RouterModule.forChild([ - { - path: "", - component: StatusesTableComponent - } -]); - -@NgModule({ - declarations: [ - StatusesTableComponent - ], - imports: [ - CommonModule, - StatusesTableRouting, - FormsModule, - MatCardModule, - SharedModule - ], - providers:[ - ServerService - ] -}) -export class StatusesTableModule { } From d3cec1f74ab62af55db18f78028a31a537d00dd2 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 6 Mar 2023 21:11:18 +0530 Subject: [PATCH 22/47] lint error and comments addressed --- .../src/app/api/server.service.ts | 32 +++- .../status-details.component.html | 5 +- .../status-details.component.spec.ts | 25 ++-- .../status-details.component.ts | 139 ++++++++++++------ .../statuses-table.component.spec.ts | 8 +- .../statuses-table.component.ts | 35 ++++- 6 files changed, 169 insertions(+), 75 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/server.service.ts b/experimental/traffic-portal/src/app/api/server.service.ts index 4411056d03..54662f7d92 100644 --- a/experimental/traffic-portal/src/app/api/server.service.ts +++ b/experimental/traffic-portal/src/app/api/server.service.ts @@ -118,7 +118,8 @@ export class ServerService extends APIService { let ret; switch (typeof idOrName) { case "number": - ret = this.get<[ResponseStatus]>(path, {params: {id: String(idOrName)}}).toPromise(); + const response = await this.get<[ResponseStatus]>(path, undefined, { id: String(idOrName) }).toPromise(); + ret = response[0]; break; case "string": ret = this.get<[ResponseStatus]>(path, {params: {name: idOrName}}).toPromise(); @@ -186,4 +187,33 @@ export class ServerService extends APIService { return this.put(`servers/${id}/status`, {offlineReason, status}).toPromise(); } + + /** + * Creating new Status. + * + * @param data containes name and description for the status. + * @returns The 'response' property of the TO status response. See TO API docs. + */ + public async createStatus(data: ResponseStatus): Promise { + return this.post("statuses", data).toPromise(); + } + + /** + * Updates status Details. + * + * @param data containes name and description for the status., unique identifier thereof. + * @param id The Status ID + */ + public async updateStatusDetail(data: ResponseStatus, id: number): Promise { + return this.put(`statuses/${id}`, data).toPromise(); + } + + /** + * Deletes an existing Status. + * + * @param id The Status ID + */ + public async deleteStatus(id: number): Promise { + return this.delete(`statuses/${id}`).toPromise(); + } } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 34e3d3f109..3d303bf1ef 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -12,17 +12,16 @@ limitations under the License. --> -

{{title}}

Name - + Description - + diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 3ecdf7f83b..b1c0216eeb 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -12,7 +12,7 @@ * limitations under the License. */ import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { ReactiveFormsModule } from "@angular/forms"; import { MatButtonModule } from "@angular/material/button"; import { MatCardModule } from "@angular/material/card"; @@ -23,13 +23,13 @@ import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/t import { Router } from "@angular/router"; import { RouterTestingModule } from "@angular/router/testing"; -import { StatusesService } from "src/app/api/statuses.service"; +import { ServerService } from "src/app/api"; import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; import { SharedModule } from "src/app/shared/shared.module"; import { StatusDetailsComponent } from "./status-details.component"; -const status = { id: 1, name: "test", description: "test", lastUpdated: new Date("02/02/2023") }; +const status = { description: "test", id: 1,lastUpdated: new Date("02/02/2023"), name: "test"}; describe("StatusDetailsComponent", () => { let component: StatusDetailsComponent; @@ -38,10 +38,11 @@ describe("StatusDetailsComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ + declarations: [StatusDetailsComponent, DecisionDialogComponent], imports: [ HttpClientTestingModule, RouterTestingModule.withRoutes([ - { path: "core/statuses/:id", component: StatusDetailsComponent } + { component: StatusDetailsComponent, path: "core/statuses/:id" } ]), ReactiveFormsModule, MatFormFieldModule, @@ -51,8 +52,7 @@ describe("StatusDetailsComponent", () => { MatButtonModule, SharedModule ], - declarations: [StatusDetailsComponent, DecisionDialogComponent], - providers: [StatusesService] + providers: [ServerService] }) .compileComponents(); TestBed.overrideModule(BrowserDynamicTestingModule, { @@ -70,20 +70,21 @@ describe("StatusDetailsComponent", () => { expect(component).toBeTruthy(); }); - it("submits a update status request", fakeAsync(() => { - const service = TestBed.inject(StatusesService); + it("submits a update status request", (() => { + const service = TestBed.inject(ServerService); component.statusDetailsForm.setValue(status); - spyOn(service, "updateStatus").and.returnValue(Promise.resolve(status)); + spyOn(service, "updateStatusDetail").and.returnValue(Promise.resolve(status)); component.updateStatus(); - service.updateStatus(component.statusDetailsForm.value, 1).then((result) => { + service.updateStatus(component.statusDetailsForm.value, "1").then((result) => { expect(result).toEqual(status); }); })); - it("submits a status creation request", fakeAsync(() => { - const service = TestBed.inject(StatusesService); + it("submits a status creation request", (() => { + const service = TestBed.inject(ServerService); component.statusDetailsForm.setValue(status); + spyOn(service, "createStatus").and.returnValue(Promise.resolve(status)); component.createStatus(); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 1750ed9092..13509542b4 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -12,40 +12,61 @@ * limitations under the License. */ import { Component, OnInit } from "@angular/core"; -import { FormBuilder, FormControl, FormGroup, Validators } from "@angular/forms"; +import { FormControl, FormGroup, Validators } from "@angular/forms"; import { MatDialog } from "@angular/material/dialog"; import { ActivatedRoute, Router } from "@angular/router"; import { ResponseStatus } from "trafficops-types"; -import { StatusesService } from "src/app/api/statuses.service"; +import { ServerService } from "src/app/api"; import { DecisionDialogComponent, DecisionDialogData } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; +import { NavigationService } from "src/app/shared/navigation/navigation.service"; +/** + * StatusDetailsComponent is the controller for a status "details" page. + */ @Component({ + providers: [ServerService], selector: "tp-status-details", + styleUrls: ["./status-details.component.scss"], templateUrl: "./status-details.component.html", - styleUrls: ["./status-details.component.scss"] }) export class StatusDetailsComponent implements OnInit { - id: string | null = null; - statusDetails: ResponseStatus | null = null; - statusDetailsForm!: FormGroup; - loading = false; - submitting = false; - submitted = false; + /** Status ID expected from the route param using which we identify whether we are creating new status or load existing status */ + public id: string | number | null = null; + + /** All details of status requested */ + public statusDetails: ResponseStatus | null = null; + + /** Reactive form intialized to creat / edit status details */ + public statusDetailsForm!: FormGroup; + /** Loader status for the actions */ + public loading = false; + + /** + * Constructor. + * + * @param serverService The Servers API which is used to provide row data. + * @param route A reference to the route of this view which is used to get the 'id' query parameter of status. + * @param router Angular router + * @param dialog Dialog manager + * @param fb Form builder + * @param navSvc Manages the header + */ constructor( + private readonly serverService: ServerService, private readonly route: ActivatedRoute, private readonly router: Router, - private readonly fb: FormBuilder, private readonly dialog: MatDialog, - private readonly statusesService: StatusesService) { } - - ngOnInit(): void { - // Form is built here - this.statusDetailsForm = this.fb.group({ - name: ["", Validators.required], - description: ["", Validators.required], + private readonly navSvc: NavigationService, + ) { } + + /** Initializes table data, loading it from Traffic Ops. */ + public ngOnInit(): void { + this.statusDetailsForm = new FormGroup({ + description: new FormControl("",Validators.required), + name: new FormControl("",Validators.required), }); // Getting id from the route @@ -57,28 +78,38 @@ export class StatusDetailsComponent implements OnInit { this.statusDetailsForm.addControl("id", new FormControl("")); this.statusDetailsForm.addControl("lastUpdated", new FormControl("")); this.getStatusDetails(); + } else { + this.navSvc.headerTitle.next("New Status"); } } - /* - * Reloads the servers table data. - * @param id is the id passed in route for this page if this is a edit view. - */ - async getStatusDetails(): Promise { - const id = this.id as string; // id Type 'null' is not assignable to type 'string' - this.statusDetails = await this.statusesService.getStatuses(id); + /** + * Reloads the servers table data. + * + * @param id is the id passed in route for this page if this is a edit view. + */ + public async getStatusDetails(): Promise { + const id = Number(this.id) ; // id Type 'null' is not assignable to type 'string' + this.statusDetails = await this.serverService.getStatuses(id); const data: ResponseStatus = { - name: this.statusDetails.name, description: this.statusDetails.description, + id: this.statusDetails.id, lastUpdated: new Date(), - id: this.statusDetails.id + name: this.statusDetails.name }; + + // Set page title with status ID + this.navSvc.headerTitle.next(`Status #${data.id}`); + + // Patch the form with existing data we got from service requested above. this.statusDetailsForm.patchValue(data); this.loading = false; } - // On submitting the form we check for whether we are performing Create or Edit - onSubmit() { + /** + * On submitting the form we check for whether we are performing Create or Edit + */ + public onSubmit(): void { if (this.isNew) { this.createStatus(); @@ -87,23 +118,30 @@ export class StatusDetailsComponent implements OnInit { } } - // For Creating a new status - createStatus() { - this.statusesService.createStatus(this.statusDetailsForm.value).then((res: any) => { + /** + * For Creating a new status + */ + public createStatus(): void { + this.serverService.createStatus(this.statusDetailsForm.value).then((res: ResponseStatus) => { if (res) { - this.id = res?.id; + this.id = (res?.id); this.router.navigate([`/core/statuses/${this.id}`]); + this.navSvc.headerTitle.next(`Status #${this.id}`); } }); } - // For updating the Status - updateStatus() { - this.statusesService.updateStatus(this.statusDetailsForm.value, Number(this.id)); + /** + * For updating the Status + */ + public updateStatus(): void { + this.serverService.updateStatusDetail(this.statusDetailsForm.value, Number(this.id)); } - // Deleteting status - async deleteStatus() { + /** + * Deleteting status + */ + public async deleteStatus(): Promise { const ref = this.dialog.open(DecisionDialogComponent, { data: { message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails?.name}'.`, @@ -111,22 +149,27 @@ export class StatusDetailsComponent implements OnInit { } }); - if (await ref.afterClosed().toPromise()) { - const id = Number(this.id); - this.statusesService.deleteStatus(id).then(() => { - this.router.navigate(["/core/statuses"]); - }); - } - + ref.afterClosed().subscribe(result => { + if (result) { + const id = Number(this.id); + this.serverService.deleteStatus(id).then(() => { + this.router.navigate(["/core/statuses"]); + }); + } + }); } - // Title for the page - get title(): string { + /** + * Title for the page + */ + public get title(): string { return this.isNew ? "Add New Status" : "Edit Status"; } - // Checking for params to ensure given id is a number - get isNew() { + /** + * Checking for params to ensure given id is a number + */ + public get isNew(): boolean { return this.id === "new" && isNaN(Number(this.id)); } } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index 7a76590e6c..df4506bc3d 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -12,7 +12,7 @@ * limitations under the License. */ import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ComponentFixture, fakeAsync, TestBed } from "@angular/core/testing"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { FormsModule } from "@angular/forms"; import { MatCardModule } from "@angular/material/card"; import { RouterTestingModule } from "@angular/router/testing"; @@ -22,13 +22,14 @@ import { SharedModule } from "src/app/shared/shared.module"; import { StatusesTableComponent } from "./statuses-table.component"; -const statuses = [{id:1,name:"test",description:"test",lastUpdated:new Date("02/02/2023")}]; +const statuses = [{description: "test", id: 1,lastUpdated: new Date("02/02/2023"), name: "test"}]; describe("StatusesTableComponent", () => { let component: StatusesTableComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ + declarations: [ StatusesTableComponent ], imports:[ RouterTestingModule, HttpClientTestingModule, @@ -36,7 +37,6 @@ describe("StatusesTableComponent", () => { MatCardModule, SharedModule ], - declarations: [ StatusesTableComponent ], providers:[ServerService] }) .compileComponents(); @@ -50,7 +50,7 @@ describe("StatusesTableComponent", () => { expect(component).toBeTruthy(); }); - it("should get all statuses from getStatuses",fakeAsync(()=>{ + it("should get all statuses from getStatuses",(()=>{ const service = fixture.debugElement.injector.get(ServerService); spyOn(service, "getStatuses").and.returnValue(Promise.resolve(statuses)); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 7cfbbe1ca2..57576a4410 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -17,16 +17,25 @@ import { ResponseStatus } from "trafficops-types"; import { ServerService } from "src/app/api"; import { ContextMenuItem } from "src/app/shared/generic-table/generic-table.component"; +import { NavigationService } from "src/app/shared/navigation/navigation.service"; +/** + * StatusesTableComponent is the controller for the statuses page - which + * principally contains a table. + */ @Component({ + providers:[ ServerService ], selector: "tp-statuses-table", + styleUrls: ["./statuses-table.component.scss"], templateUrl: "./statuses-table.component.html", - styleUrls: ["./statuses-table.component.scss"] }) export class StatusesTableComponent implements OnInit { - statuses: ResponseStatus[] = []; - columnDefs = [ + /** All of the statues which should appear in the table. */ + public statuses: ResponseStatus[] = []; + + /** Definitions of the table's columns according to the ag-grid API */ + public columnDefs = [ { field: "name", headerName: "Name", @@ -41,7 +50,7 @@ export class StatusesTableComponent implements OnInit { /** The current search text. */ public searchText = ""; - /** Definitions for the context menu items (which act on user data). */ + /** Definitions for the context menu items (which act on statuses data). */ public contextMenuItems: Array> = [ { href: (u: ResponseStatus): string => `${u.id}`, @@ -55,16 +64,28 @@ export class StatusesTableComponent implements OnInit { /** Emits changes to the fuzzy search text. */ public fuzzySubject = new BehaviorSubject(""); + + /** + * Constructs the component with its required injections. + * + * @param serverService The Servers API which is used to provide row data. + * @param navSvc Manages the header + */ constructor( private readonly serverService: ServerService, - ) { } + private readonly navSvc: NavigationService, + ) { + this.fuzzySubject = new BehaviorSubject(""); + this.navSvc.headerTitle.next("Statuses"); + } - ngOnInit(): void { + /** Initializes table data, loading it from Traffic Ops. */ + public ngOnInit(): void { this.getStatuses(); } /** Reloads the servers table data. */ - async getStatuses(): Promise { + public async getStatuses(): Promise { this.statuses = await this.serverService.getStatuses(); } From d0c2728fddb1dfd98ad876e3b23eada6e2a55b05 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 7 Mar 2023 07:16:13 +0530 Subject: [PATCH 23/47] removed reactive forms module import --- experimental/traffic-portal/src/app/core/core.module.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index fec9dafba8..062e380c12 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -18,7 +18,6 @@ */ import { CommonModule } from "@angular/common"; import { NgModule } from "@angular/core"; -import { ReactiveFormsModule } from "@angular/forms"; import { RouterModule, type Routes } from "@angular/router"; import { AppUIModule } from "../app.ui.module"; @@ -121,8 +120,7 @@ export const ROUTES: Routes = [ SharedModule, AppUIModule, CommonModule, - RouterModule.forChild(ROUTES), - ReactiveFormsModule + RouterModule.forChild(ROUTES) ] }) export class CoreModule { } From 0270d98178d5dc12d086e9ebd3e0df676dff4c6a Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:09:12 +0530 Subject: [PATCH 24/47] comments addressed --- .../src/app/api/server.service.ts | 10 +-- .../src/app/api/testing/server.service.ts | 58 ++++++++++++++++- .../status-details.component.html | 4 +- .../status-details.component.ts | 62 ++++++------------- 4 files changed, 83 insertions(+), 51 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/server.service.ts b/experimental/traffic-portal/src/app/api/server.service.ts index 54662f7d92..f8c06e429d 100644 --- a/experimental/traffic-portal/src/app/api/server.service.ts +++ b/experimental/traffic-portal/src/app/api/server.service.ts @@ -14,7 +14,7 @@ import { HttpClient } from "@angular/common/http"; import { Injectable } from "@angular/core"; -import type { RequestServer, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; +import type { RequestServer, RequestStatus, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; import { APIService } from "./base-api.service"; @@ -194,8 +194,8 @@ export class ServerService extends APIService { * @param data containes name and description for the status. * @returns The 'response' property of the TO status response. See TO API docs. */ - public async createStatus(data: ResponseStatus): Promise { - return this.post("statuses", data).toPromise(); + public async createStatus(payload: RequestStatus): Promise { + return this.post("statuses", payload).toPromise(); } /** @@ -204,8 +204,8 @@ export class ServerService extends APIService { * @param data containes name and description for the status., unique identifier thereof. * @param id The Status ID */ - public async updateStatusDetail(data: ResponseStatus, id: number): Promise { - return this.put(`statuses/${id}`, data).toPromise(); + public async updateStatusDetail(payload: ResponseStatus, id: number): Promise { + return this.put(`statuses/${id}`, payload).toPromise(); } /** diff --git a/experimental/traffic-portal/src/app/api/testing/server.service.ts b/experimental/traffic-portal/src/app/api/testing/server.service.ts index a33ed5bb64..68136ddff4 100644 --- a/experimental/traffic-portal/src/app/api/testing/server.service.ts +++ b/experimental/traffic-portal/src/app/api/testing/server.service.ts @@ -13,9 +13,10 @@ */ import { Injectable } from "@angular/core"; -import type { RequestServer, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; +import type { RequestServer, RequestStatus, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; import { CDNService, PhysicalLocationService, ProfileService, TypeService } from ".."; +import { HttpResponse } from "@angular/common/http"; /** * Generates a `Servercheck` for a given `server`. @@ -47,7 +48,7 @@ export class ServerService { public servers = new Array(); - private readonly statuses = [ + private statuses = [ { description: "Sever is administrative down and does not receive traffic.", id: 4, @@ -87,6 +88,7 @@ export class ServerService { ]; private idCounter = 1; + private statusIdCounter = 6; constructor( private readonly cdnService: CDNService, @@ -302,4 +304,56 @@ export class ServerService { srv.statusId = status.id; srv.offlineReason = offlineReason ?? null; } + + /** + * Creates a status. + * + * @param status The status details (name & description) to create. + * @returns The status as created and returned by the API. + */ + public async createStatus(status: RequestStatus): Promise { + const newStatus = { + ...status, + id: ++this.statusIdCounter, + lastUpdated: new Date() + } as { description: string; id: number; lastUpdated: Date; name: string; }; + this.statuses.push(newStatus); + return newStatus; + } + + /** + * Updates status Details. + * + * @param data containes name and description for the status., unique identifier thereof. + * @param id The Status ID + */ + public async updateStatusDetail(payload: ResponseStatus, id: number): Promise { + const index = this.statuses.findIndex(u => u.id === id); + if (index < 0) { + throw new Error(`no such status with id: ${id}`); + } + const updated = { + ...payload, + lastUpdated: new Date() + } as { description: string; id: number; lastUpdated: Date; name: string; }; + this.statuses[index] = updated; + + return updated; + } + + + /** + * Deletes a Status. + * + * @param id The ID of the Status to delete. + * @returns The response. + */ + public async deleteStatus(id: number): Promise | null> { + const idx = this.statuses.findIndex(j => j.id === id); + if (idx < 0) { + throw new Error(`no such status: #${id}`); + } + this.statuses.splice(idx, 1); + return new HttpResponse({ body: { alerts: [{ level: "success", text: "Successfully logged in." }] }, status: 200 }) + } } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 3d303bf1ef..7846bf31fc 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -25,10 +25,10 @@ - + diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 13509542b4..4d7fbbf0a7 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -35,14 +35,17 @@ export class StatusDetailsComponent implements OnInit { /** Status ID expected from the route param using which we identify whether we are creating new status or load existing status */ public id: string | number | null = null; + /** Loader status for the actions */ + public loading = false; + /** All details of status requested */ - public statusDetails: ResponseStatus | null = null; + public statusDetails!: ResponseStatus; /** Reactive form intialized to creat / edit status details */ - public statusDetailsForm!: FormGroup; - - /** Loader status for the actions */ - public loading = false; + public statusDetailsForm: FormGroup = new FormGroup({ + description: new FormControl("", Validators.required), + name: new FormControl("", Validators.required), + }); /** * Constructor. @@ -60,20 +63,16 @@ export class StatusDetailsComponent implements OnInit { private readonly router: Router, private readonly dialog: MatDialog, private readonly navSvc: NavigationService, - ) { } + ) { + // Getting id from the route + this.id = this.route.snapshot.paramMap.get("id"); + } /** Initializes table data, loading it from Traffic Ops. */ public ngOnInit(): void { - this.statusDetailsForm = new FormGroup({ - description: new FormControl("",Validators.required), - name: new FormControl("",Validators.required), - }); - - // Getting id from the route - this.id = this.route.snapshot.paramMap.get("id"); // we check whether params is a number if not we shall assume user wants to add a new status. - if (!this.isNew) { + if (this.id !== "new") { this.loading = true; this.statusDetailsForm.addControl("id", new FormControl("")); this.statusDetailsForm.addControl("lastUpdated", new FormControl("")); @@ -89,20 +88,14 @@ export class StatusDetailsComponent implements OnInit { * @param id is the id passed in route for this page if this is a edit view. */ public async getStatusDetails(): Promise { - const id = Number(this.id) ; // id Type 'null' is not assignable to type 'string' + const id = Number(this.id); // id Type 'null' is not assignable to type 'string' this.statusDetails = await this.serverService.getStatuses(id); - const data: ResponseStatus = { - description: this.statusDetails.description, - id: this.statusDetails.id, - lastUpdated: new Date(), - name: this.statusDetails.name - }; - // Set page title with status ID - this.navSvc.headerTitle.next(`Status #${data.id}`); + // Set page title with status Name + this.navSvc.headerTitle.next(`Status #${this.statusDetails.name}`); // Patch the form with existing data we got from service requested above. - this.statusDetailsForm.patchValue(data); + this.statusDetailsForm.patchValue(this.statusDetails); this.loading = false; } @@ -110,7 +103,7 @@ export class StatusDetailsComponent implements OnInit { * On submitting the form we check for whether we are performing Create or Edit */ public onSubmit(): void { - if (this.isNew) { + if (this.id === "new") { this.createStatus(); } else { @@ -124,9 +117,8 @@ export class StatusDetailsComponent implements OnInit { public createStatus(): void { this.serverService.createStatus(this.statusDetailsForm.value).then((res: ResponseStatus) => { if (res) { - this.id = (res?.id); - this.router.navigate([`/core/statuses/${this.id}`]); - this.navSvc.headerTitle.next(`Status #${this.id}`); + this.statusDetails = res; + this.router.navigate(["/core/statuses"]); } }); } @@ -158,18 +150,4 @@ export class StatusDetailsComponent implements OnInit { } }); } - - /** - * Title for the page - */ - public get title(): string { - return this.isNew ? "Add New Status" : "Edit Status"; - } - - /** - * Checking for params to ensure given id is a number - */ - public get isNew(): boolean { - return this.id === "new" && isNaN(Number(this.id)); - } } From cfcadaf1dfe94495b87a93fb1ceb42d040187840 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:46:09 +0530 Subject: [PATCH 25/47] lint fixes --- experimental/traffic-portal/.eslintrc.json | 7 ++++++- .../traffic-portal/src/app/api/server.service.ts | 4 ++-- .../src/app/api/testing/server.service.ts | 11 +++++------ 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/experimental/traffic-portal/.eslintrc.json b/experimental/traffic-portal/.eslintrc.json index 65a76ad4f1..500f64c4bb 100644 --- a/experimental/traffic-portal/.eslintrc.json +++ b/experimental/traffic-portal/.eslintrc.json @@ -237,7 +237,12 @@ "enableFixer": false } ], - "@typescript-eslint/unbound-method": "error", + "@typescript-eslint/unbound-method": [ + "error", + { + "ignoreStatic": true + } + ], "@typescript-eslint/restrict-plus-operands": "error", "@typescript-eslint/triple-slash-reference": "error", "arrow-parens": [ diff --git a/experimental/traffic-portal/src/app/api/server.service.ts b/experimental/traffic-portal/src/app/api/server.service.ts index f8c06e429d..235fa6390a 100644 --- a/experimental/traffic-portal/src/app/api/server.service.ts +++ b/experimental/traffic-portal/src/app/api/server.service.ts @@ -191,7 +191,7 @@ export class ServerService extends APIService { /** * Creating new Status. * - * @param data containes name and description for the status. + * @param payload containes name and description for the status. * @returns The 'response' property of the TO status response. See TO API docs. */ public async createStatus(payload: RequestStatus): Promise { @@ -201,7 +201,7 @@ export class ServerService extends APIService { /** * Updates status Details. * - * @param data containes name and description for the status., unique identifier thereof. + * @param payload containes name and description for the status., unique identifier thereof. * @param id The Status ID */ public async updateStatusDetail(payload: ResponseStatus, id: number): Promise { diff --git a/experimental/traffic-portal/src/app/api/testing/server.service.ts b/experimental/traffic-portal/src/app/api/testing/server.service.ts index 68136ddff4..94d26c975d 100644 --- a/experimental/traffic-portal/src/app/api/testing/server.service.ts +++ b/experimental/traffic-portal/src/app/api/testing/server.service.ts @@ -12,11 +12,11 @@ * limitations under the License. */ +import { HttpResponse } from "@angular/common/http"; import { Injectable } from "@angular/core"; import type { RequestServer, RequestStatus, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; import { CDNService, PhysicalLocationService, ProfileService, TypeService } from ".."; -import { HttpResponse } from "@angular/common/http"; /** * Generates a `Servercheck` for a given `server`. @@ -316,7 +316,7 @@ export class ServerService { ...status, id: ++this.statusIdCounter, lastUpdated: new Date() - } as { description: string; id: number; lastUpdated: Date; name: string; }; + } as { description: string; id: number; lastUpdated: Date; name: string }; this.statuses.push(newStatus); return newStatus; } @@ -324,7 +324,7 @@ export class ServerService { /** * Updates status Details. * - * @param data containes name and description for the status., unique identifier thereof. + * @param payload containes name and description for the status., unique identifier thereof. * @param id The Status ID */ public async updateStatusDetail(payload: ResponseStatus, id: number): Promise { @@ -335,13 +335,12 @@ export class ServerService { const updated = { ...payload, lastUpdated: new Date() - } as { description: string; id: number; lastUpdated: Date; name: string; }; + } as { description: string; id: number; lastUpdated: Date; name: string }; this.statuses[index] = updated; return updated; } - /** * Deletes a Status. * @@ -354,6 +353,6 @@ export class ServerService { throw new Error(`no such status: #${id}`); } this.statuses.splice(idx, 1); - return new HttpResponse({ body: { alerts: [{ level: "success", text: "Successfully logged in." }] }, status: 200 }) + return new HttpResponse({ body: { alerts: [{ level: "success", text: "Successfully logged in." }] }, status: 200 }); } } From 3b580332173f9c74aa8dbb84096f858fae5c3cfa Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Fri, 24 Mar 2023 13:24:24 +0530 Subject: [PATCH 26/47] test clean up statuses clean up to write fresh test cases --- .../status-details.component.spec.ts | 92 +++++++------------ .../status-details.component.ts | 1 - .../statuses-table.component.spec.ts | 35 ++----- .../statuses-table.component.ts | 1 - 4 files changed, 44 insertions(+), 85 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index b1c0216eeb..d9df16c6dc 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -11,56 +11,56 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { ReactiveFormsModule } from "@angular/forms"; -import { MatButtonModule } from "@angular/material/button"; -import { MatCardModule } from "@angular/material/card"; -import { MatFormFieldModule } from "@angular/material/form-field"; -import { MatGridListModule } from "@angular/material/grid-list"; -import { MatInputModule } from "@angular/material/input"; -import { BrowserDynamicTestingModule } from "@angular/platform-browser-dynamic/testing"; -import { Router } from "@angular/router"; -import { RouterTestingModule } from "@angular/router/testing"; -import { ServerService } from "src/app/api"; -import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; -import { SharedModule } from "src/app/shared/shared.module"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { StatusDetailsComponent } from "./status-details.component"; +import { HttpClientModule } from "@angular/common/http"; +import { FormsModule, ReactiveFormsModule } from "@angular/forms"; +import { RouterTestingModule } from "@angular/router/testing"; +import { APITestingModule } from "src/app/api/testing"; +import { MatDialog } from "@angular/material/dialog"; +import { Observable, of } from "rxjs"; + +/** + * Define the MockDialog + */ +class MockDialog { -const status = { description: "test", id: 1,lastUpdated: new Date("02/02/2023"), name: "test"}; + /** + * Fake opens the dialog + * + * @returns unknown + */ + public open(): unknown { + return { + afterClosed: (): Observable => of(true) + }; + } +} describe("StatusDetailsComponent", () => { let component: StatusDetailsComponent; let fixture: ComponentFixture; - let router: Router; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [StatusDetailsComponent, DecisionDialogComponent], - imports: [ - HttpClientTestingModule, - RouterTestingModule.withRoutes([ - { component: StatusDetailsComponent, path: "core/statuses/:id" } + declarations: [StatusDetailsComponent], + imports:[ + HttpClientModule, + RouterTestingModule.withRoutes([ + {component: StatusDetailsComponent, path: "statuses/:id"}, + {component: StatusDetailsComponent, path: "statuses/new"} ]), + FormsModule, ReactiveFormsModule, - MatFormFieldModule, - MatInputModule, - MatGridListModule, - MatCardModule, - MatButtonModule, - SharedModule + APITestingModule ], - providers: [ServerService] + providers:[ + { provide: MatDialog, useClass: MockDialog }, + ] }) .compileComponents(); - TestBed.overrideModule(BrowserDynamicTestingModule, { - set: { - entryComponents: [DecisionDialogComponent] - } - }); - router = TestBed.inject(Router); fixture = TestBed.createComponent(StatusDetailsComponent); component = fixture.componentInstance; fixture.detectChanges(); @@ -70,28 +70,4 @@ describe("StatusDetailsComponent", () => { expect(component).toBeTruthy(); }); - it("submits a update status request", (() => { - const service = TestBed.inject(ServerService); - component.statusDetailsForm.setValue(status); - spyOn(service, "updateStatusDetail").and.returnValue(Promise.resolve(status)); - component.updateStatus(); - - service.updateStatus(component.statusDetailsForm.value, "1").then((result) => { - expect(result).toEqual(status); - }); - })); - - it("submits a status creation request", (() => { - const service = TestBed.inject(ServerService); - component.statusDetailsForm.setValue(status); - - spyOn(service, "createStatus").and.returnValue(Promise.resolve(status)); - component.createStatus(); - - service.createStatus(component.statusDetailsForm.value).then((result) => { - expect(result).toEqual(status); - router.navigate(["/core/statuses/1"]); - }); - })); - }); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 4d7fbbf0a7..68cb37944e 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -25,7 +25,6 @@ import { NavigationService } from "src/app/shared/navigation/navigation.service" * StatusDetailsComponent is the controller for a status "details" page. */ @Component({ - providers: [ServerService], selector: "tp-status-details", styleUrls: ["./status-details.component.scss"], templateUrl: "./status-details.component.html", diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index df4506bc3d..cca67d8498 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -11,18 +11,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { HttpClientTestingModule } from "@angular/common/http/testing"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { FormsModule } from "@angular/forms"; -import { MatCardModule } from "@angular/material/card"; -import { RouterTestingModule } from "@angular/router/testing"; - -import { ServerService } from "src/app/api"; -import { SharedModule } from "src/app/shared/shared.module"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { StatusesTableComponent } from "./statuses-table.component"; +import { HttpClientModule } from "@angular/common/http"; +import { RouterTestingModule } from "@angular/router/testing"; +import { APITestingModule } from "src/app/api/testing"; -const statuses = [{description: "test", id: 1,lastUpdated: new Date("02/02/2023"), name: "test"}]; describe("StatusesTableComponent", () => { let component: StatusesTableComponent; let fixture: ComponentFixture; @@ -31,13 +26,12 @@ describe("StatusesTableComponent", () => { await TestBed.configureTestingModule({ declarations: [ StatusesTableComponent ], imports:[ - RouterTestingModule, - HttpClientTestingModule, - FormsModule, - MatCardModule, - SharedModule - ], - providers:[ServerService] + HttpClientModule, + RouterTestingModule.withRoutes([ + {component: StatusesTableComponent, path: ""}, + ]), + APITestingModule + ] }) .compileComponents(); @@ -49,13 +43,4 @@ describe("StatusesTableComponent", () => { it("should create", () => { expect(component).toBeTruthy(); }); - - it("should get all statuses from getStatuses",(()=>{ - const service = fixture.debugElement.injector.get(ServerService); - spyOn(service, "getStatuses").and.returnValue(Promise.resolve(statuses)); - - service.getStatuses().then((result)=>{ - expect(result).toEqual(statuses); - }); - })); }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 57576a4410..0f02debf6a 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -24,7 +24,6 @@ import { NavigationService } from "src/app/shared/navigation/navigation.service" * principally contains a table. */ @Component({ - providers:[ ServerService ], selector: "tp-statuses-table", styleUrls: ["./statuses-table.component.scss"], templateUrl: "./statuses-table.component.html", From 7c42dc33cb40fd2b2aa6a5ff47dbe1696a136054 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Fri, 24 Mar 2023 15:07:37 +0530 Subject: [PATCH 27/47] test file base correction for future test cases --- .../status-details.component.spec.ts | 37 ++++++++++--------- .../statuses-table.component.spec.ts | 12 +++--- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index d9df16c6dc..6ebe7ca780 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -12,15 +12,17 @@ * limitations under the License. */ -import { ComponentFixture, TestBed } from "@angular/core/testing"; - -import { StatusDetailsComponent } from "./status-details.component"; import { HttpClientModule } from "@angular/common/http"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { FormsModule, ReactiveFormsModule } from "@angular/forms"; -import { RouterTestingModule } from "@angular/router/testing"; -import { APITestingModule } from "src/app/api/testing"; import { MatDialog } from "@angular/material/dialog"; -import { Observable, of } from "rxjs"; +import { RouterTestingModule } from "@angular/router/testing"; +import { Observable, ReplaySubject, of } from "rxjs"; + +import { ServerService } from "src/app/api"; +import { NavigationService } from "src/app/shared/navigation/navigation.service"; + +import { StatusDetailsComponent } from "./status-details.component"; /** * Define the MockDialog @@ -44,21 +46,22 @@ describe("StatusDetailsComponent", () => { let fixture: ComponentFixture; beforeEach(async () => { + + const navSvc = jasmine.createSpyObj([], { headerHidden: new ReplaySubject(), headerTitle: new ReplaySubject() }); + await TestBed.configureTestingModule({ declarations: [StatusDetailsComponent], - imports:[ + imports: [ HttpClientModule, - RouterTestingModule.withRoutes([ - {component: StatusDetailsComponent, path: "statuses/:id"}, - {component: StatusDetailsComponent, path: "statuses/new"} - ]), - FormsModule, - ReactiveFormsModule, - APITestingModule + RouterTestingModule, + FormsModule, + ReactiveFormsModule ], - providers:[ - { provide: MatDialog, useClass: MockDialog }, - ] + providers: [ + { provide: MatDialog, useClass: MockDialog }, + { provide: NavigationService, useValue: navSvc }, + ServerService + ] }) .compileComponents(); fixture = TestBed.createComponent(StatusDetailsComponent); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index cca67d8498..34b70ffc88 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -12,23 +12,25 @@ * limitations under the License. */ -import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { StatusesTableComponent } from "./statuses-table.component"; import { HttpClientModule } from "@angular/common/http"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; + import { APITestingModule } from "src/app/api/testing"; +import { StatusesTableComponent } from "./statuses-table.component"; + describe("StatusesTableComponent", () => { let component: StatusesTableComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ - declarations: [ StatusesTableComponent ], - imports:[ + declarations: [StatusesTableComponent], + imports: [ HttpClientModule, RouterTestingModule.withRoutes([ - {component: StatusesTableComponent, path: ""}, + { component: StatusesTableComponent, path: "" }, ]), APITestingModule ] From 7a4698fa03dbe8f3de258e1bc49e6bab0a479e6c Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Fri, 7 Apr 2023 15:03:40 +0530 Subject: [PATCH 28/47] latest changes for comments --- experimental/traffic-portal/.eslintrc.json | 6 -- .../status-details.component.html | 4 +- .../status-details.component.spec.ts | 6 +- .../status-details.component.ts | 26 ++++++-- .../statuses-table.component.html | 18 +++-- .../statuses-table.component.spec.ts | 63 +++++++++++++++++- .../statuses-table.component.ts | 65 +++++++++++++++---- 7 files changed, 152 insertions(+), 36 deletions(-) diff --git a/experimental/traffic-portal/.eslintrc.json b/experimental/traffic-portal/.eslintrc.json index fa9e13fbd8..48cef4fbe7 100644 --- a/experimental/traffic-portal/.eslintrc.json +++ b/experimental/traffic-portal/.eslintrc.json @@ -237,12 +237,6 @@ "enableFixer": false } ], - "@typescript-eslint/unbound-method": [ - "error", - { - "ignoreStatic": true - } - ], "@typescript-eslint/restrict-plus-operands": "error", "@typescript-eslint/triple-slash-reference": "error", "arrow-parens": [ diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 7846bf31fc..be13e7feb4 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -13,7 +13,7 @@ --> -
+ Name @@ -27,7 +27,7 @@ - diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 6ebe7ca780..5830730f46 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -23,6 +23,8 @@ import { ServerService } from "src/app/api"; import { NavigationService } from "src/app/shared/navigation/navigation.service"; import { StatusDetailsComponent } from "./status-details.component"; +import { APITestingModule } from "src/app/api/testing"; +import { SharedModule } from "src/app/shared/shared.module"; /** * Define the MockDialog @@ -55,7 +57,9 @@ describe("StatusDetailsComponent", () => { HttpClientModule, RouterTestingModule, FormsModule, - ReactiveFormsModule + ReactiveFormsModule, + APITestingModule, + SharedModule ], providers: [ { provide: MatDialog, useClass: MockDialog }, diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 68cb37944e..df80d6b779 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -65,12 +65,17 @@ export class StatusDetailsComponent implements OnInit { ) { // Getting id from the route this.id = this.route.snapshot.paramMap.get("id"); - } - /** Initializes table data, loading it from Traffic Ops. */ - public ngOnInit(): void { + // Show error if route param is null + if (this.id === null) { + console.error("missing required route parameter 'id'"); + return; + } - // we check whether params is a number if not we shall assume user wants to add a new status. + /** + * Initializes table data, loading it from Traffic Ops. + * we check whether params is a number if not we shall assume user wants to add a new status. + */ if (this.id !== "new") { this.loading = true; this.statusDetailsForm.addControl("id", new FormControl("")); @@ -81,6 +86,10 @@ export class StatusDetailsComponent implements OnInit { } } + + public ngOnInit(): void { + } + /** * Reloads the servers table data. * @@ -100,8 +109,15 @@ export class StatusDetailsComponent implements OnInit { /** * On submitting the form we check for whether we are performing Create or Edit + * @param event The DOM form submission event. */ - public onSubmit(): void { + public onSubmit(event: Event): void { + event.preventDefault(); + event.stopPropagation(); + + if (this.statusDetailsForm.invalid) { + return; + } if (this.id === "new") { this.createStatus(); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html index 9ca9f8d8c9..2b89cbeb7a 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html @@ -12,11 +12,15 @@ limitations under the License. --> -
- -
- - +
+ +
+ +
diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index 34b70ffc88..207868538f 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -13,12 +13,17 @@ */ import { HttpClientModule } from "@angular/common/http"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ComponentFixture, TestBed, fakeAsync, tick } from "@angular/core/testing"; import { RouterTestingModule } from "@angular/router/testing"; import { APITestingModule } from "src/app/api/testing"; import { StatusesTableComponent } from "./statuses-table.component"; +import { MatDialog, MatDialogModule, MatDialogRef } from "@angular/material/dialog"; +import { of } from "rxjs"; + +import { isAction } from "src/app/shared/generic-table/generic-table.component"; +import { ServerService } from "src/app/api/server.service"; describe("StatusesTableComponent", () => { let component: StatusesTableComponent; @@ -28,11 +33,12 @@ describe("StatusesTableComponent", () => { await TestBed.configureTestingModule({ declarations: [StatusesTableComponent], imports: [ + APITestingModule, HttpClientModule, + MatDialogModule, RouterTestingModule.withRoutes([ { component: StatusesTableComponent, path: "" }, ]), - APITestingModule ] }) .compileComponents(); @@ -45,4 +51,57 @@ describe("StatusesTableComponent", () => { it("should create", () => { expect(component).toBeTruthy(); }); + + it("updates the fuzzy search output", fakeAsync(() => { + let called = false; + const text = "testquest"; + const spy = jasmine.createSpy("subscriber", (txt: string): void =>{ + if (!called) { + expect(txt).toBe(""); + called = true; + } else { + expect(txt).toBe(text); + } + }); + component.fuzzySubject.subscribe(spy); + tick(); + expect(spy).toHaveBeenCalled(); + component.fuzzControl.setValue(text); + component.updateURL(); + tick(); + expect(spy).toHaveBeenCalledTimes(2); + })); + + it("handles the 'delete' context menu item", fakeAsync(async () => { + const item = component.contextMenuItems.find(c => c.name === "Delete"); + if (!item) { + return fail("missing 'Delete' context menu item"); + } + if (!isAction(item)) { + return fail("expected an action, not a link"); + } + expect(item.multiRow).toBeFalsy(); + expect(item.disabled).toBeUndefined(); + + const api = TestBed.inject(ServerService); + const spy = spyOn(api, "deleteStatus").and.callThrough(); + expect(spy).not.toHaveBeenCalled(); + + const dialogService = TestBed.inject(MatDialog); + const openSpy = spyOn(dialogService, "open").and.returnValue({ + afterClosed: () => of(true) + } as MatDialogRef); + + const type = await api.createStatus({description: "blah", name: "test"}); + expect(openSpy).not.toHaveBeenCalled(); + const asyncExpectation = expectAsync(component.handleContextMenu({action: "delete", data: type})).toBeResolvedTo(undefined); + tick(); + + expect(openSpy).toHaveBeenCalled(); + tick(); + + expect(spy).toHaveBeenCalled(); + + await asyncExpectation; + })); }); diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 0f02debf6a..fa3c18c8c9 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -16,8 +16,12 @@ import { BehaviorSubject } from "rxjs"; import { ResponseStatus } from "trafficops-types"; import { ServerService } from "src/app/api"; -import { ContextMenuItem } from "src/app/shared/generic-table/generic-table.component"; +import { ContextMenuActionEvent, ContextMenuItem } from "src/app/shared/generic-table/generic-table.component"; import { NavigationService } from "src/app/shared/navigation/navigation.service"; +import { ActivatedRoute } from "@angular/router"; +import { FormControl } from "@angular/forms"; +import { MatDialog } from "@angular/material/dialog"; +import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; /** * StatusesTableComponent is the controller for the statuses page - which @@ -31,7 +35,7 @@ import { NavigationService } from "src/app/shared/navigation/navigation.service" export class StatusesTableComponent implements OnInit { /** All of the statues which should appear in the table. */ - public statuses: ResponseStatus[] = []; + public statuses: Promise>; /** Definitions of the table's columns according to the ag-grid API */ public columnDefs = [ @@ -58,34 +62,48 @@ export class StatusesTableComponent implements OnInit { { href: (): string => "new", name: "Create New Status" + }, + { + action: "delete", + multiRow: false, + name: "Delete" } ]; - /** Emits changes to the fuzzy search text. */ - public fuzzySubject = new BehaviorSubject(""); + /** A subject that child components can subscribe to for access to the fuzzy search query text */ + public fuzzySubject: BehaviorSubject; + + /** Form controller for the user search input. */ + public fuzzControl = new FormControl("", {nonNullable: true}); /** * Constructs the component with its required injections. * - * @param serverService The Servers API which is used to provide row data. + * @param api The Servers API which is used to provide row data. * @param navSvc Manages the header */ - constructor( - private readonly serverService: ServerService, + constructor( + private readonly dialog: MatDialog, + private readonly route: ActivatedRoute, + private readonly api: ServerService, private readonly navSvc: NavigationService, ) { this.fuzzySubject = new BehaviorSubject(""); + this.statuses = this.api.getStatuses(); this.navSvc.headerTitle.next("Statuses"); } /** Initializes table data, loading it from Traffic Ops. */ public ngOnInit(): void { - this.getStatuses(); - } - - /** Reloads the servers table data. */ - public async getStatuses(): Promise { - this.statuses = await this.serverService.getStatuses(); + this.route.queryParamMap.subscribe( + m => { + const search = m.get("search"); + if (search) { + this.fuzzControl.setValue(decodeURIComponent(search)); + this.updateURL(); + } + } + ); } /** @@ -95,4 +113,25 @@ export class StatusesTableComponent implements OnInit { public updateURL(): void { this.fuzzySubject.next(this.searchText); } + + /** + * Handles a context menu event. + * + * @param evt The action selected from the context menu. + */ + public async handleContextMenu(evt: ContextMenuActionEvent): Promise { + const data = evt.data as ResponseStatus; + switch(evt.action) { + case "delete": + const ref = this.dialog.open(DecisionDialogComponent, { + data: {message: `Are you sure you want to delete status ${data.name} with id ${data.id} ?`, title: "Confirm Delete"} + }); + ref.afterClosed().subscribe(result => { + if(result) { + this.api.deleteStatus(data.id).then(async () => this.statuses = this.api.getStatuses()); + } + }); + break; + } + } } From fb0b0b31f11990075c9b60c5051acebef79a7588 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Fri, 7 Apr 2023 16:55:58 +0530 Subject: [PATCH 29/47] latest changes --- .../status-details/status-details.component.ts | 12 ++++++------ .../statuses-table/statuses-table.component.spec.ts | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index df80d6b779..d9faeab365 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -49,7 +49,7 @@ export class StatusDetailsComponent implements OnInit { /** * Constructor. * - * @param serverService The Servers API which is used to provide row data. + * @param api The Servers API which is used to provide row data. * @param route A reference to the route of this view which is used to get the 'id' query parameter of status. * @param router Angular router * @param dialog Dialog manager @@ -57,7 +57,7 @@ export class StatusDetailsComponent implements OnInit { * @param navSvc Manages the header */ constructor( - private readonly serverService: ServerService, + private readonly api: ServerService, private readonly route: ActivatedRoute, private readonly router: Router, private readonly dialog: MatDialog, @@ -97,7 +97,7 @@ export class StatusDetailsComponent implements OnInit { */ public async getStatusDetails(): Promise { const id = Number(this.id); // id Type 'null' is not assignable to type 'string' - this.statusDetails = await this.serverService.getStatuses(id); + this.statusDetails = await this.api.getStatuses(id); // Set page title with status Name this.navSvc.headerTitle.next(`Status #${this.statusDetails.name}`); @@ -130,7 +130,7 @@ export class StatusDetailsComponent implements OnInit { * For Creating a new status */ public createStatus(): void { - this.serverService.createStatus(this.statusDetailsForm.value).then((res: ResponseStatus) => { + this.api.createStatus(this.statusDetailsForm.value).then((res: ResponseStatus) => { if (res) { this.statusDetails = res; this.router.navigate(["/core/statuses"]); @@ -142,7 +142,7 @@ export class StatusDetailsComponent implements OnInit { * For updating the Status */ public updateStatus(): void { - this.serverService.updateStatusDetail(this.statusDetailsForm.value, Number(this.id)); + this.api.updateStatusDetail(this.statusDetailsForm.value, Number(this.id)); } /** @@ -159,7 +159,7 @@ export class StatusDetailsComponent implements OnInit { ref.afterClosed().subscribe(result => { if (result) { const id = Number(this.id); - this.serverService.deleteStatus(id).then(() => { + this.api.deleteStatus(id).then(() => { this.router.navigate(["/core/statuses"]); }); } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index 207868538f..8bd40ca399 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -92,9 +92,9 @@ describe("StatusesTableComponent", () => { afterClosed: () => of(true) } as MatDialogRef); - const type = await api.createStatus({description: "blah", name: "test"}); + const status = await api.createStatus({description: "blah", name: "test"}); expect(openSpy).not.toHaveBeenCalled(); - const asyncExpectation = expectAsync(component.handleContextMenu({action: "delete", data: type})).toBeResolvedTo(undefined); + const asyncExpectation = expectAsync(component.handleContextMenu({action: "delete", data: status})).toBeResolvedTo(undefined); tick(); expect(openSpy).toHaveBeenCalled(); From 4669bc0456ec18e54aba7b77e1ff1c7b40d83854 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Fri, 7 Apr 2023 17:08:40 +0530 Subject: [PATCH 30/47] added button for new status --- .../statuses/statuses-table/statuses-table.component.html | 2 ++ .../statuses/statuses-table/statuses-table.component.ts | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html index 2b89cbeb7a..bfb8a2636a 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html @@ -24,3 +24,5 @@ (contextMenuAction)="handleContextMenu($event)"> + +add diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index fa3c18c8c9..776f3ce4f4 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -22,6 +22,7 @@ import { ActivatedRoute } from "@angular/router"; import { FormControl } from "@angular/forms"; import { MatDialog } from "@angular/material/dialog"; import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; +import { CurrentUserService } from "src/app/shared/current-user/current-user.service"; /** * StatusesTableComponent is the controller for the statuses page - which @@ -60,8 +61,8 @@ export class StatusesTableComponent implements OnInit { name: "View Status Details" }, { - href: (): string => "new", - name: "Create New Status" + href: (u: ResponseStatus): string => `${u.id}`, + name: "Edit" }, { action: "delete", @@ -86,7 +87,7 @@ export class StatusesTableComponent implements OnInit { private readonly dialog: MatDialog, private readonly route: ActivatedRoute, private readonly api: ServerService, - private readonly navSvc: NavigationService, + private readonly navSvc: NavigationService, public readonly auth: CurrentUserService ) { this.fuzzySubject = new BehaviorSubject(""); this.statuses = this.api.getStatuses(); From b1f8f01f14947b80b18307f9ca839e83a37f2641 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Fri, 7 Apr 2023 18:09:11 +0530 Subject: [PATCH 31/47] latest changes applied to review comments --- .../src/app/api/server.service.ts | 20 ++--- .../src/app/api/testing/server.service.ts | 15 +++- .../src/app/core/core.module.ts | 2 - .../status-details.component.spec.ts | 73 +++++++-------- .../status-details.component.ts | 89 ++++++------------- .../statuses-table.component.spec.ts | 9 +- .../statuses-table.component.ts | 40 ++++----- 7 files changed, 114 insertions(+), 134 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/server.service.ts b/experimental/traffic-portal/src/app/api/server.service.ts index 235fa6390a..71b834b621 100644 --- a/experimental/traffic-portal/src/app/api/server.service.ts +++ b/experimental/traffic-portal/src/app/api/server.service.ts @@ -191,21 +191,21 @@ export class ServerService extends APIService { /** * Creating new Status. * - * @param payload containes name and description for the status. - * @returns The 'response' property of the TO status response. See TO API docs. + * @param status The status to create. + * @returns The created status. */ - public async createStatus(payload: RequestStatus): Promise { - return this.post("statuses", payload).toPromise(); + public async createStatus(status: RequestStatus): Promise { + return this.post("statuses", status).toPromise(); } /** * Updates status Details. * - * @param payload containes name and description for the status., unique identifier thereof. - * @param id The Status ID + * @param status The status to update. + * @returns The updated status. */ - public async updateStatusDetail(payload: ResponseStatus, id: number): Promise { - return this.put(`statuses/${id}`, payload).toPromise(); + public async updateStatusDetail(status: ResponseStatus): Promise { + return this.put(`statuses/${status.id}`, status).toPromise(); } /** @@ -213,7 +213,7 @@ export class ServerService extends APIService { * * @param id The Status ID */ - public async deleteStatus(id: number): Promise { - return this.delete(`statuses/${id}`).toPromise(); + public async deleteStatus(id: number | ResponseStatus): Promise { + return this.delete(`statuses/${id}`).toPromise(); } } diff --git a/experimental/traffic-portal/src/app/api/testing/server.service.ts b/experimental/traffic-portal/src/app/api/testing/server.service.ts index 94d26c975d..30c0fbdc7d 100644 --- a/experimental/traffic-portal/src/app/api/testing/server.service.ts +++ b/experimental/traffic-portal/src/app/api/testing/server.service.ts @@ -49,6 +49,12 @@ export class ServerService { public servers = new Array(); private statuses = [ + { + description: "Sever is administrative down and does not receive traffic.", + id: 0, + lastUpdated: new Date(), + name: "ADMIN_DOWNS" + }, { description: "Sever is administrative down and does not receive traffic.", id: 4, @@ -216,7 +222,14 @@ export class ServerService { } return status; } - return this.statuses; + return this.statuses.map( + p => ({ + description: p.description, + id: p.id, + lastUpdated: p.lastUpdated, + name: p.name + }) + ); } /** diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index 053b91e754..e3f18ab1cd 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -136,8 +136,6 @@ export const ROUTES: Routes = [ TypeDetailComponent, CoordinatesTableComponent, CoordinateDetailComponent, - TypesTableComponent, - TypeDetailComponent, ISOGenerationFormComponent, CDNDetailComponent, ], diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index 5830730f46..ae075ea612 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -12,62 +12,41 @@ * limitations under the License. */ -import { HttpClientModule } from "@angular/common/http"; import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { FormsModule, ReactiveFormsModule } from "@angular/forms"; -import { MatDialog } from "@angular/material/dialog"; +import { MatDialogModule } from "@angular/material/dialog"; +import { ActivatedRoute } from "@angular/router"; import { RouterTestingModule } from "@angular/router/testing"; -import { Observable, ReplaySubject, of } from "rxjs"; +import { ReplaySubject } from "rxjs"; -import { ServerService } from "src/app/api"; +import { APITestingModule } from "src/app/api/testing"; import { NavigationService } from "src/app/shared/navigation/navigation.service"; import { StatusDetailsComponent } from "./status-details.component"; -import { APITestingModule } from "src/app/api/testing"; -import { SharedModule } from "src/app/shared/shared.module"; - -/** - * Define the MockDialog - */ -class MockDialog { - - /** - * Fake opens the dialog - * - * @returns unknown - */ - public open(): unknown { - return { - afterClosed: (): Observable => of(true) - }; - } -} describe("StatusDetailsComponent", () => { let component: StatusDetailsComponent; let fixture: ComponentFixture; + let route: ActivatedRoute; + let paramMap: jasmine.Spy; + const navSvc = jasmine.createSpyObj([], { headerHidden: new ReplaySubject(), headerTitle: new ReplaySubject() }); beforeEach(async () => { - - const navSvc = jasmine.createSpyObj([], { headerHidden: new ReplaySubject(), headerTitle: new ReplaySubject() }); - await TestBed.configureTestingModule({ declarations: [StatusDetailsComponent], imports: [ - HttpClientModule, - RouterTestingModule, - FormsModule, - ReactiveFormsModule, APITestingModule, - SharedModule + RouterTestingModule, + MatDialogModule ], providers: [ - { provide: MatDialog, useClass: MockDialog }, - { provide: NavigationService, useValue: navSvc }, - ServerService + { provide: NavigationService, useValue: navSvc } ] }) .compileComponents(); + + route = TestBed.inject(ActivatedRoute); + paramMap = spyOn(route.snapshot.paramMap, "get"); + paramMap.and.returnValue(null); fixture = TestBed.createComponent(StatusDetailsComponent); component = fixture.componentInstance; fixture.detectChanges(); @@ -77,4 +56,28 @@ describe("StatusDetailsComponent", () => { expect(component).toBeTruthy(); }); + it("new status", async () => { + paramMap.and.returnValue("new"); + + fixture = TestBed.createComponent(StatusDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + await fixture.whenStable(); + expect(paramMap).toHaveBeenCalled(); + expect(component.statusDetails).not.toBeNull(); + expect(component.new).toBeTrue(); + }); + + it("existing status", async () => { + paramMap.and.returnValue("1"); + + fixture = TestBed.createComponent(StatusDetailsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + await fixture.whenStable(); + expect(paramMap).toHaveBeenCalled(); + expect(component.statusDetails).not.toBeNull(); + expect(component.statusDetails.name).toBe("OFFLINE"); + expect(component.new).toBeFalse(); + }); }); diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index d9faeab365..1a96a07d77 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -11,10 +11,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Component, OnInit } from "@angular/core"; + +import { Location } from "@angular/common"; +import { Component } from "@angular/core"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { MatDialog } from "@angular/material/dialog"; -import { ActivatedRoute, Router } from "@angular/router"; +import { ActivatedRoute } from "@angular/router"; import { ResponseStatus } from "trafficops-types"; import { ServerService } from "src/app/api"; @@ -29,10 +31,8 @@ import { NavigationService } from "src/app/shared/navigation/navigation.service" styleUrls: ["./status-details.component.scss"], templateUrl: "./status-details.component.html", }) -export class StatusDetailsComponent implements OnInit { - - /** Status ID expected from the route param using which we identify whether we are creating new status or load existing status */ - public id: string | number | null = null; +export class StatusDetailsComponent { + public new = false; /** Loader status for the actions */ public loading = false; @@ -59,45 +59,33 @@ export class StatusDetailsComponent implements OnInit { constructor( private readonly api: ServerService, private readonly route: ActivatedRoute, - private readonly router: Router, private readonly dialog: MatDialog, - private readonly navSvc: NavigationService, + private readonly navSvc: NavigationService, private readonly location: Location, ) { // Getting id from the route - this.id = this.route.snapshot.paramMap.get("id"); - - // Show error if route param is null - if (this.id === null) { - console.error("missing required route parameter 'id'"); - return; - } + const id = this.route.snapshot.paramMap.get("id"); - /** + /** * Initializes table data, loading it from Traffic Ops. * we check whether params is a number if not we shall assume user wants to add a new status. */ - if (this.id !== "new") { + if (id !== "new") { this.loading = true; this.statusDetailsForm.addControl("id", new FormControl("")); this.statusDetailsForm.addControl("lastUpdated", new FormControl("")); this.getStatusDetails(); } else { this.navSvc.headerTitle.next("New Status"); + this.new = true; } } - - public ngOnInit(): void { - } - /** - * Reloads the servers table data. - * - * @param id is the id passed in route for this page if this is a edit view. + * Get status details for the id + * patch the form with status details */ public async getStatusDetails(): Promise { - const id = Number(this.id); // id Type 'null' is not assignable to type 'string' - this.statusDetails = await this.api.getStatuses(id); + this.statusDetails = await this.api.getStatuses(this.statusDetails.id); // Set page title with status Name this.navSvc.headerTitle.next(`Status #${this.statusDetails.name}`); @@ -109,40 +97,21 @@ export class StatusDetailsComponent implements OnInit { /** * On submitting the form we check for whether we are performing Create or Edit - * @param event The DOM form submission event. + * + * @param event HTML form submission event. */ - public onSubmit(event: Event): void { + public async onSubmit(event: Event): Promise { event.preventDefault(); event.stopPropagation(); - if (this.statusDetailsForm.invalid) { - return; - } - if (this.id === "new") { - this.createStatus(); - - } else { - this.updateStatus(); - } - } - - /** - * For Creating a new status - */ - public createStatus(): void { - this.api.createStatus(this.statusDetailsForm.value).then((res: ResponseStatus) => { - if (res) { - this.statusDetails = res; - this.router.navigate(["/core/statuses"]); + if (this.statusDetailsForm.valid) { + if (this.new) { + this.statusDetails = await this.api.createStatus(this.statusDetailsForm.value); + this.location.back(); + } else { + this.statusDetails = await this.api.updateStatusDetail(this.statusDetailsForm.value); } - }); - } - - /** - * For updating the Status - */ - public updateStatus(): void { - this.api.updateStatusDetail(this.statusDetailsForm.value, Number(this.id)); + } } /** @@ -151,17 +120,15 @@ export class StatusDetailsComponent implements OnInit { public async deleteStatus(): Promise { const ref = this.dialog.open(DecisionDialogComponent, { data: { - message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails?.name}'.`, - title: `Delete Status: ${this.statusDetails?.name}` + message: `This action CANNOT be undone. This will permanently delete '${this.statusDetails.name}'.`, + title: `Delete Status: ${this.statusDetails.name}` } }); ref.afterClosed().subscribe(result => { if (result) { - const id = Number(this.id); - this.api.deleteStatus(id).then(() => { - this.router.navigate(["/core/statuses"]); - }); + this.api.deleteStatus(this.statusDetails.id); + this.location.back(); } }); } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts index 8bd40ca399..b0e3a50fba 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.spec.ts @@ -14,16 +14,15 @@ import { HttpClientModule } from "@angular/common/http"; import { ComponentFixture, TestBed, fakeAsync, tick } from "@angular/core/testing"; +import { MatDialog, MatDialogModule, MatDialogRef } from "@angular/material/dialog"; import { RouterTestingModule } from "@angular/router/testing"; +import { of } from "rxjs"; +import { ServerService } from "src/app/api/server.service"; import { APITestingModule } from "src/app/api/testing"; +import { isAction } from "src/app/shared/generic-table/generic-table.component"; import { StatusesTableComponent } from "./statuses-table.component"; -import { MatDialog, MatDialogModule, MatDialogRef } from "@angular/material/dialog"; -import { of } from "rxjs"; - -import { isAction } from "src/app/shared/generic-table/generic-table.component"; -import { ServerService } from "src/app/api/server.service"; describe("StatusesTableComponent", () => { let component: StatusesTableComponent; diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 776f3ce4f4..ef7435f4fc 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -12,17 +12,17 @@ * limitations under the License. */ import { Component, OnInit } from "@angular/core"; +import { FormControl } from "@angular/forms"; +import { MatDialog } from "@angular/material/dialog"; +import { ActivatedRoute } from "@angular/router"; import { BehaviorSubject } from "rxjs"; import { ResponseStatus } from "trafficops-types"; import { ServerService } from "src/app/api"; +import { CurrentUserService } from "src/app/shared/current-user/current-user.service"; +import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; import { ContextMenuActionEvent, ContextMenuItem } from "src/app/shared/generic-table/generic-table.component"; import { NavigationService } from "src/app/shared/navigation/navigation.service"; -import { ActivatedRoute } from "@angular/router"; -import { FormControl } from "@angular/forms"; -import { MatDialog } from "@angular/material/dialog"; -import { DecisionDialogComponent } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; -import { CurrentUserService } from "src/app/shared/current-user/current-user.service"; /** * StatusesTableComponent is the controller for the statuses page - which @@ -83,7 +83,7 @@ export class StatusesTableComponent implements OnInit { * @param api The Servers API which is used to provide row data. * @param navSvc Manages the header */ - constructor( + constructor( private readonly dialog: MatDialog, private readonly route: ActivatedRoute, private readonly api: ServerService, @@ -120,19 +120,19 @@ export class StatusesTableComponent implements OnInit { * * @param evt The action selected from the context menu. */ - public async handleContextMenu(evt: ContextMenuActionEvent): Promise { - const data = evt.data as ResponseStatus; - switch(evt.action) { - case "delete": - const ref = this.dialog.open(DecisionDialogComponent, { - data: {message: `Are you sure you want to delete status ${data.name} with id ${data.id} ?`, title: "Confirm Delete"} - }); - ref.afterClosed().subscribe(result => { - if(result) { - this.api.deleteStatus(data.id).then(async () => this.statuses = this.api.getStatuses()); - } - }); - break; - } + public async handleContextMenu(evt: ContextMenuActionEvent): Promise { + const data = evt.data as ResponseStatus; + switch(evt.action) { + case "delete": + const ref = this.dialog.open(DecisionDialogComponent, { + data: {message: `Are you sure you want to delete status ${data.name} with id ${data.id} ?`, title: "Confirm Delete"} + }); + ref.afterClosed().subscribe(result => { + if(result) { + this.api.deleteStatus(data.id).then(async () => this.statuses = this.api.getStatuses()); + } + }); + break; } + } } From 91cc331c57905eb9f39f642e021213528bf36d7e Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 11 Apr 2023 07:22:01 +0530 Subject: [PATCH 32/47] latest changes --- experimental/traffic-portal/.eslintrc.json | 1 + .../traffic-portal/src/app/api/server.service.ts | 2 +- .../src/app/api/testing/server.service.ts | 16 ++++------------ .../statuses-table/statuses-table.component.html | 2 +- .../statuses-table/statuses-table.component.ts | 12 +++++++++++- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/experimental/traffic-portal/.eslintrc.json b/experimental/traffic-portal/.eslintrc.json index 48cef4fbe7..4311f5802f 100644 --- a/experimental/traffic-portal/.eslintrc.json +++ b/experimental/traffic-portal/.eslintrc.json @@ -237,6 +237,7 @@ "enableFixer": false } ], + "@typescript-eslint/unbound-method": "error", "@typescript-eslint/restrict-plus-operands": "error", "@typescript-eslint/triple-slash-reference": "error", "arrow-parens": [ diff --git a/experimental/traffic-portal/src/app/api/server.service.ts b/experimental/traffic-portal/src/app/api/server.service.ts index 71b834b621..a63050aa4d 100644 --- a/experimental/traffic-portal/src/app/api/server.service.ts +++ b/experimental/traffic-portal/src/app/api/server.service.ts @@ -213,7 +213,7 @@ export class ServerService extends APIService { * * @param id The Status ID */ - public async deleteStatus(id: number | ResponseStatus): Promise { + public async deleteStatus(id: number): Promise { return this.delete(`statuses/${id}`).toPromise(); } } diff --git a/experimental/traffic-portal/src/app/api/testing/server.service.ts b/experimental/traffic-portal/src/app/api/testing/server.service.ts index 30c0fbdc7d..ae32c2a0c4 100644 --- a/experimental/traffic-portal/src/app/api/testing/server.service.ts +++ b/experimental/traffic-portal/src/app/api/testing/server.service.ts @@ -222,14 +222,7 @@ export class ServerService { } return status; } - return this.statuses.map( - p => ({ - description: p.description, - id: p.id, - lastUpdated: p.lastUpdated, - name: p.name - }) - ); + return this.statuses } /** @@ -358,14 +351,13 @@ export class ServerService { * Deletes a Status. * * @param id The ID of the Status to delete. - * @returns The response. + * @returns The deleted status. */ - public async deleteStatus(id: number): Promise | null> { + public async deleteStatus(id: number): Promise { const idx = this.statuses.findIndex(j => j.id === id); if (idx < 0) { throw new Error(`no such status: #${id}`); } - this.statuses.splice(idx, 1); - return new HttpResponse({ body: { alerts: [{ level: "success", text: "Successfully logged in." }] }, status: 200 }); + return this.statuses.splice(idx, 1)[0]; } } diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html index bfb8a2636a..0dabb5c1ee 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.html @@ -13,7 +13,7 @@ -->
- +
{ if(result) { From 152b395487fd8ca58635946a91864a24f5fe781b Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 12 Apr 2023 05:55:50 +0530 Subject: [PATCH 33/47] lint fixes --- .../traffic-portal/src/app/api/testing/server.service.ts | 3 +-- .../statuses/status-details/status-details.component.html | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/testing/server.service.ts b/experimental/traffic-portal/src/app/api/testing/server.service.ts index ae32c2a0c4..abffdf9812 100644 --- a/experimental/traffic-portal/src/app/api/testing/server.service.ts +++ b/experimental/traffic-portal/src/app/api/testing/server.service.ts @@ -12,7 +12,6 @@ * limitations under the License. */ -import { HttpResponse } from "@angular/common/http"; import { Injectable } from "@angular/core"; import type { RequestServer, RequestStatus, ResponseServer, ResponseStatus, Servercheck } from "trafficops-types"; @@ -222,7 +221,7 @@ export class ServerService { } return status; } - return this.statuses + return this.statuses; } /** diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index be13e7feb4..672f0a63d6 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -25,10 +25,10 @@
- +
From f3613f1e82857a87a5459c5aa466d86d09281ab0 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 12 Apr 2023 06:41:32 +0530 Subject: [PATCH 34/47] reactive form corrections lint error --- .../status-details.component.html | 1 + .../status-details.component.ts | 42 ++++++++++++------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 672f0a63d6..8dec1bad1d 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -18,6 +18,7 @@ Name + Name is required Description diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 1a96a07d77..967bbd8d6e 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -17,7 +17,7 @@ import { Component } from "@angular/core"; import { FormControl, FormGroup, Validators } from "@angular/forms"; import { MatDialog } from "@angular/material/dialog"; import { ActivatedRoute } from "@angular/router"; -import { ResponseStatus } from "trafficops-types"; +import { RequestStatus, ResponseStatus } from "trafficops-types"; import { ServerService } from "src/app/api"; import { DecisionDialogComponent, DecisionDialogData } from "src/app/shared/dialogs/decision-dialog/decision-dialog.component"; @@ -35,15 +35,15 @@ export class StatusDetailsComponent { public new = false; /** Loader status for the actions */ - public loading = false; + public loading = true; /** All details of status requested */ public statusDetails!: ResponseStatus; /** Reactive form intialized to creat / edit status details */ - public statusDetailsForm: FormGroup = new FormGroup({ - description: new FormControl("", Validators.required), - name: new FormControl("", Validators.required), + public statusDetailsForm = new FormGroup({ + description: new FormControl("", {nonNullable: true}), + name: new FormControl("", {nonNullable: true, validators: Validators.required}), }); /** @@ -69,14 +69,12 @@ export class StatusDetailsComponent { * Initializes table data, loading it from Traffic Ops. * we check whether params is a number if not we shall assume user wants to add a new status. */ - if (id !== "new") { - this.loading = true; - this.statusDetailsForm.addControl("id", new FormControl("")); - this.statusDetailsForm.addControl("lastUpdated", new FormControl("")); - this.getStatusDetails(); + if (id && id !== "new") { + this.getStatusDetails(id); } else { this.navSvc.headerTitle.next("New Status"); this.new = true; + this.loading = false; } } @@ -84,14 +82,18 @@ export class StatusDetailsComponent { * Get status details for the id * patch the form with status details */ - public async getStatusDetails(): Promise { - this.statusDetails = await this.api.getStatuses(this.statusDetails.id); + public async getStatusDetails(id:string | number): Promise { + this.statusDetails = await this.api.getStatuses(Number(id)); // Set page title with status Name this.navSvc.headerTitle.next(`Status #${this.statusDetails.name}`); // Patch the form with existing data we got from service requested above. - this.statusDetailsForm.patchValue(this.statusDetails); + this.statusDetailsForm.setValue({ + description: this.statusDetails.description ? this.statusDetails.description : "", + name: this.statusDetails.name + }); + this.loading = false; } @@ -106,10 +108,20 @@ export class StatusDetailsComponent { if (this.statusDetailsForm.valid) { if (this.new) { - this.statusDetails = await this.api.createStatus(this.statusDetailsForm.value); + const newData: RequestStatus = { + description: this.statusDetailsForm.controls.description.value, + name: this.statusDetailsForm.controls.name.value + }; + this.statusDetails = await this.api.createStatus(newData); this.location.back(); } else { - this.statusDetails = await this.api.updateStatusDetail(this.statusDetailsForm.value); + const editData: ResponseStatus = { + description: this.statusDetailsForm.controls.description.value, + id: this.statusDetails.id, + lastUpdated:this.statusDetails.lastUpdated, + name:this.statusDetailsForm.controls.name.value + }; + this.statusDetails = await this.api.updateStatusDetail(editData); } } } From 64fa23bf9783a67e2c430c1bf84b314853ab1440 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 12 Apr 2023 07:29:46 +0530 Subject: [PATCH 35/47] latest changes --- .../statuses/status-details/status-details.component.html | 1 - .../statuses/status-details/status-details.component.ts | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 8dec1bad1d..672f0a63d6 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -18,7 +18,6 @@ Name - Name is required Description diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index 967bbd8d6e..b01a2b3ec0 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -14,7 +14,7 @@ import { Location } from "@angular/common"; import { Component } from "@angular/core"; -import { FormControl, FormGroup, Validators } from "@angular/forms"; +import { FormControl, FormGroup } from "@angular/forms"; import { MatDialog } from "@angular/material/dialog"; import { ActivatedRoute } from "@angular/router"; import { RequestStatus, ResponseStatus } from "trafficops-types"; @@ -43,7 +43,7 @@ export class StatusDetailsComponent { /** Reactive form intialized to creat / edit status details */ public statusDetailsForm = new FormGroup({ description: new FormControl("", {nonNullable: true}), - name: new FormControl("", {nonNullable: true, validators: Validators.required}), + name: new FormControl("", {nonNullable: true}), }); /** @@ -81,8 +81,10 @@ export class StatusDetailsComponent { /** * Get status details for the id * patch the form with status details + * + * @param id ID of the status */ - public async getStatusDetails(id:string | number): Promise { + public async getStatusDetails(id: string | number): Promise { this.statusDetails = await this.api.getStatuses(Number(id)); // Set page title with status Name From c5f623b22865c17f0e0e37a60386f42ac322af62 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:59:57 +0530 Subject: [PATCH 36/47] merge comments fixes --- .../src/app/api/testing/server.service.ts | 17 ++++++----------- .../traffic-portal/src/app/core/core.module.ts | 8 ++++---- .../status-details.component.spec.ts | 10 +++++++--- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/testing/server.service.ts b/experimental/traffic-portal/src/app/api/testing/server.service.ts index abffdf9812..021252d553 100644 --- a/experimental/traffic-portal/src/app/api/testing/server.service.ts +++ b/experimental/traffic-portal/src/app/api/testing/server.service.ts @@ -47,13 +47,7 @@ export class ServerService { public servers = new Array(); - private statuses = [ - { - description: "Sever is administrative down and does not receive traffic.", - id: 0, - lastUpdated: new Date(), - name: "ADMIN_DOWNS" - }, + private readonly statuses: ResponseStatus[] = [ { description: "Sever is administrative down and does not receive traffic.", id: 4, @@ -313,15 +307,16 @@ export class ServerService { /** * Creates a status. * - * @param status The status details (name & description) to create. + * @param status The status details (name & description) to create. Description is an optional property in status. * @returns The status as created and returned by the API. */ public async createStatus(status: RequestStatus): Promise { const newStatus = { - ...status, + description: status.description ? status.description : null, id: ++this.statusIdCounter, - lastUpdated: new Date() - } as { description: string; id: number; lastUpdated: Date; name: string }; + lastUpdated: new Date(), + name: status.name + }; this.statuses.push(newStatus); return newStatus; } diff --git a/experimental/traffic-portal/src/app/core/core.module.ts b/experimental/traffic-portal/src/app/core/core.module.ts index e3f18ab1cd..6119b73fe1 100644 --- a/experimental/traffic-portal/src/app/core/core.module.ts +++ b/experimental/traffic-portal/src/app/core/core.module.ts @@ -87,12 +87,12 @@ export const ROUTES: Routes = [ { component: TenantDetailsComponent, path: "tenants/:id"}, { component: PhysLocDetailComponent, path: "phys-locs/:id" }, { component: PhysLocTableComponent, path: "phys-locs" }, - { component: StatusesTableComponent, path: "statuses" }, - { component: StatusDetailsComponent, path: "statuses/:id" }, { component: CoordinateDetailComponent, path: "coordinates/:id" }, { component: CoordinatesTableComponent, path: "coordinates" }, { component: TypesTableComponent, path: "types" }, { component: TypeDetailComponent, path: "types/:id"}, + { component: StatusesTableComponent, path: "statuses" }, + { component: StatusDetailsComponent, path: "statuses/:id" }, { component: ISOGenerationFormComponent, path: "iso-gen"}, ].map(r => ({...r, canActivate: [AuthenticatedGuard]})); @@ -130,12 +130,12 @@ export const ROUTES: Routes = [ RegionsTableComponent, RegionDetailComponent, CacheGroupDetailsComponent, - StatusesTableComponent, - StatusDetailsComponent, TypesTableComponent, TypeDetailComponent, CoordinatesTableComponent, CoordinateDetailComponent, + StatusesTableComponent, + StatusDetailsComponent, ISOGenerationFormComponent, CDNDetailComponent, ], diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts index ae075ea612..ce7be0b2f3 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.spec.ts @@ -18,6 +18,7 @@ import { ActivatedRoute } from "@angular/router"; import { RouterTestingModule } from "@angular/router/testing"; import { ReplaySubject } from "rxjs"; +import { ServerService } from "src/app/api"; import { APITestingModule } from "src/app/api/testing"; import { NavigationService } from "src/app/shared/navigation/navigation.service"; @@ -28,6 +29,7 @@ describe("StatusDetailsComponent", () => { let fixture: ComponentFixture; let route: ActivatedRoute; let paramMap: jasmine.Spy; + let service: ServerService; const navSvc = jasmine.createSpyObj([], { headerHidden: new ReplaySubject(), headerTitle: new ReplaySubject() }); beforeEach(async () => { @@ -46,6 +48,7 @@ describe("StatusDetailsComponent", () => { route = TestBed.inject(ActivatedRoute); paramMap = spyOn(route.snapshot.paramMap, "get"); + service = TestBed.inject(ServerService); paramMap.and.returnValue(null); fixture = TestBed.createComponent(StatusDetailsComponent); component = fixture.componentInstance; @@ -69,15 +72,16 @@ describe("StatusDetailsComponent", () => { }); it("existing status", async () => { - paramMap.and.returnValue("1"); - + const id = 1; + paramMap.and.returnValue(id); + const status = await service.getStatuses(id); fixture = TestBed.createComponent(StatusDetailsComponent); component = fixture.componentInstance; fixture.detectChanges(); await fixture.whenStable(); expect(paramMap).toHaveBeenCalled(); expect(component.statusDetails).not.toBeNull(); - expect(component.statusDetails.name).toBe("OFFLINE"); + expect(component.statusDetails.name).toBe(status.name); expect(component.new).toBeFalse(); }); }); From d2a684f9dcb736c31d1f38e6b8987e2542821162 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 18 Apr 2023 22:49:14 +0530 Subject: [PATCH 37/47] e2e test cases added --- .../nightwatch/globals/globals.ts | 17 +++++++ .../nightwatch/page_objects/common.ts | 1 + .../page_objects/statuses/statusDetail.ts | 42 +++++++++++++++++ .../page_objects/statuses/statusesTable.ts | 45 +++++++++++++++++++ .../nightwatch/tests/statuses/detail.spec.ts | 44 ++++++++++++++++++ .../nightwatch/tests/statuses/table.spec.ts | 26 +++++++++++ 6 files changed, 175 insertions(+) create mode 100644 experimental/traffic-portal/nightwatch/page_objects/statuses/statusDetail.ts create mode 100644 experimental/traffic-portal/nightwatch/page_objects/statuses/statusesTable.ts create mode 100644 experimental/traffic-portal/nightwatch/tests/statuses/detail.spec.ts create mode 100644 experimental/traffic-portal/nightwatch/tests/statuses/table.spec.ts diff --git a/experimental/traffic-portal/nightwatch/globals/globals.ts b/experimental/traffic-portal/nightwatch/globals/globals.ts index afcd947938..8a3554e9b2 100644 --- a/experimental/traffic-portal/nightwatch/globals/globals.ts +++ b/experimental/traffic-portal/nightwatch/globals/globals.ts @@ -36,6 +36,8 @@ import type { ProfilePageObject } from "nightwatch/page_objects/profiles/profile import type { PhysLocDetailPageObject } from "nightwatch/page_objects/servers/physLocDetail"; import type { PhysLocTablePageObject } from "nightwatch/page_objects/servers/physLocTable"; import type { ServersPageObject } from "nightwatch/page_objects/servers/servers"; +import type { StatusDetailPageObject } from "nightwatch/page_objects/statuses/statusDetail"; +import type { StatusesTablePageObject } from "nightwatch/page_objects/statuses/statusesTable"; import type { ChangeLogsPageObject } from "nightwatch/page_objects/users/changeLogs"; import type { TenantDetailPageObject } from "nightwatch/page_objects/users/tenantDetail"; import type { TenantsPageObject } from "nightwatch/page_objects/users/tenants"; @@ -66,6 +68,8 @@ import { ResponseCoordinate, RequestCoordinate, RequestType, + ResponseStatus, + RequestStatus, ResponseProfile, RequestProfile, ProfileType @@ -109,6 +113,8 @@ declare module "nightwatch" { physLocDetail: () => PhysLocDetailPageObject; physLocTable: () => PhysLocTablePageObject; servers: () => ServersPageObject; + statusesTable: () => StatusesTablePageObject; + statusDetail: () => StatusDetailPageObject; }; users: { changeLogs: () => ChangeLogsPageObject; @@ -151,6 +157,7 @@ export interface CreatedData { steeringDS: ResponseDeliveryService; tenant: ResponseTenant; type: TypeFromResponse; + statuses: ResponseStatus; profile: ResponseProfile; } @@ -390,6 +397,16 @@ const globals = { console.log(`Successfully created Type ${respType.name}`); data.type = respType; + const status: RequestStatus = { + description: "blah", + name: `status${globals.uniqueString}`, + }; + url = `${apiUrl}/profiles`; + resp = await client.post(url, JSON.stringify(status)); + const respStatus: ResponseProfile = resp.data.response; + console.log(`Successfully created Profile ${respStatus.name}`); + data.statuses = respStatus; + const profile: RequestProfile = { cdn: 1, description: "blah", diff --git a/experimental/traffic-portal/nightwatch/page_objects/common.ts b/experimental/traffic-portal/nightwatch/page_objects/common.ts index 091b647a65..01534b4b51 100644 --- a/experimental/traffic-portal/nightwatch/page_objects/common.ts +++ b/experimental/traffic-portal/nightwatch/page_objects/common.ts @@ -63,6 +63,7 @@ const commonPageObject = { regions: "[aria-label='Navigate to Regions']", servers: "[aria-label='Navigate to Servers']", serversContainer: "[aria-label='Toggle Servers']", + statuses: "[aria-label='Navigate to Statuses']", tenants: "[aria-label='Navigate to Tenants']", types: "[aria-label='Navigate to Types']", users: "[aria-label='Navigate to Users']", diff --git a/experimental/traffic-portal/nightwatch/page_objects/statuses/statusDetail.ts b/experimental/traffic-portal/nightwatch/page_objects/statuses/statusDetail.ts new file mode 100644 index 0000000000..133be79e6d --- /dev/null +++ b/experimental/traffic-portal/nightwatch/page_objects/statuses/statusDetail.ts @@ -0,0 +1,42 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EnhancedPageObject } from "nightwatch"; + +/** + * Defines the PageObject for Status Details. + */ +export type StatusDetailPageObject = EnhancedPageObject<{}, typeof statusDetailPageObject.elements>; + +const statusDetailPageObject = { + elements: { + description: { + selector: "input[name='description']" + }, + id: { + selector: "input[name='id']" + }, + lastUpdated: { + selector: "input[name='lastUpdated']" + }, + name: { + selector: "input[name='name']" + }, + saveBtn: { + selector: "button[type='submit']" + } + }, +}; + +export default statusDetailPageObject; diff --git a/experimental/traffic-portal/nightwatch/page_objects/statuses/statusesTable.ts b/experimental/traffic-portal/nightwatch/page_objects/statuses/statusesTable.ts new file mode 100644 index 0000000000..6072bd6bb3 --- /dev/null +++ b/experimental/traffic-portal/nightwatch/page_objects/statuses/statusesTable.ts @@ -0,0 +1,45 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EnhancedPageObject, EnhancedSectionInstance, NightwatchAPI } from "nightwatch"; + +import { TABLE_COMMANDS, TableSectionCommands } from "../../globals/tables"; + +/** + * Defines the Statuses table commands + */ +type StatusesTableCommands = TableSectionCommands; + +/** + * Defines the Page Object for the Statuses page. + */ +export type StatusesTablePageObject = EnhancedPageObject<{}, {}, EnhancedSectionInstance>; + +const statusesTablePageObject = { + api: {} as NightwatchAPI, + sections: { + statusesTable: { + commands: { + ...TABLE_COMMANDS + }, + elements: {}, + selector: "mat-card" + } + }, + url(): string { + return `${this.api.launchUrl}/core/statuses`; + } +}; + +export default statusesTablePageObject; diff --git a/experimental/traffic-portal/nightwatch/tests/statuses/detail.spec.ts b/experimental/traffic-portal/nightwatch/tests/statuses/detail.spec.ts new file mode 100644 index 0000000000..65203fbf84 --- /dev/null +++ b/experimental/traffic-portal/nightwatch/tests/statuses/detail.spec.ts @@ -0,0 +1,44 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +describe("Status Detail Spec", () => { + it("Test status", () => { + const page = browser.page.statuses.statusDetail(); + browser.url(`${page.api.launchUrl}/core/statuses/${browser.globals.testData.statuses.id}`, res => { + browser.assert.ok(res.status === 0); + page.waitForElementVisible("mat-card") + .assert.enabled("@name") + .assert.enabled("@description") + .assert.enabled("@saveBtn") + .assert.not.enabled("@id") + .assert.not.enabled("@lastUpdated") + .assert.valueEquals("@name", browser.globals.testData.statuses.name) + .assert.valueEquals("@id", String(browser.globals.testData.statuses.id)); + }); + }); + + it("New Status", () => { + const page = browser.page.statuses.statusDetail(); + browser.url(`${page.api.launchUrl}/core/statuses/new`, res => { + browser.assert.ok(res.status === 0); + page.waitForElementVisible("mat-card") + .assert.enabled("@name") + .assert.enabled("@description") + .assert.enabled("@saveBtn") + .assert.not.elementPresent("@id") + .assert.not.elementPresent("@lastUpdated") + .assert.valueEquals("@name", ""); + }); + }); +}); diff --git a/experimental/traffic-portal/nightwatch/tests/statuses/table.spec.ts b/experimental/traffic-portal/nightwatch/tests/statuses/table.spec.ts new file mode 100644 index 0000000000..a02d46be71 --- /dev/null +++ b/experimental/traffic-portal/nightwatch/tests/statuses/table.spec.ts @@ -0,0 +1,26 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +describe("Statuses Spec", () => { + it("Loads elements", async () => { + await browser.page.common() + .section.sidebar + .navigateToNode("statuses", ["serversContainer"]); + await browser.waitForElementPresent("input[name=fuzzControl]"); + await browser.elements("css selector", "div.ag-row", rows => { + browser.assert.ok(rows.status === 0); + browser.assert.ok((rows.value as []).length >= 2); + }); + }); +}); From a13384dd8cf0e615a32f847d89d6686ef688a4a4 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 19 Apr 2023 05:48:15 +0530 Subject: [PATCH 38/47] url corrected --- experimental/traffic-portal/nightwatch/globals/globals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/traffic-portal/nightwatch/globals/globals.ts b/experimental/traffic-portal/nightwatch/globals/globals.ts index 8a3554e9b2..b74de2a19b 100644 --- a/experimental/traffic-portal/nightwatch/globals/globals.ts +++ b/experimental/traffic-portal/nightwatch/globals/globals.ts @@ -401,7 +401,7 @@ const globals = { description: "blah", name: `status${globals.uniqueString}`, }; - url = `${apiUrl}/profiles`; + url = `${apiUrl}/statuses`; resp = await client.post(url, JSON.stringify(status)); const respStatus: ResponseProfile = resp.data.response; console.log(`Successfully created Profile ${respStatus.name}`); From b05fa6a455a096f192efc1976ce2319d738cddca Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 19 Apr 2023 06:04:26 +0530 Subject: [PATCH 39/47] added name to fix selector in dom --- .../statuses/status-details/status-details.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 672f0a63d6..1f1cf8ebf3 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -17,11 +17,11 @@ Name - + Description - + From 758314f06a53dbe38efa526d7a795b37be76706e Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 19 Apr 2023 09:32:58 +0530 Subject: [PATCH 40/47] e2e test error fix --- experimental/traffic-portal/nightwatch/globals/globals.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/traffic-portal/nightwatch/globals/globals.ts b/experimental/traffic-portal/nightwatch/globals/globals.ts index b74de2a19b..75eeea9ab9 100644 --- a/experimental/traffic-portal/nightwatch/globals/globals.ts +++ b/experimental/traffic-portal/nightwatch/globals/globals.ts @@ -403,7 +403,7 @@ const globals = { }; url = `${apiUrl}/statuses`; resp = await client.post(url, JSON.stringify(status)); - const respStatus: ResponseProfile = resp.data.response; + const respStatus: ResponseStatus = resp.data.response; console.log(`Successfully created Profile ${respStatus.name}`); data.statuses = respStatus; From 9156cb9a8645023780d2a367b3383849a78d6bc6 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 19 Apr 2023 10:05:04 +0530 Subject: [PATCH 41/47] added element id and lastdate to dom --- .../statuses/status-details/status-details.component.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 1f1cf8ebf3..53738d896f 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -15,6 +15,10 @@
+ + ID + + Name @@ -23,6 +27,10 @@ Description + + Last Updated + + From b3af5ecc42462f3c7be0e184395595429572b5f7 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 19 Apr 2023 12:22:28 +0530 Subject: [PATCH 42/47] error fix --- experimental/traffic-portal/nightwatch/globals/globals.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/experimental/traffic-portal/nightwatch/globals/globals.ts b/experimental/traffic-portal/nightwatch/globals/globals.ts index 75eeea9ab9..d8c3925b58 100644 --- a/experimental/traffic-portal/nightwatch/globals/globals.ts +++ b/experimental/traffic-portal/nightwatch/globals/globals.ts @@ -113,6 +113,8 @@ declare module "nightwatch" { physLocDetail: () => PhysLocDetailPageObject; physLocTable: () => PhysLocTablePageObject; servers: () => ServersPageObject; + }; + statuses: { statusesTable: () => StatusesTablePageObject; statusDetail: () => StatusDetailPageObject; }; From a4fd9fa8c7a33e32e40dc722c51d761b127b95e4 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Tue, 25 Apr 2023 06:16:08 +0530 Subject: [PATCH 43/47] comment addressed --- .../traffic-portal/src/app/api/server.service.ts | 2 +- .../status-details/status-details.component.html | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/server.service.ts b/experimental/traffic-portal/src/app/api/server.service.ts index a63050aa4d..71b834b621 100644 --- a/experimental/traffic-portal/src/app/api/server.service.ts +++ b/experimental/traffic-portal/src/app/api/server.service.ts @@ -213,7 +213,7 @@ export class ServerService extends APIService { * * @param id The Status ID */ - public async deleteStatus(id: number): Promise { + public async deleteStatus(id: number | ResponseStatus): Promise { return this.delete(`statuses/${id}`).toPromise(); } } diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 53738d896f..4cf4962ca4 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -11,14 +11,15 @@ See the License for the specific language governing permissions and limitations under the License. --> + - ID - - + ID + + Name @@ -28,9 +29,10 @@ - Last Updated - - + Last Updated + + From 0b59682647b3f5551c85422790a0fa2b76fb48a5 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 26 Apr 2023 20:29:38 +0530 Subject: [PATCH 44/47] comments addressed --- experimental/traffic-portal/src/app/api/server.service.ts | 5 +++-- .../traffic-portal/src/app/api/testing/server.service.ts | 7 +++---- .../statuses/status-details/status-details.component.ts | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/server.service.ts b/experimental/traffic-portal/src/app/api/server.service.ts index 71b834b621..1f0d8f23b2 100644 --- a/experimental/traffic-portal/src/app/api/server.service.ts +++ b/experimental/traffic-portal/src/app/api/server.service.ts @@ -211,9 +211,10 @@ export class ServerService extends APIService { /** * Deletes an existing Status. * - * @param id The Status ID + * @param statusId The Status ID */ - public async deleteStatus(id: number | ResponseStatus): Promise { + public async deleteStatus(statusId: number | ResponseStatus): Promise { + const id = typeof (statusId) === "number" ? statusId : statusId.id; return this.delete(`statuses/${id}`).toPromise(); } } diff --git a/experimental/traffic-portal/src/app/api/testing/server.service.ts b/experimental/traffic-portal/src/app/api/testing/server.service.ts index 021252d553..ab288cca1a 100644 --- a/experimental/traffic-portal/src/app/api/testing/server.service.ts +++ b/experimental/traffic-portal/src/app/api/testing/server.service.ts @@ -325,12 +325,11 @@ export class ServerService { * Updates status Details. * * @param payload containes name and description for the status., unique identifier thereof. - * @param id The Status ID */ - public async updateStatusDetail(payload: ResponseStatus, id: number): Promise { - const index = this.statuses.findIndex(u => u.id === id); + public async updateStatusDetail(payload: ResponseStatus): Promise { + const index = this.statuses.findIndex(u => u.id === payload.id); if (index < 0) { - throw new Error(`no such status with id: ${id}`); + throw new Error(`no such status with id: ${payload.id}`); } const updated = { ...payload, diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index b01a2b3ec0..aa219f85c6 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -115,7 +115,6 @@ export class StatusDetailsComponent { name: this.statusDetailsForm.controls.name.value }; this.statusDetails = await this.api.createStatus(newData); - this.location.back(); } else { const editData: ResponseStatus = { description: this.statusDetailsForm.controls.description.value, From 789cdde41485c08d2f5f1617e94d8502e2e89b15 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Wed, 26 Apr 2023 22:33:36 +0530 Subject: [PATCH 45/47] comment addressed --- .../traffic-portal/src/app/api/testing/server.service.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/experimental/traffic-portal/src/app/api/testing/server.service.ts b/experimental/traffic-portal/src/app/api/testing/server.service.ts index ab288cca1a..c316b0c9d3 100644 --- a/experimental/traffic-portal/src/app/api/testing/server.service.ts +++ b/experimental/traffic-portal/src/app/api/testing/server.service.ts @@ -343,10 +343,11 @@ export class ServerService { /** * Deletes a Status. * - * @param id The ID of the Status to delete. + * @param statusId The ID of the Status to delete. * @returns The deleted status. */ - public async deleteStatus(id: number): Promise { + public async deleteStatus(statusId: number | ResponseStatus): Promise { + const id = typeof (statusId) === "number" ? statusId : statusId.id; const idx = this.statuses.findIndex(j => j.id === id); if (idx < 0) { throw new Error(`no such status: #${id}`); From d3ab71a9407840748ed3b52aa504fee13b67a7dc Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Thu, 27 Apr 2023 09:04:02 +0530 Subject: [PATCH 46/47] comment fixed --- .../statuses/status-details/status-details.component.html | 1 - .../statuses/statuses-table/statuses-table.component.ts | 7 ++++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html index 4cf4962ca4..ae4fffe695 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.html @@ -36,7 +36,6 @@ - diff --git a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts index 82e9e82ceb..e43ed839cc 100644 --- a/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/statuses-table/statuses-table.component.ts @@ -67,11 +67,12 @@ export class StatusesTableComponent implements OnInit { /** Definitions for the context menu items (which act on statuses data). */ public contextMenuItems: Array> = [ { - href: (u: ResponseStatus): string => `${u.id}`, - name: "View Status Details" + href: (status: ResponseStatus): string => `${status.id}`, + name: "Open in New Tab", + newTab: true }, { - href: (u: ResponseStatus): string => `${u.id}`, + href: (status: ResponseStatus): string => `${status.id}`, name: "Edit" }, { From e3d51d50dcd21f2c3c2f9cf23587739d154b9623 Mon Sep 17 00:00:00 2001 From: gbkannan89 <111871677+gbkannan89@users.noreply.github.com> Date: Fri, 28 Apr 2023 05:40:21 +0530 Subject: [PATCH 47/47] # removed --- .../core/statuses/status-details/status-details.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts index aa219f85c6..04119b49f6 100644 --- a/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts +++ b/experimental/traffic-portal/src/app/core/statuses/status-details/status-details.component.ts @@ -88,7 +88,7 @@ export class StatusDetailsComponent { this.statusDetails = await this.api.getStatuses(Number(id)); // Set page title with status Name - this.navSvc.headerTitle.next(`Status #${this.statusDetails.name}`); + this.navSvc.headerTitle.next(`Status ${this.statusDetails.name}`); // Patch the form with existing data we got from service requested above. this.statusDetailsForm.setValue({