ocket8888 commented on code in PR #7881:
URL: https://github.com/apache/trafficcontrol/pull/7881#discussion_r1445386640


##########
experimental/traffic-portal/src/app/api/coordinate.service.ts:
##########
@@ -0,0 +1,85 @@
+/*
+ * 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 type { ResponseCoordinate } from "trafficops-types";
+
+import { APIService } from "./base-api.service";
+
+/**
+ * CoordinateService exposes API functionality relating to Coordinates.
+ */
+@Injectable()
+export class CoordinateService extends APIService {
+       /**
+        * Gets a specific Coordinate from Traffic Ops.
+        *
+        * @param idOrName Either the integral, unique identifier (number) or 
name
+        * (string) of the Coordinate to be returned.
+        * @returns The requested Coordinate.
+        */
+       public async getCoordinates(
+               idOrName: number | string
+       ): Promise<ResponseCoordinate>;
+       /**
+        * Gets Coordinates from Traffic Ops.
+        *
+        * @returns An Array of all Coordinates from Traffic Ops.
+        */
+       public async getCoordinates(): Promise<Array<ResponseCoordinate>>;
+
+       /**
+        * Gets one or all Coordinates from Traffic Ops.
+        *
+        * @param idOrName Optionally the integral, unique identifier (number) 
or
+        * name (string) of a single Coordinate to be returned.
+        * @returns The requested Coordinate(s).
+        */
+       public async getCoordinates(
+               idOrName?: number | string
+       ): Promise<ResponseCoordinate | Array<ResponseCoordinate>> {
+               const path = "coordinates";
+               if (idOrName !== undefined) {
+                       let params;
+                       switch (typeof idOrName) {
+                               case "string":
+                                       params = { name: idOrName };
+                                       break;
+                               case "number":
+                                       params = { id: idOrName };
+                       }
+                       const r = await this.get<[ResponseCoordinate]>(
+                               path,
+                               undefined,
+                               params
+                       ).toPromise();
+                       if (r.length !== 1) {
+                               throw new Error(

Review Comment:
   this statement is not covered by tests



##########
experimental/traffic-portal/src/app/core/origins/detail/origin-detail.component.ts:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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 { Location } from "@angular/common";
+import { Component, OnInit } from "@angular/core";
+import { MatDialog } from "@angular/material/dialog";
+import { ActivatedRoute } from "@angular/router";
+import type {
+       RequestOrigin,
+       RequestOriginResponse,
+       ResponseCacheGroup,
+       ResponseCoordinate,
+       ResponseDeliveryService,
+       ResponseProfile,
+       ResponseTenant,
+} from "trafficops-types";
+
+import {
+       CacheGroupService,
+       CoordinateService,
+       DeliveryServiceService,
+       OriginService,
+       ProfileService,
+       UserService,
+} from "src/app/api";
+import { DecisionDialogComponent } from 
"src/app/shared/dialogs/decision-dialog/decision-dialog.component";
+import { LoggingService } from "src/app/shared/logging.service";
+import { NavigationService } from 
"src/app/shared/navigation/navigation.service";
+
+/**
+ * OriginDetailComponent is the controller for the origin add/edit form.
+ */
+@Component({
+       selector: "tp-origins-detail",
+       styleUrls: ["./origin-detail.component.scss"],
+       templateUrl: "./origin-detail.component.html",
+})
+export class OriginDetailComponent implements OnInit {
+       public new = false;
+       public origin!: RequestOriginResponse;
+       public tenants = new Array<ResponseTenant>();
+       public coordinates = new Array<ResponseCoordinate>();
+       public cacheGroups = new Array<ResponseCacheGroup>();
+       public profiles = new Array<ResponseProfile>();
+       public deliveryServices = new Array<ResponseDeliveryService>();
+       public protocols = new Array<string>();
+
+       constructor(
+               private readonly route: ActivatedRoute,
+               private readonly originService: OriginService,
+               private readonly location: Location,
+               private readonly dialog: MatDialog,
+               private readonly navSvc: NavigationService,
+               private readonly log: LoggingService,
+               private readonly userService: UserService,
+               private readonly coordinateService: CoordinateService,
+               private readonly cacheGroupService: CacheGroupService,
+               private readonly profileService: ProfileService,
+               private readonly dsService: DeliveryServiceService
+       ) {}
+
+       /**
+        * Angular lifecycle hook where data is initialized.
+        */
+       public async ngOnInit(): Promise<void> {
+               this.tenants = await this.userService.getTenants();
+               this.cacheGroups = await 
this.cacheGroupService.getCacheGroups();
+               this.coordinates = await 
this.coordinateService.getCoordinates();
+               this.profiles = await this.profileService.getProfiles();
+               this.deliveryServices = await 
this.dsService.getDeliveryServices();
+               this.protocols = ["http", "https"];
+
+               const ID = this.route.snapshot.paramMap.get("id");
+               if (ID === null) {
+                       this.log.error("missing required route parameter 'id'");
+                       return;
+               }
+               if (ID === "new") {
+                       this.navSvc.headerTitle.next("New Origin");
+                       this.new = true;
+                       this.origin = {
+                               cachegroup: null,
+                               cachegroupId: -1,
+                               coordinate: null,
+                               coordinateId: -1,
+                               deliveryService: null,
+                               deliveryServiceId: -1,
+                               fqdn: "",
+                               id: -1,
+                               ip6Address: null,
+                               ipAddress: null,
+                               isPrimary: null,
+                               lastUpdated: new Date(),
+                               name: "",
+                               port: null,
+                               profile: null,
+                               profileId: -1,
+                               protocol: "https",
+                               tenant: null,
+                               tenantId: -1,
+                       };
+                       return;
+               }
+               const numID = parseInt(ID, 10);
+               if (Number.isNaN(numID)) {
+                       this.log.error("route parameter 'id' was non-number: ", 
ID);
+                       return;
+               }
+               this.origin = await this.originService.getOrigins(numID);
+               this.navSvc.headerTitle.next(`Origin: ${this.origin.name}`);
+       }
+
+       /**
+        * Deletes the current origin.
+        */
+       public async deleteOrigin(): Promise<void> {

Review Comment:
   This method is not tested



##########
experimental/traffic-portal/src/app/api/origin.service.ts:
##########
@@ -0,0 +1,122 @@
+/*
+ * 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 type { RequestOrigin, RequestOriginResponse } from "trafficops-types";
+
+import { APIService } from "./base-api.service";
+
+/**
+ * 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<RequestOriginResponse>;
+       /**
+        * Gets Origins from Traffic Ops.
+        *
+        * @returns An Array of all Origins from Traffic Ops.
+        */
+       public async getOrigins(): Promise<Array<RequestOriginResponse>>;
+       /**
+        * 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<RequestOriginResponse | Array<RequestOriginResponse>> {
+               const path = "origins";
+               if (idOrName !== undefined) {
+                       let params;
+                       switch (typeof idOrName) {
+                               case "string":
+                                       params = { name: idOrName };
+                                       break;
+                               case "number":
+                                       params = { id: idOrName };
+                       }
+                       const r = await this.get<[RequestOriginResponse]>(
+                               path,
+                               undefined,
+                               params
+                       ).toPromise();
+                       if (r.length !== 1) {
+                               throw new Error(

Review Comment:
   This statement is not covered by tests



##########
experimental/traffic-portal/src/app/core/origins/detail/origin-detail.component.ts:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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 { Location } from "@angular/common";
+import { Component, OnInit } from "@angular/core";
+import { MatDialog } from "@angular/material/dialog";
+import { ActivatedRoute } from "@angular/router";
+import type {
+       RequestOrigin,
+       RequestOriginResponse,
+       ResponseCacheGroup,
+       ResponseCoordinate,
+       ResponseDeliveryService,
+       ResponseProfile,
+       ResponseTenant,
+} from "trafficops-types";
+
+import {
+       CacheGroupService,
+       CoordinateService,
+       DeliveryServiceService,
+       OriginService,
+       ProfileService,
+       UserService,
+} from "src/app/api";
+import { DecisionDialogComponent } from 
"src/app/shared/dialogs/decision-dialog/decision-dialog.component";
+import { LoggingService } from "src/app/shared/logging.service";
+import { NavigationService } from 
"src/app/shared/navigation/navigation.service";
+
+/**
+ * OriginDetailComponent is the controller for the origin add/edit form.
+ */
+@Component({
+       selector: "tp-origins-detail",
+       styleUrls: ["./origin-detail.component.scss"],
+       templateUrl: "./origin-detail.component.html",
+})
+export class OriginDetailComponent implements OnInit {
+       public new = false;
+       public origin!: RequestOriginResponse;
+       public tenants = new Array<ResponseTenant>();
+       public coordinates = new Array<ResponseCoordinate>();
+       public cacheGroups = new Array<ResponseCacheGroup>();
+       public profiles = new Array<ResponseProfile>();
+       public deliveryServices = new Array<ResponseDeliveryService>();
+       public protocols = new Array<string>();
+
+       constructor(
+               private readonly route: ActivatedRoute,
+               private readonly originService: OriginService,
+               private readonly location: Location,
+               private readonly dialog: MatDialog,
+               private readonly navSvc: NavigationService,
+               private readonly log: LoggingService,
+               private readonly userService: UserService,
+               private readonly coordinateService: CoordinateService,
+               private readonly cacheGroupService: CacheGroupService,
+               private readonly profileService: ProfileService,
+               private readonly dsService: DeliveryServiceService
+       ) {}
+
+       /**
+        * Angular lifecycle hook where data is initialized.
+        */
+       public async ngOnInit(): Promise<void> {
+               this.tenants = await this.userService.getTenants();
+               this.cacheGroups = await 
this.cacheGroupService.getCacheGroups();
+               this.coordinates = await 
this.coordinateService.getCoordinates();
+               this.profiles = await this.profileService.getProfiles();
+               this.deliveryServices = await 
this.dsService.getDeliveryServices();
+               this.protocols = ["http", "https"];
+
+               const ID = this.route.snapshot.paramMap.get("id");
+               if (ID === null) {
+                       this.log.error("missing required route parameter 'id'");
+                       return;
+               }
+               if (ID === "new") {
+                       this.navSvc.headerTitle.next("New Origin");
+                       this.new = true;
+                       this.origin = {
+                               cachegroup: null,
+                               cachegroupId: -1,
+                               coordinate: null,
+                               coordinateId: -1,
+                               deliveryService: null,
+                               deliveryServiceId: -1,
+                               fqdn: "",
+                               id: -1,
+                               ip6Address: null,
+                               ipAddress: null,
+                               isPrimary: null,
+                               lastUpdated: new Date(),
+                               name: "",
+                               port: null,
+                               profile: null,
+                               profileId: -1,
+                               protocol: "https",
+                               tenant: null,
+                               tenantId: -1,
+                       };
+                       return;
+               }
+               const numID = parseInt(ID, 10);
+               if (Number.isNaN(numID)) {
+                       this.log.error("route parameter 'id' was non-number: ", 
ID);
+                       return;
+               }
+               this.origin = await this.originService.getOrigins(numID);
+               this.navSvc.headerTitle.next(`Origin: ${this.origin.name}`);
+       }
+
+       /**
+        * Deletes the current origin.
+        */
+       public async deleteOrigin(): Promise<void> {
+               if (this.new) {
+                       this.log.error("Unable to delete new origin");
+                       return;
+               }
+               const ref = this.dialog.open(DecisionDialogComponent, {
+                       data: {
+                               message: `Are you sure you want to delete 
origin ${this.origin.name}`,
+                               title: "Confirm Delete",
+                       },
+               });
+               ref.afterClosed().subscribe((result) => {
+                       if (result) {
+                               this.originService.deleteOrigin(this.origin.id);
+                               this.location.back();
+                       }
+               });
+       }
+
+       /**
+        * Submits new/updated origin.
+        *
+        * @param e HTML form submission event.
+        */
+       public async submit(e: Event): Promise<void> {

Review Comment:
   This method is not tested



##########
experimental/traffic-portal/src/app/core/origins/table/origins-table.component.ts:
##########
@@ -0,0 +1,193 @@
+/*
+ * 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, type OnInit } from "@angular/core";
+import { FormControl } from "@angular/forms";
+import { MatDialog } from "@angular/material/dialog";
+import { ActivatedRoute } from "@angular/router";
+import { ColDef } from "ag-grid-community";
+import { BehaviorSubject } from "rxjs";
+import type { RequestOriginResponse } from "trafficops-types";
+
+import { OriginService } 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 type {
+       ContextMenuActionEvent,
+       ContextMenuItem,
+       DoubleClickLink,
+} from "src/app/shared/generic-table/generic-table.component";
+import { NavigationService } from 
"src/app/shared/navigation/navigation.service";
+
+/**
+ * OriginsTableComponent is the controller for the "Origins" table.
+ */
+@Component({
+       selector: "tp-origins",
+       styleUrls: ["./origins-table.component.scss"],
+       templateUrl: "./origins-table.component.html",
+})
+export class OriginsTableComponent implements OnInit {
+       /** List of origins */
+       public origins: Promise<Array<RequestOriginResponse>>;
+
+       /** Definitions of the table's columns according to the ag-grid API */
+       public columnDefs: ColDef[] = [
+               {
+                       field: "name",
+                       headerName: "Name",
+               },
+               {
+                       field: "tenant",
+                       headerName: "Tenant",
+               },
+               {
+                       field: "isPrimary",
+                       filter: "tpBooleanFilter",
+                       headerName: "Primary",
+               },
+               {
+                       field: "deliveryService",
+                       headerName: "Delivery Service",
+               },
+               {
+                       field: "fqdn",
+                       headerName: "FQDN",
+               },
+               {
+                       cellRenderer: "sshCellRenderer",
+                       field: "ipAddress",
+                       headerName: "IPv4 Address",
+               },
+               {
+                       cellRenderer: "sshCellRenderer",
+                       field: "ip6Address",
+                       headerName: "IPv6 Address",
+               },
+               {
+                       field: "protocol",
+                       headerName: "Protocol",
+               },
+               {
+                       field: "port",
+                       headerName: "Port",
+               },
+               {
+                       field: "coordinate",
+                       headerName: "Coordinate",
+               },
+               {
+                       field: "cachegroup",
+                       headerName: "CacheGroup",
+               },
+               {
+                       field: "profile",
+                       headerName: "Profile",
+               },
+               {
+                       field: "lastUpdated",
+                       filter: "agDateColumnFilter",
+                       headerName: "Last Updated",
+                       hide: true,
+               },
+       ];
+
+       /** Definitions for the context menu items (which act on augmented 
origin data). */
+       public contextMenuItems: Array<ContextMenuItem<RequestOriginResponse>> 
= [
+               {
+                       href: (origin: RequestOriginResponse): string => 
`${origin.id}`,
+                       name: "Edit",
+               },
+               {
+                       href: (origin: RequestOriginResponse): string => 
`${origin.id}`,
+                       name: "Open in New Tab",
+                       newTab: true,
+               },
+               {
+                       action: "delete",
+                       multiRow: false,
+                       name: "Delete",
+               },
+       ];
+
+       /** Defines what the table should do when a row is double-clicked. */
+       public doubleClickLink: DoubleClickLink<RequestOriginResponse> = {
+               href: (row: RequestOriginResponse): string => 
`/core/origins/${row.id}`,

Review Comment:
   Though it seems pedantic and trivial, testing this object's `href` method is 
the only thing preventing this file from having 100% coverage.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to