diff --git a/src/frontend/app/features/endpoints/endpoint-helpers.ts b/src/frontend/app/features/endpoints/endpoint-helpers.ts index a5a7d3c55c..9f521d677f 100644 --- a/src/frontend/app/features/endpoints/endpoint-helpers.ts +++ b/src/frontend/app/features/endpoints/endpoint-helpers.ts @@ -1,6 +1,12 @@ +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { first, map } from 'rxjs/operators'; import { Validators } from '@angular/forms'; import { urlValidationExpression } from '../../core/utils.service'; +import { AppState } from '../../store/app-state'; +import { endpointSchemaKey } from '../../store/helpers/entity-factory'; +import { selectEntities } from '../../store/selectors/api.selectors'; import { EndpointModel, EndpointType } from './../../store/types/endpoint.types'; export function getFullEndpointApiUrl(endpoint: EndpointModel) { @@ -109,6 +115,13 @@ export function getIconForEndpoint(type: string): EndpointIcon { return icon; } +export function endpointHasMetrics(endpointGuid: string, store: Store): Observable { + return store.select(selectEntities(endpointSchemaKey)).pipe( + first(), + map(state => !!state[endpointGuid].metadata && !!state[endpointGuid].metadata.metrics) + ); +} + export function getEndpointAuthTypes() { return endpointAuthTypes; } diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/cf-app-instances-config.service.ts b/src/frontend/app/shared/components/list/list-types/app-instance/cf-app-instances-config.service.ts index 4a10c99bd9..56f357b95e 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/cf-app-instances-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/app-instance/cf-app-instances-config.service.ts @@ -25,6 +25,14 @@ import { CfAppInstancesDataSource } from './cf-app-instances-data-source'; import { TableCellCfCellComponent } from './table-cell-cf-cell/table-cell-cf-cell.component'; import { TableCellUsageComponent } from './table-cell-usage/table-cell-usage.component'; +export function createAppInstancesMetricAction(appGuid: string, cfGuid: string): FetchApplicationMetricsAction { + return new FetchApplicationMetricsAction( + appGuid, + cfGuid, + new MetricQueryConfig('firehose_container_metric_cpu_percentage'), + MetricQueryType.QUERY + ); +} @Injectable() export class CfAppInstancesConfigService implements IListConfig { @@ -215,12 +223,7 @@ export class CfAppInstancesConfigService implements IListConfig getInitialised = () => this.initialised$; private createMetricsResults(entityServiceFactory: EntityServiceFactory) { - const metricsAction = new FetchApplicationMetricsAction( - this.appService.appGuid, - this.appService.cfGuid, - new MetricQueryConfig('firehose_container_metric_cpu_percentage'), - MetricQueryType.QUERY - ); + const metricsAction = createAppInstancesMetricAction(this.appService.appGuid, this.appService.cfGuid); return entityServiceFactory.create>>( metricSchemaKey, entityFactory(metricSchemaKey), @@ -229,5 +232,4 @@ export class CfAppInstancesConfigService implements IListConfig false ); } - } diff --git a/src/frontend/app/store/effects/app.effects.ts b/src/frontend/app/store/effects/app.effects.ts index 9e54583919..0ce65b99fe 100644 --- a/src/frontend/app/store/effects/app.effects.ts +++ b/src/frontend/app/store/effects/app.effects.ts @@ -1,10 +1,15 @@ import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; import { Store } from '@ngrx/store'; -import { map } from 'rxjs/operators'; +import { first, map } from 'rxjs/operators'; +import { endpointHasMetrics } from '../../features/endpoints/endpoint-helpers'; +import { + createAppInstancesMetricAction, +} from '../../shared/components/list/list-types/app-instance/cf-app-instances-config.service'; import { GetAppSummaryAction } from '../actions/app-metadata.actions'; -import { ASSIGN_ROUTE, AssociateRouteWithAppApplication, ASSIGN_ROUTE_SUCCESS } from '../actions/application-service-routes.actions'; +import { ASSIGN_ROUTE_SUCCESS } from '../actions/application-service-routes.actions'; +import { UPDATE_SUCCESS, UpdateExistingApplication } from '../actions/application.actions'; import { AppState } from '../app-state'; import { APISuccessOrFailedAction } from '../types/request.types'; @@ -17,10 +22,27 @@ export class AppEffects { private store: Store, ) { } - @Effect({ dispatch: false }) upateSummary$ = this.actions$.ofType(ASSIGN_ROUTE_SUCCESS).pipe( + @Effect({ dispatch: false }) updateSummary$ = this.actions$.ofType(ASSIGN_ROUTE_SUCCESS).pipe( map(action => { - this.store.dispatch(new GetAppSummaryAction(action.apiAction.guid, action.apiAction.endpointGuid)); + this.store.dispatch(new GetAppSummaryAction(action.apiAction.guid, action.apiAction.endpointGuid)); }), + ); + @Effect({ dispatch: false }) clearCellMetrics$ = this.actions$.ofType(UPDATE_SUCCESS).pipe( + map(action => { + // User's can scale down instances and previous instance data is kept in store, when the user scales up again this stale data can + // be incorrectly shown straight away. In order to work around this fetch the latest metrics again when scaling up + // Note - If this happens within the metrics update time period (60 seconds) the stale one is returned again, unfortunately there's + // no way to work around this. + const updateAction: UpdateExistingApplication = action.apiAction as UpdateExistingApplication; + if (updateAction.newApplication.instances > updateAction.existingApplication.instances) { + // First check that we have a metrics endpoint associated with this cf + endpointHasMetrics(updateAction.endpointGuid, this.store).pipe(first()).subscribe(hasMetrics => { + if (hasMetrics) { + this.store.dispatch(createAppInstancesMetricAction(updateAction.guid, updateAction.endpointGuid)); + } + }); + } + }), ); } diff --git a/src/frontend/app/store/reducers/routes.reducer.ts b/src/frontend/app/store/reducers/routes.reducer.ts index 77aa5fc3b2..97e6b1327e 100644 --- a/src/frontend/app/store/reducers/routes.reducer.ts +++ b/src/frontend/app/store/reducers/routes.reducer.ts @@ -1,10 +1,9 @@ -import { AppState, IRequestEntityTypeState } from '../app-state'; -import { Action } from '@ngrx/store'; +import { IAppSummary, IRoute } from '../../core/cf-api.types'; +import { ASSIGN_ROUTE_SUCCESS, AssociateRouteWithAppApplication } from '../actions/application-service-routes.actions'; +import { DeleteRoute, RouteEvents, UnmapRoute } from '../actions/route.actions'; +import { IRequestEntityTypeState } from '../app-state'; import { APIResource } from '../types/api.types'; -import { RouteEvents, UnmapRoute, DeleteRoute } from '../actions/route.actions'; import { APISuccessOrFailedAction } from '../types/request.types'; -import { IRoute, IAppSummary } from '../../core/cf-api.types'; -import { ASSIGN_ROUTE_SUCCESS, AssociateRouteWithAppApplication } from '../actions/application-service-routes.actions'; export function routeReducer(state: IRequestEntityTypeState>, action: APISuccessOrFailedAction) { switch (action.type) {