shamrickus commented on code in PR #7292:
URL: https://github.com/apache/trafficcontrol/pull/7292#discussion_r1081549988


##########
experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.ts:
##########
@@ -162,13 +222,78 @@ export class CacheGroupTableComponent implements OnInit {
                this.fuzzySubject.next(this.fuzzControl.value);
        }
 
+       /**
+        * Queues or clears updates on a group of Cache Groups.
+        *
+        * @param cgs The Cache Groups on which to operate.
+        * @param queue Whether updates should be queued (`true`) or cleared
+        * (`false`).
+        */
+       private async queueUpdates(cgs: ResponseCacheGroup[], queue: boolean = 
true): Promise<void> {
+               const title = `${queue ? "Queue" : "Clear"} Updates on 
${cgs.length === 1 ? cgs[0].name : `${cgs.length} Cache Groups`}`;
+               const data = {
+                       collection: (await this.cdns).map(c => ({label: c.name, 
value: c.id})),
+                       hint: "Note that 'ALL' does NOT mean 'all CDNs'!",
+                       label: "CDN",
+                       message: `Select a CDN to which to limit the ${queue ? 
"Queuing" : "Clearing"} of Updates.`,
+                       title,
+               };
+               const ref = this.dialog.open<CollectionChoiceDialogComponent, 
CollectionChoiceDialogData<number>, number | false>(
+                       CollectionChoiceDialogComponent,
+                       {data}
+               );
+               const result = await ref.afterClosed().toPromise();
+               if (typeof(result) === "number") {
+                       const responses = await Promise.all(cgs.map(async cg => 
this.api.queueCacheGroupUpdates(cg, result)));
+                       const serverNum = responses.map(r => 
r.serverNames.length).reduce((n, l) => n+l, 0);
+                       // This endpoint returns no alerts at the time of this 
writing, so
+                       // we gotta do it by hand.
+                       this.alerts.newAlert(
+                               AlertLevel.SUCCESS,
+                               `${queue ? "Queued" : "Cleared"} Updates on 
${serverNum} server${serverNum === 1 ? "" : "s"}`
+                       );
+               }
+       }
+
+       /**
+        * Asks the user for confirmation before deleting a Cache Group.
+        *
+        * @param cg The Cache Group (potentially) being deleted.
+        */
+       private async delete(cg: ResponseCacheGroup): Promise<void> {
+               const ref = this.dialog.open<DecisionDialogComponent, 
DecisionDialogData, boolean>(DecisionDialogComponent, {
+                       data: {
+                               message: `Are you sure you want to delete the 
${cg.name} Cache Group?`,
+                               title: `Delete ${cg.name}`
+                       }
+               });
+               if (await ref.afterClosed().toPromise()) {
+                       this.api.deleteCacheGroup(cg);

Review Comment:
   Should probably be awaited



##########
experimental/traffic-portal/src/app/core/cache-groups/cache-group-details/cache-group-details.component.html:
##########
@@ -0,0 +1,87 @@
+<!--
+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>
+       <tp-loading *ngIf="!cacheGroup"></tp-loading>
+       <form ngNativeValidate (ngSubmit)="submit($event)" *ngIf="cacheGroup">
+               <mat-card-content>
+                       <mat-form-field>
+                               <mat-label>Name</mat-label>
+                               <input matInput type="text" name="name" 
required [(ngModel)]="cacheGroup.name" required/>

Review Comment:
   Double required



##########
experimental/traffic-portal/src/app/core/cache-groups/cache-group-table/cache-group-table.component.spec.ts:
##########
@@ -58,4 +95,288 @@ describe("CacheGroupTableComponent", () => {
        it("doesn't throw errors when handling context menu events", () => {
                expect(()=>component.handleContextMenu({action: "something", 
data: []})).not.toThrow();
        });
+
+       it("renders parent cache group cells", () => {
+               const col = component.columnDefs.find(d => d.field === 
"parentCachegroupName");
+               if (!col) {
+                       return fail("parentCachegroupName column not found");
+               }
+               const {valueFormatter} = col;
+               if (typeof(valueFormatter) !== "function") {
+                       return fail(`invalid valueFormatter found on 
parentCachegroupName column definition: ${valueFormatter}`);
+               }
+               let value = valueFormatter({data: sampleCG} as 
ValueFormatterParams);
+               expect(value).toBe("");
+               value = valueFormatter({data: {...sampleCG, parentCachegroupId: 
1, parentCachegroupName: "sample"}} as ValueFormatterParams);
+               expect(value).toBe("sample (#1)");
+       });
+       it("renders secondary parent cache group cells", () => {
+               const col = component.columnDefs.find(d => d.field === 
"secondaryParentCachegroupName");
+               if (!col) {
+                       return fail("secondaryParentCachegroupName column not 
found");
+               }
+               const {valueFormatter} = col;
+               if (typeof(valueFormatter) !== "function") {
+                       return fail(`invalid valueFormatter found on 
secondaryParentCachegroupName column definition: ${valueFormatter}`);
+               }
+               let value = valueFormatter({data: sampleCG} as 
ValueFormatterParams);
+               expect(value).toBe("");
+               value = valueFormatter({
+                       data: {
+                               ...sampleCG,
+                               secondaryParentCachegroupId: 1,
+                               secondaryParentCachegroupName: "sample"
+                       }
+               } as ValueFormatterParams);
+               expect(value).toBe("sample (#1)");
+       });
+       it("renders type cells", () => {
+               const col = component.columnDefs.find(d => d.field === 
"typeName");
+               if (!col) {
+                       return fail("type column not found");
+               }
+               const {valueFormatter} = col;
+               if (typeof(valueFormatter) !== "function") {
+                       return fail(`invalid valueFormatter found on type 
column definition: ${valueFormatter}`);
+               }
+               const value = valueFormatter({data: {...sampleCG, typeId: 1, 
typeName: "sample"}} as ValueFormatterParams);
+               expect(value).toBe("sample (#1)");
+       });
+       it("renders localization methods cells", () => {
+               const col = component.columnDefs.find(d => d.field === 
"localizationMethods");
+               if (!col) {
+                       return fail("localizationMethods column not found");
+               }
+               const {valueGetter} = col;
+               if (typeof(valueGetter) !== "function") {
+                       return fail(`invalid valueGetter found on 
localizationMethods column definition: ${valueGetter}`);
+               }
+               let value: string = valueGetter({data: {...sampleCG, 
localizationMethods: []}} as ValueGetterParams);
+               let valueArr = value.split(", ");
+               expect(valueArr.length).toBe(3);
+               
expect(valueArr).toContain(localizationMethodToString(LocalizationMethod.CZ));
+               
expect(valueArr).toContain(localizationMethodToString(LocalizationMethod.DEEP_CZ));
+               
expect(valueArr).toContain(localizationMethodToString(LocalizationMethod.GEO));
+               value = valueGetter({data: {...sampleCG, localizationMethods: 
[LocalizationMethod.CZ]}} as ValueGetterParams);
+               valueArr = value.split(", ");
+               expect(valueArr.length).toBe(1);
+               
expect(valueArr).toContain(localizationMethodToString(LocalizationMethod.CZ));
+       });
+
+       it("has context menu links to individual Cache Groups", () => {
+               let menuItem = component.contextMenuItems.find(i => i.name === 
"Open in New Tab");
+               if (!menuItem) {
+                       return fail("'Open in New Tab' context menu item not 
found");
+               }
+               if (isAction(menuItem) || typeof(menuItem.href) !== "function") 
{
+                       return fail(`invalid 'Open in New Tab' context menu 
item; either not a link or has a static href: ${menuItem}`);
+               }
+               expect(menuItem.newTab).toBeTrue();
+               expect(menuItem.href({...sampleCG, id: 
5})).toBe("core/cache-groups/5");
+
+               menuItem = component.contextMenuItems.find(i => i.name === 
"Edit");
+               if (!menuItem) {
+                       return fail("'Edit' context menu item not found");
+               }
+               if (isAction(menuItem) || typeof(menuItem.href) !== "function") 
{
+                       return fail(`invalid 'Edit' context menu item; either 
not a link or has a static href: ${menuItem}`);
+               }
+               expect(menuItem.newTab).toBeFalsy();
+               expect(menuItem.href({...sampleCG, id: 
5})).toBe("core/cache-groups/5");
+       });
+       it("doesn't allow selection of unimplemented context menu items", () => 
{
+               let menuItem = component.contextMenuItems.find(i => i.name === 
"Manage ASNs");
+               if (!menuItem) {
+                       return fail("'Manage ASNs' context menu item not 
found");
+               }
+               if (!isAction(menuItem)) {
+                       return fail(`Invalid 'Manage ASNs' context menu item; 
not an action: ${menuItem}`);
+               }
+               if (typeof(menuItem.disabled) !== "function") {
+                       return fail("'Manage ASNs' context menu item should be 
disabled, but no disabled function is defined");
+               }
+               if (menuItem.multiRow) {
+                       expect(menuItem.disabled([sampleCG])).toBeTrue();
+               } else {
+                       expect(menuItem.disabled(sampleCG)).toBeTrue();
+               }
+
+               menuItem = component.contextMenuItems.find(i => i.name === 
"Manage Servers");
+               if (!menuItem) {
+                       return fail("'Manage Servers' context menu item not 
found");
+               }
+               if (!isAction(menuItem)) {
+                       return fail(`Invalid 'Manage Servers' context menu 
item; not an action: ${menuItem}`);
+               }
+               if (typeof(menuItem.disabled) !== "function") {
+                       return fail("'Manage Servers' context menu item should 
be disabled, but no disabled function is defined");
+               }
+               if (menuItem.multiRow) {
+                       expect(menuItem.disabled([sampleCG])).toBeTrue();
+               } else {
+                       expect(menuItem.disabled(sampleCG)).toBeTrue();
+               }
+
+               menuItem = component.contextMenuItems.find(i => i.name === 
"Manage Parameters");
+               if (!menuItem) {
+                       return fail("'Manage Parameters' context menu item not 
found");
+               }
+               if (!isAction(menuItem)) {
+                       return fail(`Invalid 'Manage Parameters' context menu 
item; not an action: ${menuItem}`);
+               }
+               if (typeof(menuItem.disabled) !== "function") {
+                       return fail("'Manage Parameters' context menu item 
should be disabled, but no disabled function is defined");
+               }
+               if (menuItem.multiRow) {
+                       expect(menuItem.disabled([sampleCG])).toBeTrue();
+               } else {
+                       expect(menuItem.disabled(sampleCG)).toBeTrue();
+               }
+       });
+
+       it("initializes from query string parameters", fakeAsync(() => {
+               const router = TestBed.inject(Router);
+               router.navigate([], {queryParams: {search: "testquest"}});
+               component.ngOnInit();
+               tick();
+               
expectAsync(component.fuzzySubject.toPromise()).toBeResolvedTo("testquest");
+       }));
+
+       it("deletes Cache Groups", async () => {
+               expect(() => component.handleContextMenu({action: "delete", 
data: []})).not.toThrow();
+               let dialogs = await loader.getAllHarnesses(MatDialogHarness);
+               expect(dialogs.length).toBe(0);
+
+               const cgSrv = TestBed.inject(CacheGroupService);
+               const spy = spyOn(cgSrv, "deleteCacheGroup");
+
+               component.handleContextMenu({action: "delete", data: sampleCG});
+               dialogs = await loader.getAllHarnesses(MatDialogHarness);
+               if (dialogs.length !== 1) {
+                       return fail(`dialog should have opened for deleting, 
actual number of dialogs: ${dialogs.length}`);
+               }
+               let dialog = dialogs[0];
+               dialog.close();

Review Comment:
   Awaitable



-- 
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