Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Origins page Traffic Portal v2 #7881

Merged
merged 8 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions experimental/traffic-portal/src/app/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export * from "./server.service";
export * from "./topology.service";
export * from "./type.service";
export * from "./user.service";
export * from "./origin.service";

/**
* The API Module contains all logic used to access the Traffic Ops API.
Expand Down
128 changes: 128 additions & 0 deletions experimental/traffic-portal/src/app/api/origin.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* 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 { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import {RequestOrigin, ResponseOrigin} from "trafficops-types/dist/origin";

import { APIService } from "./base-api.service";

/** The allowed values for the 'useInTables' query parameter of GET requests to /origins. */
type UseInTable = "cachegroup" |
"server" |
"deliveryservice" |
"to_extension" |
"federation_resolver" |
"regex" |
"staticdnsentry" |
"steering_target";

/**
* OriginService exposes API functionality relating to Origins.
*/
@Injectable()
export class OriginService extends APIService {
/**
* Gets a specific Origin from Traffic Ops.
*
* @param idOrName Either the integral, unique identifier (number) or name
* (string) of the Origin to be returned.
* @returns The requested Origin.
*/
public async getOrigins(idOrName: number | string): Promise<ResponseOrigin>;
/**
* Gets Origins from Traffic Ops.
*
* @param idOrName Either the integral, unique identifier (number) or name
* (string) of a single Origin to be returned.
* @returns The requested Origin(s).
*/
public async getOrigins(): Promise<Array<ResponseOrigin>>;
/**
* Gets one or all Origins from Traffic Ops.
*
* @param idOrName Optionally the integral, unique identifier (number) or
* name (string) of a single Origin to be returned.
* @returns The requested Origin(s).
*/
public async getOrigins(idOrName?: number | string): Promise<ResponseOrigin | Array<ResponseOrigin>> {
const path = "origins";

Check warning on line 59 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L58-L59

Added lines #L58 - L59 were not covered by tests
if (idOrName !== undefined) {
let params;
switch (typeof idOrName) {
case "string":
params = {name: idOrName};
break;

Check warning on line 65 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L64-L65

Added lines #L64 - L65 were not covered by tests
case "number":
params = {id: idOrName};

Check warning on line 67 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L67

Added line #L67 was not covered by tests
}
const r = await this.get<[ResponseOrigin]>(path, undefined, params).toPromise();

Check warning on line 69 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L69

Added line #L69 was not covered by tests
if (r.length !== 1) {
throw new Error(`Traffic Ops responded with ${r.length} Origins by identifier ${idOrName}`);

Check warning on line 71 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L71

Added line #L71 was not covered by tests
}
return r[0];

Check warning on line 73 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L73

Added line #L73 was not covered by tests
}
return this.get<Array<ResponseOrigin>>(path).toPromise();

Check warning on line 75 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L75

Added line #L75 was not covered by tests
}

/**
* Gets all Origins used by specific database table.
*
* @param useInTable The database table for which to retrieve Origins.
* @returns The requested Origins.
*/
public async getOriginsInTable(useInTable: UseInTable): Promise<Array<ResponseOrigin>> {
return this.get<Array<ResponseOrigin>>("types", undefined, {useInTable}).toPromise();

Check warning on line 85 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L84-L85

Added lines #L84 - L85 were not covered by tests
}

/**
* Deletes an existing origin.
*
* @param typeOrId Id of the origin to delete.
* @returns The deleted origin.
*/
public async deleteOrigin(typeOrId: number | ResponseOrigin): Promise<ResponseOrigin> {

Check warning on line 94 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L94

Added line #L94 was not covered by tests
const id = typeof(typeOrId) === "number" ? typeOrId : typeOrId.id;
return this.delete<ResponseOrigin>(`origins/${id}`).toPromise();

Check warning on line 96 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L96

Added line #L96 was not covered by tests
}

/**
* Creates a new origin.
*
* @param origin The origin to create.
* @returns The created origin.
*/
public async createOrigin(origin: RequestOrigin): Promise<ResponseOrigin> {
return this.post<ResponseOrigin>("origins", origin).toPromise();

Check warning on line 106 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L105-L106

Added lines #L105 - L106 were not covered by tests
}

/**
* Replaces the current definition of a origin with the one given.
*
* @param origin The new origin.
* @returns The updated origin.
*/
public async updateOrigin(origin: ResponseOrigin): Promise<ResponseOrigin> {
const path = `origins/${origin.id}`;
return this.put<ResponseOrigin>(path, origin).toPromise();

Check warning on line 117 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L115-L117

Added lines #L115 - L117 were not covered by tests
}

/**
* Injects the Angular HTTP client service into the parent constructor.
*
* @param http The Angular HTTP client service.
*/
constructor(http: HttpClient) {
super(http);

Check warning on line 126 in experimental/traffic-portal/src/app/api/origin.service.ts

View check run for this annotation

Codecov / codecov/patch

experimental/traffic-portal/src/app/api/origin.service.ts#L125-L126

Added lines #L125 - L126 were not covered by tests
}
}
6 changes: 6 additions & 0 deletions experimental/traffic-portal/src/app/core/core.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ import {
} from "./deliveryservice/invalidation-jobs/new-invalidation-job-dialog/new-invalidation-job-dialog.component";
import { NewDeliveryServiceComponent } from "./deliveryservice/new-delivery-service/new-delivery-service.component";
import { ISOGenerationFormComponent } from "./misc/isogeneration-form/isogeneration-form.component";
import { OriginDetailComponent } from "./origins/detail/origin-detail.component";
import { OriginsTableComponent } from "./origins/table/origins-table.component";
import { ParameterDetailComponent } from "./parameters/detail/parameter-detail.component";
import { ParametersTableComponent } from "./parameters/table/parameters-table.component";
import { ProfileDetailComponent } from "./profiles/profile-detail/profile-detail.component";
Expand Down Expand Up @@ -118,6 +120,8 @@ export const ROUTES: Routes = [
{ component: CoordinatesTableComponent, path: "coordinates" },
{ component: TypesTableComponent, path: "types" },
{ component: TypeDetailComponent, path: "types/:id"},
{ component: OriginsTableComponent, path: "origins" },
{ component: OriginDetailComponent, path: "origins/:id"},
{ component: ParametersTableComponent, path: "parameters" },
{ component: ParameterDetailComponent, path: "parameters/:id" },
{ component: StatusesTableComponent, path: "statuses" },
Expand Down Expand Up @@ -175,6 +179,8 @@ export const ROUTES: Routes = [
TopologyDetailsComponent,
TypeDetailComponent,
TypesTableComponent,
OriginDetailComponent,
OriginsTableComponent,
UpdatePasswordDialogComponent,
UpdateStatusComponent,
UserDetailsComponent,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<!--
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 appearance="outlined" class="page-content single-column">
<tp-loading *ngIf="!type"></tp-loading>
<form ngNativeValidate (ngSubmit)="submit($event)" *ngIf="type">
<mat-card-header class="headers-container" *ngIf="!new">
<a mat-raised-button color="primary" *ngIf="type.useInTable === 'cachegroup'" [routerLink]="'/core/cache-groups'" [queryParams]="{typeName: type.name}">View Cache Groups</a>
<a mat-raised-button color="primary" *ngIf="type.useInTable === 'server'" [routerLink]="'/core/servers'" [queryParams]="{type: type.name}">View Servers</a>
</mat-card-header>
<mat-card-content class="container">
<mat-form-field *ngIf="!new">
<mat-label>ID</mat-label>
<input matInput type="text" name="id" disabled readonly [defaultValue]="type.id" />
</mat-form-field>
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput type="text" name="name" required [(ngModel)]="type.name" />
</mat-form-field>
<mat-form-field>
<mat-label>Description</mat-label>
<input matInput type="text" name="description" required [(ngModel)]="type.description" />
</mat-form-field>
<mat-form-field>
<mat-label>Use In Table</mat-label>
<input matInput type="text" name="useInTable" disabled readonly [defaultValue]="new ? 'server' : type.useInTable" />
</mat-form-field>
<mat-form-field *ngIf="!new">
<mat-label>Last Updated</mat-label>
<input matInput type="text" name="lastUpdated" disabled readonly [defaultValue]="type.lastUpdated" />
</mat-form-field>
</mat-card-content>
<mat-card-actions align="end" class="actions-container">
<button mat-raised-button type="button" *ngIf="!new" color="warn" (click)="deleteType()">Delete</button>
<button mat-raised-button type="submit" color="primary">Save</button>
</mat-card-actions>
</form>
</mat-card>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* 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.
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* 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 { MatDialogModule } from "@angular/material/dialog";
import { ActivatedRoute } from "@angular/router";
import { RouterTestingModule } from "@angular/router/testing";
import { ReplaySubject } from "rxjs";

import { APITestingModule } from "src/app/api/testing";
import { TypeDetailComponent } from "src/app/core/types/detail/type-detail.component";
import { NavigationService } from "src/app/shared/navigation/navigation.service";

describe("TypeDetailComponent", () => {
let component: TypeDetailComponent;
let fixture: ComponentFixture<TypeDetailComponent>;
let route: ActivatedRoute;
let paramMap: jasmine.Spy;

const navSvc = jasmine.createSpyObj([],{headerHidden: new ReplaySubject<boolean>(), headerTitle: new ReplaySubject<string>()});
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ TypeDetailComponent ],
imports: [ APITestingModule, RouterTestingModule, MatDialogModule ],
providers: [ { provide: NavigationService, useValue: navSvc } ]
})
.compileComponents();

route = TestBed.inject(ActivatedRoute);
paramMap = spyOn(route.snapshot.paramMap, "get");
paramMap.and.returnValue(null);
fixture = TestBed.createComponent(TypeDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it("should create", () => {
expect(component).toBeTruthy();
expect(paramMap).toHaveBeenCalled();
});

it("new type", async () => {
paramMap.and.returnValue("new");

fixture = TestBed.createComponent(TypeDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
await fixture.whenStable();
expect(paramMap).toHaveBeenCalled();
expect(component.type).not.toBeNull();
expect(component.type.name).toBe("");
expect(component.new).toBeTrue();
});

it("existing type", async () => {
paramMap.and.returnValue("1");

fixture = TestBed.createComponent(TypeDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
await fixture.whenStable();
expect(paramMap).toHaveBeenCalled();
expect(component.type).not.toBeNull();
expect(component.type.name).toBe("MID_LOC");
expect(component.new).toBeFalse();
});
});
Loading