Skip to content

Commit

Permalink
Fix row highligh & Improve metrics summary view (#4270)
Browse files Browse the repository at this point in the history
* Fix row highlight (see app github tab commit table)
- wasn't showing after list in list changes
- fixes #4243

* Improve metrics view

* Fix code climate issues

* Fix 2 more code climate issues

* Fix merge issue

* Fix dark mode box shadow
- dark mode styling overode disabling of box shadow

* Fix multiple metrics requests due to recreated list cards

* Fix most additional /stratos requests on load of endpoints page with metrics

* Fix unit tests

* Fix EndpointCardComponent test

* Fix root list colour in metrics summary page dark mode

* Fix first row alignment in users table

* Fix invite user e2e test (see #4272)

* Disable random route override setting when deploying apps in e2e tests
- pushes route over max 63 chars

Co-authored-by: Neil MacDougall <[email protected]>
  • Loading branch information
richard-cox and nwmac authored May 8, 2020
1 parent 5edc001 commit 91861f2
Show file tree
Hide file tree
Showing 22 changed files with 494 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@
align-items: center;
display: flex;
justify-content: center;
width: 32px;
margin-right: 8px;
}
}
4 changes: 3 additions & 1 deletion src/frontend/packages/core/sass/_all-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
@import '../../cloud-foundry/src/features/applications/application-wall/application-wall.component.theme';
@import '../../core/src/features/error-page/error-page/error-page.component.theme';
@import '../../core/src/features/endpoints/backup-restore/restore-endpoints/restore-endpoints.component.theme';
@import '../../core/src/features/metrics/metrics/metrics.component.theme';

// Defaults
$side-nav-light-text: #fff;
Expand Down Expand Up @@ -160,7 +161,8 @@ $side-nav-light-active: #484848;
@include code-block-theme($theme, $app-theme);
@include copy-to-clipboard-theme($theme, $app-theme);
@include app-user-avatar-theme($theme, $app-theme);
@include restore-endpoints-theme($theme, $app-theme)
@include restore-endpoints-theme($theme, $app-theme);
@include metrics-component-theme($theme, $app-theme);
}

@function app-generate-nav-theme($theme, $nav-theme: null) {
Expand Down
10 changes: 7 additions & 3 deletions src/frontend/packages/core/src/base-entity-types.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import { systemEndpointsReducer } from '../../store/src/reducers/system-endpoints.reducer';
import { StratosCatalogEndpointEntity, StratosCatalogEntity } from '../../store/src/entity-catalog/entity-catalog-entity';
import {
addOrUpdateUserFavoriteMetadataReducer,
deleteUserFavoriteMetadataReducer,
} from '../../store/src/reducers/favorite.reducer';
import { systemEndpointsReducer } from '../../store/src/reducers/system-endpoints.reducer';
import {
endpointEntitySchema,
STRATOS_ENDPOINT_TYPE,
systemInfoEntitySchema,
userFavoritesEntitySchema,
userProfileEntitySchema,
} from './base-entity-schemas';
import { StratosCatalogEndpointEntity, StratosCatalogEntity } from '../../store/src/entity-catalog/entity-catalog-entity';
import { BaseEndpointAuth } from './features/endpoints/endpoint-auth';
import {
MetricsEndpointDetailsComponent,
} from './features/metrics/metrics-endpoint-details/metrics-endpoint-details.component';

//
// These types are used to represent the base stratos types.
Expand Down Expand Up @@ -94,7 +97,8 @@ export function generateStratosEntities() {
tokenSharing: true,
logoUrl: '/core/assets/endpoint-icons/metrics.svg',
authTypes: [BaseEndpointAuth.UsernamePassword, BaseEndpointAuth.None],
renderPriority: 1
renderPriority: 1,
listDetailsComponent: MetricsEndpointDetailsComponent,
},
metadata => `/endpoints/metrics/${metadata.guid}`
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="metrics-details" *ngIf="data$ | async as info">
<div class="metrics-details__line">
<mat-icon *ngIf="info.warning" class="metrics-details__icon text-warning">warning</mat-icon>
{{ info.ok }}
<ng-container *ngIf="info.total > 0"> / {{info.total}}</ng-container>
<ng-container *ngIf="info.plural"> sources</ng-container>
<ng-container *ngIf="!info.plural"> source</ng-container>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
.metrics-details {
display: flex;
flex-direction: column;

&__line {
align-items: center;
display: flex;
}

&__icon {
align-items: center;
display: flex;
justify-content: center;
margin-right: 8px;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { createBasicStoreModule } from '../../../../../store/testing/src/store-test-helper';
import { CoreTestingModule } from '../../../../test-framework/core-test.modules';
import { SharedModule } from '../../../shared/shared.module';
import { MetricsService } from '../services/metrics-service';
import { CoreModule } from './../../../core/core.module';
import { MetricsEndpointDetailsComponent } from './metrics-endpoint-details.component';

describe('MetricsEndpointDetailsComponent', () => {
let component: MetricsEndpointDetailsComponent;
let fixture: ComponentFixture<MetricsEndpointDetailsComponent>;

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule,
SharedModule,
CoreTestingModule,
createBasicStoreModule()
],
declarations: [ MetricsEndpointDetailsComponent ],
providers: [ MetricsService ]
})
.compileComponents();
}));

beforeEach(() => {
fixture = TestBed.createComponent(MetricsEndpointDetailsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { Component, Input } from '@angular/core';
import { Store } from '@ngrx/store';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { distinctUntilChanged, filter, map, publishReplay, refCount, tap } from 'rxjs/operators';

import { MetricsStratosAction } from '../../../../../store/src/actions/metrics-api.actions';
import { AppState } from '../../../../../store/src/app-state';
import { EndpointListDetailsComponent } from '../../../shared/components/list/list-types/endpoint/endpoint-list.helpers';
import { mapMetricsData } from '../metrics.helpers';
import { MetricsEndpointProvider, MetricsService } from '../services/metrics-service';
import { EndpointModel } from './../../../../../store/src/types/endpoint.types';


interface MetricsDetailsInfo {
ok: number;
total: number;
warning: boolean;
plural: boolean;
}

@Component({
selector: 'app-metrics-endpoint-details',
templateUrl: './metrics-endpoint-details.component.html',
styleUrls: ['./metrics-endpoint-details.component.scss']
})
export class MetricsEndpointDetailsComponent extends EndpointListDetailsComponent {

data$: Observable<MetricsDetailsInfo>;

// The guid of the metrics endpoint that this row shows
guid$ = new BehaviorSubject<string>(null);

constructor(
public store: Store<AppState>,
private metricsService: MetricsService
) {
super();

const endpoints$ = this.metricsService.metricsEndpoints$.pipe(
filter(endpoints => !!endpoints),
distinctUntilChanged()
);

const guid$ = this.guid$.asObservable().pipe(
filter(guid => !!guid),
distinctUntilChanged()
);

// Raw endpoint data for this metrics endpoint
this.data$ = combineLatest(
endpoints$,
guid$
).pipe(
map(([endpoints, guid]) => endpoints.find((item) => item.provider.guid === guid)),
filter(provider => !!provider),
tap(data => {
if (!this.hasStratosData(data)) {
this.store.dispatch(new MetricsStratosAction(data.provider.guid));
}
}),
map((provider) => this.processProvider(provider)),
publishReplay(1),
refCount()
);
}

private hasStratosData(provider: MetricsEndpointProvider): boolean {
const data = provider.provider;
return !!data && !!data.metadata && !!data.metadata.metrics_stratos;
}

private processProvider(provider: MetricsEndpointProvider): MetricsDetailsInfo {
const hasStratosData = this.hasStratosData(provider);
const parsed = mapMetricsData(provider);
const known = parsed.filter(item => item.known).length;
return {
ok: known,
total: hasStratosData ? parsed.length : -1,
warning: known === 0,
plural: hasStratosData ? parsed.length !== 1 : known !== 1,
};
}

@Input()
set row(data: EndpointModel) {
this.guid$.next(data.guid);
}
}
74 changes: 74 additions & 0 deletions src/frontend/packages/core/src/features/metrics/metrics.helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { Observable, of as observableOf } from 'rxjs';

import { StratosStatus } from '../../shared/shared.types';
import { EndpointIcon, getFullEndpointApiUrl } from '../endpoints/endpoint-helpers';
import { entityCatalog } from './../../../../store/src/entity-catalog/entity-catalog.service';
import { MetricsEndpointProvider } from './services/metrics-service';

// Info for an endpoint that a metrics endpoint provides for
export interface MetricsEndpointInfo {
name: string;
icon: EndpointIcon;
type: string;
known: boolean;
url: string;
metadata: {
metrics_job?: string;
metrics_environment?: string;
};
status: Observable<StratosStatus>;
}

// Process the endpoint and Stratos marker file data to give a single list of endpoitns
// linked to this metrics endpoint, comprising those that are known in Stratos and those that are not
export function mapMetricsData(ep: MetricsEndpointProvider): MetricsEndpointInfo[] {
const data: MetricsEndpointInfo[] = [];

// Add all of the known endpoints first
ep.endpoints.forEach(endpoint => {
const catalogEndpoint = entityCatalog.getEndpoint(endpoint.cnsi_type, endpoint.sub_type);

data.push({
known: true,
name: endpoint.name,
url: getFullEndpointApiUrl(endpoint),
type: catalogEndpoint.definition.label,
icon: {
name: catalogEndpoint.definition.icon,
font: 'stratos-icons'
},
metadata: {
metrics_job: endpoint.metadata ? endpoint.metadata.metrics_job : null,
metrics_environment: endpoint.metadata ? endpoint.metadata.metrics_environment : null
},
status: observableOf(StratosStatus.OK)
});
});

// Add all of the potentially unknown endpoints
if (ep.provider && ep.provider.metadata && ep.provider.metadata && ep.provider.metadata.metrics_stratos
&& Array.isArray(ep.provider.metadata.metrics_stratos)) {
ep.provider.metadata.metrics_stratos.forEach(endp => {
// See if we already know about this endpoint
const hasEndpoint = data.findIndex(i => i.url === endp.url || i.url === endp.cfEndpoint) !== -1;
if (!hasEndpoint) {
const catalogEndpoint = entityCatalog.getEndpoint(endp.type, '');
data.push({
known: false,
name: '<Unregistered Endpoint>',
url: endp.cfEndpoint || endp.url,
type: catalogEndpoint.definition.label,
icon: {
name: catalogEndpoint.definition.icon,
font: 'stratos-icons'
},
metadata: {
metrics_job: endp.job
},
status: observableOf(StratosStatus.WARNING)
});
}
});
}
return data;
}
15 changes: 10 additions & 5 deletions src/frontend/packages/core/src/features/metrics/metrics.module.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MetricsComponent } from './metrics/metrics.component';
import { MetricsRoutingModule } from './metrics.routing';
import { MetricsService } from './services/metrics-service';
import { NgModule } from '@angular/core';

import { CoreModule } from '../../core/core.module';
import { SharedModule } from '../../shared/shared.module';
import { MetricsEndpointDetailsComponent } from './metrics-endpoint-details/metrics-endpoint-details.component';
import { MetricsRoutingModule } from './metrics.routing';
import { MetricsComponent } from './metrics/metrics.component';
import { MetricsService } from './services/metrics-service';

@NgModule({
imports: [
Expand All @@ -13,9 +15,12 @@ import { SharedModule } from '../../shared/shared.module';
SharedModule,
MetricsRoutingModule,
],
declarations: [MetricsComponent],
declarations: [MetricsComponent, MetricsEndpointDetailsComponent],
providers: [
MetricsService,
],
entryComponents: [
MetricsEndpointDetailsComponent,
]
})
export class MetricsModule { }
Loading

0 comments on commit 91861f2

Please sign in to comment.