This is an automated email from the ASF dual-hosted git repository.
shamrick pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new 814a979a7c tpv2 redirect to tpv1 (#7585)
814a979a7c is described below
commit 814a979a7c66fbd547a5253374689e63d185e8c6
Author: Kannan.G.B <[email protected]>
AuthorDate: Thu Jul 27 23:09:13 2023 +0530
tpv2 redirect to tpv1 (#7585)
* tpv2 redirect to tpv1
* tpv1 url added from server side loading
* tpv1url config added
* tpv1url config added
* lint error fix
* localstorage
* comments addressed
* comments addressed
* config changes for integration test
---
.github/actions/tpv2-integration-tests/config.json | 1 +
experimental/traffic-portal/server.config.ts | 54 +++++++++++++++-------
experimental/traffic-portal/server.ts | 19 +++++++-
.../traffic-portal/src/app/app.component.ts | 19 +++++++-
.../app/shared/navigation/navigation.service.ts | 50 ++++++++++++++++++--
.../tp-sidebar/tp-sidebar.component.html | 21 +++++++--
.../navigation/tp-sidebar/tp-sidebar.component.ts | 11 +++++
.../src/environments/environment.prod.ts | 2 +-
.../traffic-portal/src/environments/environment.ts | 2 +-
9 files changed, 152 insertions(+), 27 deletions(-)
diff --git a/.github/actions/tpv2-integration-tests/config.json
b/.github/actions/tpv2-integration-tests/config.json
index 2664c0cb6c..00d1287511 100644
--- a/.github/actions/tpv2-integration-tests/config.json
+++ b/.github/actions/tpv2-integration-tests/config.json
@@ -2,6 +2,7 @@
"insecure": true,
"port": 4200,
"trafficOps": "https://localhost:6443",
+ "tpv1Url": "https://localhost",
"useSSL": false,
"browserFolder": "$GITHUB_WORKSPACE/$BROWSER_FOLDER"
}
diff --git a/experimental/traffic-portal/server.config.ts
b/experimental/traffic-portal/server.config.ts
index 12dcff7fdf..ce552ca83b 100644
--- a/experimental/traffic-portal/server.config.ts
+++ b/experimental/traffic-portal/server.config.ts
@@ -15,6 +15,7 @@
import { execSync } from "child_process";
import { existsSync, readFileSync } from "fs";
import { join } from "path";
+import { hasProperty } from "src/app/utils";
/**
* ServerVersion contains versioning information for the server,
@@ -139,6 +140,8 @@ interface BaseConfig {
version: ServerVersion;
/** Path to the folder containing browser files. **/
browserFolder: string;
+ /** The URL of the Traffic Portal V1. */
+ tpv1Url: URL;
}
/**
@@ -183,53 +186,65 @@ function isConfig(c: unknown): c is ServerConfig {
throw new Error("'null' is not a valid configuration");
}
- if (Object.prototype.hasOwnProperty.call(c, "insecure")) {
- if (typeof((c as {insecure: unknown}).insecure) !== "boolean") {
+ if (hasProperty(c, "insecure")) {
+ if (typeof(c.insecure) !== "boolean") {
throw new Error("'insecure' must be a boolean");
}
} else {
(c as {insecure: boolean}).insecure = false;
}
- if (!Object.prototype.hasOwnProperty.call(c, "port")) {
+ if (!hasProperty(c, "port")) {
throw new Error("'port' is required");
}
- if (typeof((c as {port: unknown}).port) !== "number") {
+ if (typeof(c.port) !== "number") {
throw new Error("'port' must be a number");
}
- if (!Object.prototype.hasOwnProperty.call(c, "trafficOps")) {
+ if (!hasProperty(c, "trafficOps")){
throw new Error("'trafficOps' is required");
}
- if (typeof((c as {trafficOps: unknown}).trafficOps) !== "string") {
+ if (typeof(c.trafficOps) !== "string") {
throw new Error("'trafficOps' must be a string");
}
- if (!Object.prototype.hasOwnProperty.call(c, "browserFolder")) {
+ if(!hasProperty(c, "tpv1Url")){
+ throw new Error("'tpv1Url' is required");
+ }
+ if (typeof(c.tpv1Url) !== "string") {
+ throw new Error("'tpv1Url' must be a string");
+ }
+ if (!hasProperty(c, "browserFolder")) {
throw new Error("'browserFolder' is required");
}
- if (typeof((c as {browserFolder: unknown}).browserFolder) !== "string")
{
+ if (typeof(c.browserFolder) !== "string") {
throw new Error("'browserFolder' must be a string");
}
try {
- (c as {trafficOps: URL}).trafficOps = new URL((c as
{trafficOps: string}).trafficOps);
+ c.trafficOps = new URL(c.trafficOps);
} catch (e) {
throw new Error(`'trafficOps' is not a valid URL: ${e}`);
}
- if (Object.prototype.hasOwnProperty.call(c, "useSSL")) {
- if (typeof((c as {useSSL: unknown}).useSSL) !== "boolean") {
+ try {
+ c.tpv1Url = new URL(c.tpv1Url);
+ } catch (e) {
+ throw new Error(`'tpv1Url' is not a valid URL: ${e}`);
+ }
+
+ if (hasProperty(c, "useSSL")) {
+ if (typeof(c.useSSL) !== "boolean") {
throw new Error("'useSSL' must be a boolean");
}
- if ((c as {useSSL: boolean}).useSSL) {
- if (!Object.prototype.hasOwnProperty.call(c,
"certPath")) {
+ if (c.useSSL) {
+ if (!hasProperty(c, "certPath")) {
throw new Error("'certPath' is required to use
SSL");
}
- if (typeof((c as {certPath: unknown}).certPath) !==
"string") {
+ if (typeof(c.certPath) !== "string") {
throw new Error("'certPath' must be a string");
}
- if (!Object.prototype.hasOwnProperty.call(c,
"keyPath")) {
+ if (!hasProperty(c, "keyPath")) {
throw new Error("'keyPath' is required to use
SSL");
}
- if (typeof((c as {keyPath: unknown}).keyPath) !==
"string") {
+ if (typeof(c.keyPath) !== "string") {
throw new Error("'keyPath' must be a string");
}
}
@@ -296,6 +311,7 @@ export function getVersion(path?: string): ServerVersion {
/** The type of command line arguments to Traffic Portal. */
interface Args {
trafficOps?: URL;
+ tpv1Url?: URL;
insecure: boolean;
port: number;
certPath?: string;
@@ -311,6 +327,7 @@ export const defaultConfig: ServerConfig = {
insecure: false,
port: 4200,
trafficOps: new URL("https://example.com"),
+ tpv1Url: new URL("https://example.com"),
version: { version: "" }
};
/**
@@ -372,6 +389,10 @@ export function getConfig(args: Args, ver: ServerVersion):
ServerConfig {
}
}
+ if (args.tpv1Url) {
+ cfg.tpv1Url = args.tpv1Url;
+ }
+
if (readFromFile && cfg.useSSL) {
if (args.certPath) {
cfg.certPath = args.certPath;
@@ -392,6 +413,7 @@ export function getConfig(args: Args, ver: ServerVersion):
ServerConfig {
keyPath: args.keyPath,
port: cfg.port,
trafficOps: cfg.trafficOps,
+ tpv1Url: cfg.tpv1Url,
useSSL: true,
version: ver
};
diff --git a/experimental/traffic-portal/server.ts
b/experimental/traffic-portal/server.ts
index f5f615d122..78702d2ddc 100644
--- a/experimental/traffic-portal/server.ts
+++ b/experimental/traffic-portal/server.ts
@@ -192,7 +192,10 @@ export function app(serverConfig: ServerConfig):
express.Express {
// All regular routes use the Universal engine
server.get("*", (req, res) => {
- res.render(indexHtml, {providers: [{provide: APP_BASE_HREF,
useValue: req.baseUrl}], req});
+ res.render(indexHtml, {providers: [
+ {provide: APP_BASE_HREF, useValue: req.baseUrl},
+ {provide: "TP_V1_URL", useValue: config.tpv1Url}
+ ], req});
});
server.enable("trust proxy");
@@ -227,6 +230,20 @@ function run(): number {
}
}
});
+ parser.add_argument("-u", "--tpv1-url", {
+ dest: "tpv1Url",
+ help: "Specify the Traffic Portal v1 URL. (Default: uses the
`TP_V1_URL` environment variable)",
+ type: (arg: string) => {
+ try {
+ return new URL(arg);
+ } catch (e) {
+ if (e instanceof TypeError) {
+ return new URL(`https://${arg}`);
+ }
+ throw e;
+ }
+ }
+ });
parser.add_argument("-k", "--insecure", {
action: "store_true",
help: "Skip Traffic Ops server certificate validation. This
affects requests from Traffic Portal to Traffic Ops AND signature" +
diff --git a/experimental/traffic-portal/src/app/app.component.ts
b/experimental/traffic-portal/src/app/app.component.ts
index 55007fd29e..601387e47f 100644
--- a/experimental/traffic-portal/src/app/app.component.ts
+++ b/experimental/traffic-portal/src/app/app.component.ts
@@ -12,12 +12,15 @@
* limitations under the License.
*/
-import {Component, OnInit} from "@angular/core";
+import { isPlatformBrowser } from "@angular/common";
+import { Component, Inject, OnInit, Optional, PLATFORM_ID, TransferState,
makeStateKey } from "@angular/core";
import { Router } from "@angular/router";
import { ResponseCurrentUser } from "trafficops-types";
import { CurrentUserService } from
"src/app/shared/current-user/current-user.service";
+export const LOCAL_TPV1_URL = "tp_v1_url";
+
/**
* The most basic component that contains everything else. This should be kept
pretty simple.
*/
@@ -31,7 +34,19 @@ export class AppComponent implements OnInit {
/** The currently logged-in user */
public currentUser: ResponseCurrentUser | null = null;
- constructor(private readonly router: Router, private readonly auth:
CurrentUserService) {
+ constructor(private readonly router: Router, private readonly auth:
CurrentUserService,
+ @Inject(PLATFORM_ID) private readonly platformId: object,
+ @Optional() @Inject("TP_V1_URL") public tpv1url: string,
+ private readonly transferState: TransferState) {
+ const storeKey = makeStateKey<string>("messageKey");
+
+ // get data from transferState if browser side
+ if (isPlatformBrowser(this.platformId)) {
+ this.tpv1url = this.transferState.get(storeKey,
"https://localhost");
+ window.localStorage.setItem(LOCAL_TPV1_URL,
this.tpv1url);
+ } else { // server side: get provided tpv1 url and store in in
transfer state
+ this.transferState.set(storeKey, this.tpv1url);
+ }
}
/**
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 f76aa12b93..3130756f97 100644
---
a/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts
+++
b/experimental/traffic-portal/src/app/shared/navigation/navigation.service.ts
@@ -11,10 +11,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import { Injectable } from "@angular/core";
+import { isPlatformBrowser } from "@angular/common";
+import { Inject, Injectable, PLATFORM_ID } from "@angular/core";
import { ReplaySubject } from "rxjs";
import { UserService } from "src/app/api";
+import { LOCAL_TPV1_URL } from "src/app/app.component";
import { CurrentUserService } from
"src/app/shared/current-user/current-user.service";
/**
@@ -60,8 +62,15 @@ export class NavigationService {
private readonly horizontalNavs: Map<string, HeaderNavigation>;
private readonly verticalNavs: Map<string, HeaderNavigation>;
+ private readonly tpv1Url: string = "http:localhost:433";
- constructor(private readonly auth: CurrentUserService, private readonly
api: UserService) {
+ constructor(
+ private readonly auth: CurrentUserService,
+ private readonly api: UserService,
+ @Inject(PLATFORM_ID) private readonly platformId: object) {
+ if (isPlatformBrowser(this.platformId)) {
+ this.tpv1Url =
window.localStorage.getItem(LOCAL_TPV1_URL) ?? this.tpv1Url;
+ }
this.horizontalNavs = new Map<string, HeaderNavigation>([
["Home", {
routerLink: "/core",
@@ -153,11 +162,38 @@ export class NavigationService {
name: "Cache Groups"
}],
name: "Servers"
+ }, {
+ children: [
+ {
+ href: `${this.tpv1Url}/cache-checks`,
+ name: "Cache Checks"
+ },
+ {
+ href: `${this.tpv1Url}/cache-stats`,
+ name: "Cache Stats"
+ }
+ ],
+ name: "Monitor"
+ }, {
+ children: [
+ {
+ href:
`${this.tpv1Url}/delivery-services`,
+ name: "Delivery Services"
+ },
+ {
+ href:
`${this.tpv1Url}/delivery-service-requests`,
+ name: "Delivery Service Requests"
+ }
+ ],
+ name: "Services"
}, {
children: [
{
href: "/core/types",
name: "Types"
+ }, {
+ href: `${this.tpv1Url}/origins`,
+ name: "Origins"
},
{
href: "/core/parameters",
@@ -193,7 +229,15 @@ export class NavigationService {
{
href: "/core/iso-gen",
name: "Generate System ISO"
- }
+ },
+ {
+ href: `${this.tpv1Url}/jobs`,
+ name: "Invalidate Content"
+ },
+ {
+ href: `${this.tpv1Url}/notifications`,
+ name: "Notifications"
+ },
],
name: "Other"
}]);
diff --git
a/experimental/traffic-portal/src/app/shared/navigation/tp-sidebar/tp-sidebar.component.html
b/experimental/traffic-portal/src/app/shared/navigation/tp-sidebar/tp-sidebar.component.html
index a2fb6b3fbf..b28d6be4ce 100644
---
a/experimental/traffic-portal/src/app/shared/navigation/tp-sidebar/tp-sidebar.component.html
+++
b/experimental/traffic-portal/src/app/shared/navigation/tp-sidebar/tp-sidebar.component.html
@@ -17,9 +17,24 @@ limitations under the License.
<div id="sidenav-container">
<mat-tree [dataSource]="dataSource"
[treeControl]="treeCtrl" id="sidebar-nav-tree">
<mat-tree-node *matTreeNodeDef="let node"
matTreeNodeToggle>
- <a class="mat-tree-node" mat-menu-item
[attr.aria-label]="'Navigate to ' + node.name" [routerLink]="node.href"
[routerLinkActive]="['boldNode']" [routerLinkActiveOptions]="routeOptions">
- {{node.name}}
- </a>
+ <!-- load either redirect or relative
path href -->
+ <div *ngIf="isAbsoluteURL(node.href);
then absolute else relative"></div>
+
+ <!-- Checking whether href is relative
URL -->
+ <ng-template #relative>
+ <a class="mat-tree-node"
mat-menu-item [attr.aria-label]="'Navigate to ' + node.name"
[routerLink]="node.href"
+
[routerLinkActive]="['boldNode']" [routerLinkActiveOptions]="routeOptions">
+ {{node.name}}
+ </a>
+ </ng-template>
+
+ <!-- Checking whether href is absolute
URL - For TPv1 redirect purpose -->
+ <ng-template #absolute>
+ <a class="mat-tree-node"
mat-menu-item [attr.aria-label]="'Navigate to ' + node.name" [href]="node.href"
+ target="_blank">
+ {{node.name}}
+ </a>
+ </ng-template>
</mat-tree-node>
<mat-nested-tree-node *matTreeNodeDef="let
node; when: hasChild">
<div class="mat-tree-node"
matTreeNodeToggle mat-menu-item [attr.aria-label]="'Toggle ' + node.name" >
diff --git
a/experimental/traffic-portal/src/app/shared/navigation/tp-sidebar/tp-sidebar.component.ts
b/experimental/traffic-portal/src/app/shared/navigation/tp-sidebar/tp-sidebar.component.ts
index 9a723bc8bb..61fd89d8f0 100644
---
a/experimental/traffic-portal/src/app/shared/navigation/tp-sidebar/tp-sidebar.component.ts
+++
b/experimental/traffic-portal/src/app/shared/navigation/tp-sidebar/tp-sidebar.component.ts
@@ -187,4 +187,15 @@ export class TpSidebarComponent implements OnInit,
AfterViewInit {
public nodeHandle(node: TreeNavNode): string {
return `${node.name}${node.href ?? ""}`;
}
+
+ /**
+ * Determines whether href is absolute or not - for TPv2 to TPv1
redirect
+ *
+ * @param href route string
+ * @returns true if href absolute
+ */
+ public isAbsoluteURL(href: string): boolean {
+ const regexPattern = /^(?:[a-z]+:)?\/\//i;
+ return regexPattern.test(href);
+ }
}
diff --git a/experimental/traffic-portal/src/environments/environment.prod.ts
b/experimental/traffic-portal/src/environments/environment.prod.ts
index 27807dd4fa..085d3abc54 100644
--- a/experimental/traffic-portal/src/environments/environment.prod.ts
+++ b/experimental/traffic-portal/src/environments/environment.prod.ts
@@ -21,5 +21,5 @@ import type { Environment } from "./environment.type";
export const environment: Environment = {
apiVersion: "4.0",
customModule: false,
- production: true
+ production: true,
};
diff --git a/experimental/traffic-portal/src/environments/environment.ts
b/experimental/traffic-portal/src/environments/environment.ts
index 325ba4fcf2..7fca62c2b7 100644
--- a/experimental/traffic-portal/src/environments/environment.ts
+++ b/experimental/traffic-portal/src/environments/environment.ts
@@ -26,5 +26,5 @@ export const environment: Environment = {
apiVersion: "4.0",
customModule: false,
production: false,
- useExhaustiveDates: true
+ useExhaustiveDates: true,
};