Skip to content
This repository has been archived by the owner on Jan 24, 2023. It is now read-only.

Fix row highligh & Improve metrics summary view #4270

Merged
merged 19 commits into from
May 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
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
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