From ec705fe410af6c7fe43b61d26e38d742d583c0cd Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Mon, 3 Sep 2018 16:17:39 +0100 Subject: [PATCH 001/114] Added .realize.yaml file --- src/jetstream/.realize.yaml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 src/jetstream/.realize.yaml diff --git a/src/jetstream/.realize.yaml b/src/jetstream/.realize.yaml new file mode 100755 index 0000000000..91fbdac962 --- /dev/null +++ b/src/jetstream/.realize.yaml @@ -0,0 +1,21 @@ +settings: + legacy: + force: false + interval: 0s +schema: +- name: jetstream + path: . + commands: + run: + status: true + install: + status: false + watcher: + extensions: + - go + paths: + - / + ignored_paths: + - .git + - .realize + - vendor From 955ba58926f1aefe00409e6995e9af7d88e82e68 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 4 Sep 2018 11:21:20 +0100 Subject: [PATCH 002/114] Added range_query to allow time window querying --- .../application-instance-chart.component.ts | 15 ++++++---- .../metrics-tab/metrics-tab.component.html | 28 +++++-------------- .../tabs/metrics-tab/metrics-tab.component.ts | 6 +--- .../metrics-chart/metrics-chart.component.ts | 12 ++++---- .../app/store/actions/metrics.actions.ts | 16 ++++++----- .../app/store/effects/metrics.effects.ts | 4 +-- src/jetstream/.realize.yaml | 2 +- .../plugins/metrics/cloud_foundry.go | 5 ++-- 8 files changed, 38 insertions(+), 50 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts index 4210f73b36..2b8df073fa 100644 --- a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts +++ b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, Input } from '@angular/core'; import { MetricsLineChartConfig } from '../../../../shared/components/metrics-chart/metrics-chart.types'; import { MetricsConfig } from '../../../../shared/components/metrics-chart/metrics-chart.component'; import { IMetricMatrixResult } from '../../../../store/types/base-metric.types'; -import { FetchApplicationMetricsAction } from '../../../../store/actions/metrics.actions'; +import { FetchApplicationMetricsAction, MetricQueryType } from '../../../../store/actions/metrics.actions'; import { MetricsChartHelpers } from '../../../../shared/components/metrics-chart/metrics.component.helpers'; import { IMetricApplication } from '../../../../store/types/metric.types'; @@ -22,12 +22,16 @@ export class ApplicationInstanceChartComponent implements OnInit { @Input() private yAxisLabel: string; + // Prometheus query string @Input() - private metricName: string; + private queryString: string; @Input() private seriesTranslation: string; + @Input() + private queryRange = false; + @Input() public title: string; @@ -50,16 +54,17 @@ export class ApplicationInstanceChartComponent implements OnInit { getSeriesName: result => `Instance ${result.metric.instance_index}`, mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, sort: MetricsChartHelpers.sortBySeriesName, - mapSeriesItemValue: this.getmapSeriesItemValue(), + mapSeriesItemValue: this.mapSeriesItemValue(), metricsAction: new FetchApplicationMetricsAction( this.appGuid, this.endpointGuid, - this.metricName, + this.queryString, + this.queryRange ? MetricQueryType.RANGE_QUERY : MetricQueryType.QUERY ), }; } - private getmapSeriesItemValue() { + private mapSeriesItemValue() { switch (this.seriesTranslation) { case 'mb': return (bytes) => bytes / 1000000; diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html index d5da45b0b7..3bfeb589e7 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html @@ -1,29 +1,15 @@ - + - + - + - + \ No newline at end of file diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.ts b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.ts index 427e77dc58..172e998650 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.ts +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.ts @@ -1,8 +1,4 @@ -import { Component, OnInit } from '@angular/core'; -import { MetricsLineChartConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.types'; -import { MetricsConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.component'; -import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; -import { FetchApplicationMetricsAction } from '../../../../../../store/actions/metrics.actions'; +import { Component } from '@angular/core'; import { ApplicationService } from '../../../../application.service'; @Component({ diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 6d972f1f62..689d3223cf 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -65,12 +65,12 @@ export class MetricsChartComponent implements OnInit, OnDestroy { }) ); this.store.dispatch(this.metricsConfig.metricsAction); - this.pollSub = metricsMonitor.poll( - 30000, - () => this.store.dispatch(this.metricsConfig.metricsAction), - request => ({ busy: request.fetching, error: request.error, message: request.message }) - ) - .subscribe(); + this.pollSub = metricsMonitor + .poll( + 30000, + () => this.store.dispatch(this.metricsConfig.metricsAction), + request => ({ busy: request.fetching, error: request.error, message: request.message }) + ).subscribe(); } ngOnDestroy() { diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index c919c26444..e2cc8f7d17 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -1,20 +1,22 @@ -import { environment } from './../../../environments/environment.prod'; -import { schema } from 'normalizr'; import { Action } from '@ngrx/store'; +import { environment } from './../../../environments/environment.prod'; export const METRICS_START = '[Metrics] Start'; export const METRICS_START_SUCCESS = '[Metrics] Start succeeded'; export const METRICS_START_FAILED = '[Metrics] Start failed'; const { proxyAPIVersion } = environment; +export enum MetricQueryType { + QUERY = 'query', + RANGE_QUERY = 'query_range' +} + export abstract class MetricsAction implements Action { - constructor(guid: string, query: string) { + constructor(public guid: string, public query: string, public queryType: MetricQueryType = MetricQueryType.QUERY) { this.metricId = MetricsAction.buildMetricKey(guid, query); } type = METRICS_START; url: string; - query: string; - guid: string; cfGuid: string; metricId: string; static getBaseMetricsURL() { @@ -37,8 +39,8 @@ export class FetchCFMetricsAction extends MetricsAction { } export class FetchApplicationMetricsAction extends MetricsAction { - constructor(public guid: string, public cfGuid: string, public query: string) { - super(guid, query); + constructor(guid: string, public cfGuid: string, public query: string, queryType: MetricQueryType = MetricQueryType.QUERY) { + super(guid, query, queryType); this.url = `${MetricsAction.getBaseMetricsURL()}/cf/app/${guid}`; } } diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index 36dba5da8c..b84a88e564 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -1,5 +1,5 @@ -import {catchError, mergeMap, map } from 'rxjs/operators'; +import { catchError, mergeMap, map } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; @@ -59,7 +59,7 @@ export class MetricsEffect { })); private buildFullUrl(action: MetricsAction) { - return `${action.url}/query?query=${action.query}`; + return `${action.url}/${action.queryType}?query=${action.query}`; } } diff --git a/src/jetstream/.realize.yaml b/src/jetstream/.realize.yaml index 91fbdac962..8064d567ed 100755 --- a/src/jetstream/.realize.yaml +++ b/src/jetstream/.realize.yaml @@ -14,7 +14,7 @@ schema: extensions: - go paths: - - / + - / ignored_paths: - .git - .realize diff --git a/src/jetstream/plugins/metrics/cloud_foundry.go b/src/jetstream/plugins/metrics/cloud_foundry.go index d647ccb3f6..b48b84d9e2 100644 --- a/src/jetstream/plugins/metrics/cloud_foundry.go +++ b/src/jetstream/plugins/metrics/cloud_foundry.go @@ -28,8 +28,8 @@ func (m *MetricsSpecification) getCloudFoundryAppMetrics(c echo.Context) error { } // For an application, we only support the query operation - if prometheusOp != "query" { - return errors.New("Only 'query' is supported for a Cloud Foundry application") + if prometheusOp != "query" && prometheusOp != "query_range" { + return errors.New("Only 'query' or 'query_range' is supported for a Cloud Foundry application") } // Now make the metrics requests to the appropriate metrics endpoint @@ -78,7 +78,6 @@ func makePrometheusRequestInfos(c echo.Context, userGUID string, metrics map[str req.URI = makePrometheusRequestURI(c, prometheusOp, addQueries) requests = append(requests, req) } - return requests } From a1dfc2dd4336b5ace954cfaab7e5127e37b1c719 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 4 Sep 2018 17:01:36 +0100 Subject: [PATCH 003/114] WIP Start end date selector for metrics charts --- src/frontend/app/core/md.module.ts | 12 ++- .../date-time/date-time.component.html | 10 +++ .../date-time/date-time.component.scss | 0 .../date-time/date-time.component.spec.ts | 25 ++++++ .../date-time/date-time.component.ts | 83 +++++++++++++++++++ .../metrics-chart.component.html | 8 +- .../start-end-date.component.html | 3 + .../start-end-date.component.scss | 0 .../start-end-date.component.spec.ts | 25 ++++++ .../start-end-date.component.ts | 15 ++++ src/frontend/app/shared/shared.module.ts | 4 + 11 files changed, 181 insertions(+), 4 deletions(-) create mode 100644 src/frontend/app/shared/components/date-time/date-time.component.html create mode 100644 src/frontend/app/shared/components/date-time/date-time.component.scss create mode 100644 src/frontend/app/shared/components/date-time/date-time.component.spec.ts create mode 100644 src/frontend/app/shared/components/date-time/date-time.component.ts create mode 100644 src/frontend/app/shared/components/start-end-date/start-end-date.component.html create mode 100644 src/frontend/app/shared/components/start-end-date/start-end-date.component.scss create mode 100644 src/frontend/app/shared/components/start-end-date/start-end-date.component.spec.ts create mode 100644 src/frontend/app/shared/components/start-end-date/start-end-date.component.ts diff --git a/src/frontend/app/core/md.module.ts b/src/frontend/app/core/md.module.ts index 6e8d9fcb88..3e5d37dd3a 100644 --- a/src/frontend/app/core/md.module.ts +++ b/src/frontend/app/core/md.module.ts @@ -2,6 +2,7 @@ import 'hammerjs'; import { CommonModule } from '@angular/common'; import { NgModule } from '@angular/core'; +import { MAT_MOMENT_DATE_FORMATS, MomentDateAdapter } from '@angular/material-moment-adapter'; import { MatAutocompleteModule, MatButtonModule, @@ -31,6 +32,10 @@ import { MatToolbarModule, MatTooltipModule, MatRadioModule, + MatDatepickerModule, + DateAdapter, + MAT_DATE_LOCALE, + MAT_DATE_FORMATS, } from '@angular/material'; const importExport = [ @@ -62,11 +67,16 @@ const importExport = [ MatMenuModule, MatSnackBarModule, MatListModule, - MatRadioModule + MatRadioModule, + MatDatepickerModule ]; @NgModule({ imports: importExport, exports: importExport, + providers: [ + { provide: DateAdapter, useClass: MomentDateAdapter, deps: [MAT_DATE_LOCALE] }, + { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS } + ], }) export class MDAppModule { } diff --git a/src/frontend/app/shared/components/date-time/date-time.component.html b/src/frontend/app/shared/components/date-time/date-time.component.html new file mode 100644 index 0000000000..8c32ee45f8 --- /dev/null +++ b/src/frontend/app/shared/components/date-time/date-time.component.html @@ -0,0 +1,10 @@ +
+ + + + + + + + +
\ No newline at end of file diff --git a/src/frontend/app/shared/components/date-time/date-time.component.scss b/src/frontend/app/shared/components/date-time/date-time.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/app/shared/components/date-time/date-time.component.spec.ts b/src/frontend/app/shared/components/date-time/date-time.component.spec.ts new file mode 100644 index 0000000000..ef377ce7fd --- /dev/null +++ b/src/frontend/app/shared/components/date-time/date-time.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DateTimeComponent } from './date-time.component'; + +describe('DateTimeComponent', () => { + let component: DateTimeComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DateTimeComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DateTimeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/shared/components/date-time/date-time.component.ts b/src/frontend/app/shared/components/date-time/date-time.component.ts new file mode 100644 index 0000000000..f44a71c8ec --- /dev/null +++ b/src/frontend/app/shared/components/date-time/date-time.component.ts @@ -0,0 +1,83 @@ +import { SetupModule } from './../../../features/setup/setup.module'; +import { Component, OnInit, Output, OnDestroy, Input, EventEmitter } from '@angular/core'; +import { FormControl } from '@angular/forms'; +import * as moment from 'moment'; +import { tap, map, filter } from 'rxjs/operators'; +import { combineLatest, Subscription } from 'rxjs'; + +@Component({ + selector: 'app-date-time', + templateUrl: './date-time.component.html', + styleUrls: ['./date-time.component.scss'] +}) +export class DateTimeComponent implements OnInit, OnDestroy { + + public date = new FormControl(); + public time = new FormControl(); + private sub: Subscription; + private changeSub: Subscription; + private dateTimeValue: moment.Moment; + + @Output() + public dateTimeChange = new EventEmitter(); + + @Input() + get dateTime() { + return this.dateTimeValue; + } + + set dateTime(dateTime: moment.Moment) { + console.log(dateTime, this.dateTimeValue); + if (!this.dateTimeValue || !dateTime.isSame(this.dateTimeValue)) { + console.log('!same') + this.dateTimeValue = dateTime; + this.dateTimeChange.emit(this.dateTime); + } + } + + private setupInputSub() { + this.sub = combineLatest( + this.time.valueChanges, + this.date.valueChanges, + ).pipe( + map(([time, date]: [string, moment.Moment]) => { + console.log(time, date); + const [hour, minute] = time.split(':'); + return [ + parseInt(hour, 10), + parseInt(minute, 10), + date + ]; + }), + filter(([hour, minute]: [number, number, moment.Moment]) => { + return !isNaN(hour + minute); + }), + tap(([hour, minute, date]: [number, number, moment.Moment]) => { + debugger; + this.dateTime = date.set({ + hour, + minute + }); + }) + ).subscribe(); + } + + private setupChangeSub() { + this.changeSub = this.dateTimeChange.pipe( + tap((dateTime: moment.Moment) => { + this.date.setValue(dateTime); + console.log(dateTime.format('HH:MM')); + this.time.setValue(dateTime.format('HH:MM')); + }) + ).subscribe(); + } + + ngOnInit() { + this.setupInputSub(); + this.setupChangeSub(); + } + ngOnDestroy() { + this.sub.unsubscribe(); + this.changeSub.unsubscribe(); + } +} diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 342799e8c8..0dae7e145a 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,11 +1,13 @@

{{title}}

+ + {{dateTime}}
- + {{ chartConfig.chartType }} chart type not found
-
+ \ No newline at end of file diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.html b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html new file mode 100644 index 0000000000..d1c724e5a5 --- /dev/null +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html @@ -0,0 +1,3 @@ +

+ start-end-date works! +

diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss b/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.spec.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.spec.ts new file mode 100644 index 0000000000..b1af5f7b2c --- /dev/null +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { StartEndDateComponent } from './start-end-date.component'; + +describe('StartEndDateComponent', () => { + let component: StartEndDateComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ StartEndDateComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(StartEndDateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts new file mode 100644 index 0000000000..d58b00fab9 --- /dev/null +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-start-end-date', + templateUrl: './start-end-date.component.html', + styleUrls: ['./start-end-date.component.scss'] +}) +export class StartEndDateComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/frontend/app/shared/shared.module.ts b/src/frontend/app/shared/shared.module.ts index d34a8a754f..6bb54869af 100644 --- a/src/frontend/app/shared/shared.module.ts +++ b/src/frontend/app/shared/shared.module.ts @@ -133,6 +133,8 @@ import { UserPermissionDirective } from './user-permission.directive'; import { CfEndpointsMissingComponent } from './components/cf-endpoints-missing/cf-endpoints-missing.component'; import { CapitalizeFirstPipe } from './pipes/capitalizeFirstLetter.pipe'; import { RoutingIndicatorComponent } from './components/routing-indicator/routing-indicator.component'; +import { DateTimeComponent } from './components/date-time/date-time.component'; +import { StartEndDateComponent } from './components/start-end-date/start-end-date.component'; @NgModule({ imports: [ @@ -236,6 +238,8 @@ import { RoutingIndicatorComponent } from './components/routing-indicator/routin CfEndpointsMissingComponent, CapitalizeFirstPipe, RoutingIndicatorComponent, + DateTimeComponent, + StartEndDateComponent, ], exports: [ FormsModule, From 79ccad11473de0d15cd07399bc7f191181ce38f5 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Wed, 5 Sep 2018 10:11:26 +0100 Subject: [PATCH 004/114] Fixed datetime selector --- .../app/shared/components/date-time/date-time.component.ts | 7 +------ .../components/metrics-chart/metrics-chart.component.ts | 4 ++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/frontend/app/shared/components/date-time/date-time.component.ts b/src/frontend/app/shared/components/date-time/date-time.component.ts index f44a71c8ec..1d87292847 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.ts +++ b/src/frontend/app/shared/components/date-time/date-time.component.ts @@ -27,9 +27,7 @@ export class DateTimeComponent implements OnInit, OnDestroy { } set dateTime(dateTime: moment.Moment) { - console.log(dateTime, this.dateTimeValue); if (!this.dateTimeValue || !dateTime.isSame(this.dateTimeValue)) { - console.log('!same') this.dateTimeValue = dateTime; this.dateTimeChange.emit(this.dateTime); } @@ -41,7 +39,6 @@ export class DateTimeComponent implements OnInit, OnDestroy { this.date.valueChanges, ).pipe( map(([time, date]: [string, moment.Moment]) => { - console.log(time, date); const [hour, minute] = time.split(':'); return [ parseInt(hour, 10), @@ -53,8 +50,7 @@ export class DateTimeComponent implements OnInit, OnDestroy { return !isNaN(hour + minute); }), tap(([hour, minute, date]: [number, number, moment.Moment]) => { - debugger; - this.dateTime = date.set({ + this.dateTime = date.clone().set({ hour, minute }); @@ -66,7 +62,6 @@ export class DateTimeComponent implements OnInit, OnDestroy { this.changeSub = this.dateTimeChange.pipe( tap((dateTime: moment.Moment) => { this.date.setValue(dateTime); - console.log(dateTime.format('HH:MM')); this.time.setValue(dateTime.format('HH:MM')); }) ).subscribe(); diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 689d3223cf..48e79f4273 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -11,6 +11,8 @@ import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.se import { MetricsChartTypes } from './metrics-chart.types'; import { MetricsChartManager } from './metrics.component.manager'; +import * as moment from 'moment'; + export interface MetricsConfig { metricsAction: MetricsAction; getSeriesName: (T) => string; @@ -40,6 +42,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy { public chartTypes = MetricsChartTypes; + public dateTime = moment(moment.now()); + private pollSub: Subscription; public results$; From 6549b1b016fe8c71b8073a45edafe2e4026ff1a5 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Wed, 5 Sep 2018 11:51:05 +0100 Subject: [PATCH 005/114] Added start-end component --- .../date-time/date-time.component.html | 7 +++-- .../date-time/date-time.component.scss | 3 ++ .../metrics-chart.component.html | 3 +- .../metrics-chart/metrics-chart.component.ts | 3 +- .../start-end-date.component.html | 16 ++++++++-- .../start-end-date.component.scss | 19 ++++++++++++ .../start-end-date.component.ts | 31 +++++++++++++++++-- 7 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/frontend/app/shared/components/date-time/date-time.component.html b/src/frontend/app/shared/components/date-time/date-time.component.html index 8c32ee45f8..b6669ac71b 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.html +++ b/src/frontend/app/shared/components/date-time/date-time.component.html @@ -1,10 +1,11 @@ -
- + + - + + access_time \ No newline at end of file diff --git a/src/frontend/app/shared/components/date-time/date-time.component.scss b/src/frontend/app/shared/components/date-time/date-time.component.scss index e69de29bb2..742417a1c0 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.scss +++ b/src/frontend/app/shared/components/date-time/date-time.component.scss @@ -0,0 +1,3 @@ +.date-time-form { + display: flex; +} diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 0dae7e145a..b35ef2255b 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,7 +1,6 @@

{{title}}

- - {{dateTime}} +
diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 48e79f4273..780f247466 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -42,7 +42,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy { public chartTypes = MetricsChartTypes; - public dateTime = moment(moment.now()); + public from = moment(moment.now()); + public to = moment(moment.now()); private pollSub: Subscription; diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.html b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html index d1c724e5a5..0bcd91a76c 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.html +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html @@ -1,3 +1,13 @@ -

- start-end-date works! -

+
+
+
Start Date
+ +
+
+ arrow_forward +
+
+
End Date
+ +
+
\ No newline at end of file diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss b/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss index e69de29bb2..e22db1ce7b 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss @@ -0,0 +1,19 @@ +.start-end-date { + align-items: flex-end; + display: flex; + max-width: 800px; + &__selector { + flex: auto; + padding: 0 5px; + } + &__header { + font-size: 14px; + font-weight: bold; + opacity: .6; + } + &__arrow { + // Setting padding bottom to match the input fields + padding: 0 20px 1.25em; + color: rgba(0, 0, 0, 0.54); + } +} diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts index d58b00fab9..334dad897a 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -1,4 +1,5 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, Output, EventEmitter, Input } from '@angular/core'; +import * as moment from 'moment'; @Component({ selector: 'app-start-end-date', @@ -6,10 +7,36 @@ import { Component, OnInit } from '@angular/core'; styleUrls: ['./start-end-date.component.scss'] }) export class StartEndDateComponent implements OnInit { + @Output() + public endChange = new EventEmitter(); + @Output() + public startChange = new EventEmitter(); + + private startValue: moment.Moment; + private endValue: moment.Moment; + + @Input() + set start(start: moment.Moment) { + this.startValue = start; + this.startChange.emit(start); + } + + get start() { + return this.startValue; + } + + @Input() + set end(end: moment.Moment) { + this.endValue = end; + this.endChange.emit(end); + } + + get end() { + return this.endValue; + } constructor() { } ngOnInit() { } - } From 67f3d52fdc1f8f5d57fec1d15c42d267dae09a2e Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Wed, 5 Sep 2018 17:25:52 +0100 Subject: [PATCH 006/114] WIP Wire in date-time compoent to charts --- .../metrics-tab/metrics-tab.component.html | 4 +- .../date-time/date-time.component.ts | 20 +++++-- .../metrics-chart/metrics-chart.component.ts | 57 ++++++++++++++++--- .../start-end-date.component.html | 4 +- .../start-end-date.component.ts | 25 ++++---- .../app/store/actions/metrics.actions.ts | 2 +- 6 files changed, 84 insertions(+), 28 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html index 3bfeb589e7..12aa9ab275 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html @@ -3,7 +3,7 @@ yAxisLabel="CPU Usage (%)" queryRange="true" queryString="firehose_container_metric_cpu_percentage&start=1535989577.05&end=1536011177.05&step=8"> - + \ No newline at end of file diff --git a/src/frontend/app/shared/components/date-time/date-time.component.ts b/src/frontend/app/shared/components/date-time/date-time.component.ts index 1d87292847..e48b397879 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.ts +++ b/src/frontend/app/shared/components/date-time/date-time.component.ts @@ -10,7 +10,7 @@ import { combineLatest, Subscription } from 'rxjs'; templateUrl: './date-time.component.html', styleUrls: ['./date-time.component.scss'] }) -export class DateTimeComponent implements OnInit, OnDestroy { +export class DateTimeComponent implements OnDestroy { public date = new FormControl(); public time = new FormControl(); @@ -29,15 +29,20 @@ export class DateTimeComponent implements OnInit, OnDestroy { set dateTime(dateTime: moment.Moment) { if (!this.dateTimeValue || !dateTime.isSame(this.dateTimeValue)) { this.dateTimeValue = dateTime; - this.dateTimeChange.emit(this.dateTime); + this.dateTimeChange.emit(this.dateTimeValue); } } + private isDifferentDate(oldDate: moment.Moment, newDate: moment.Moment) { + return !oldDate || !oldDate.isSame(newDate); + } + private setupInputSub() { this.sub = combineLatest( this.time.valueChanges, this.date.valueChanges, ).pipe( + filter(([time, date]) => time && date), map(([time, date]: [string, moment.Moment]) => { const [hour, minute] = time.split(':'); return [ @@ -50,27 +55,32 @@ export class DateTimeComponent implements OnInit, OnDestroy { return !isNaN(hour + minute); }), tap(([hour, minute, date]: [number, number, moment.Moment]) => { - this.dateTime = date.clone().set({ + const newDate = date.clone().set({ hour, minute }); + if (this.isDifferentDate(this.dateTime, newDate)) { + this.dateTime = newDate; + } }) ).subscribe(); } private setupChangeSub() { this.changeSub = this.dateTimeChange.pipe( - tap((dateTime: moment.Moment) => { + filter(dateTime => !!dateTime), + tap(dateTime => { this.date.setValue(dateTime); this.time.setValue(dateTime.format('HH:MM')); }) ).subscribe(); } - ngOnInit() { + constructor() { this.setupInputSub(); this.setupChangeSub(); } + ngOnDestroy() { this.sub.unsubscribe(); this.changeSub.unsubscribe(); diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 780f247466..bb6b174da2 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,9 +1,9 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit, OnChanges, SimpleChanges } from '@angular/core'; import { Store } from '@ngrx/store'; import { filter, map } from 'rxjs/operators'; import { Subscription } from 'rxjs'; -import { MetricsAction } from '../../../store/actions/metrics.actions'; +import { MetricsAction, MetricQueryType, FetchCFMetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types/base-metric.types'; @@ -32,7 +32,7 @@ export interface MetricsChartConfig { templateUrl: './metrics-chart.component.html', styleUrls: ['./metrics-chart.component.scss'] }) -export class MetricsChartComponent implements OnInit, OnDestroy { +export class MetricsChartComponent implements OnInit, OnDestroy, OnChanges { @Input() public metricsConfig: MetricsConfig; @Input() @@ -42,11 +42,16 @@ export class MetricsChartComponent implements OnInit, OnDestroy { public chartTypes = MetricsChartTypes; - public from = moment(moment.now()); - public to = moment(moment.now()); + @Input() + public start: moment.Moment; + + @Input() + public end: moment.Moment; private pollSub: Subscription; + private commit: Function = null; + public results$; constructor( private store: Store, @@ -54,8 +59,17 @@ export class MetricsChartComponent implements OnInit, OnDestroy { ) { } ngOnInit() { + this.start = moment(moment.now()); + this.end = moment(moment.now()); + this.setup(this.metricsConfig.metricsAction); + } + + private setup(action: MetricsAction) { + if (this.pollSub) { + this.pollSub.unsubscribe(); + } const metricsMonitor = this.entityMonitorFactory.create( - this.metricsConfig.metricsAction.metricId, + action.metricId, metricSchemaKey, entityFactory(metricSchemaKey) ); @@ -69,11 +83,13 @@ export class MetricsChartComponent implements OnInit, OnDestroy { return metricsArray; }) ); - this.store.dispatch(this.metricsConfig.metricsAction); + this.store.dispatch(action); this.pollSub = metricsMonitor .poll( 30000, - () => this.store.dispatch(this.metricsConfig.metricsAction), + () => { + this.store.dispatch(action); + }, request => ({ busy: request.fetching, error: request.error, message: request.message }) ).subscribe(); } @@ -93,6 +109,31 @@ export class MetricsChartComponent implements OnInit, OnDestroy { default: throw new Error(`Could not find chart data mapper for metrics type ${metrics.resultType}`); } + } + private getCommitFn(action: MetricsAction) { + return () => { + console.log('commiting') + this.setup(action); + }; + } + + ngOnChanges(changes: SimpleChanges) { + console.log(changes) + if (changes.start || changes.end) { + const startMs = (changes.start.currentValue as moment.Moment).valueOf(); + const endMs = (changes.end.currentValue as moment.Moment).valueOf(); + const rangeParm = `&start=${startMs}&end=${endMs}`; + const oldAction = this.metricsConfig.metricsAction; + const action = new FetchCFMetricsAction( + oldAction.guid, + this.metricsConfig.metricsAction.query + rangeParm, + MetricQueryType.RANGE_QUERY + ); + this.commit = this.getCommitFn(action); + this.commit(); + } else { + this.commit = null; + } } } diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.html b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html index 0bcd91a76c..c92cc119b7 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.html +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html @@ -1,13 +1,13 @@
-
Start Date
+
Start
arrow_forward
-
End Date
+
End
\ No newline at end of file diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts index 334dad897a..b14971813b 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -6,7 +6,7 @@ import * as moment from 'moment'; templateUrl: './start-end-date.component.html', styleUrls: ['./start-end-date.component.scss'] }) -export class StartEndDateComponent implements OnInit { +export class StartEndDateComponent { @Output() public endChange = new EventEmitter(); @Output() @@ -15,10 +15,17 @@ export class StartEndDateComponent implements OnInit { private startValue: moment.Moment; private endValue: moment.Moment; + private isDifferentDate(oldDate: moment.Moment, newDate: moment.Moment) { + return !oldDate || !oldDate.isSame(newDate); + } + @Input() set start(start: moment.Moment) { - this.startValue = start; - this.startChange.emit(start); + if (this.isDifferentDate(this.startValue, start)) { + const clone = start.clone(); + this.startValue = clone; + this.startChange.emit(this.startValue); + } } get start() { @@ -27,16 +34,14 @@ export class StartEndDateComponent implements OnInit { @Input() set end(end: moment.Moment) { - this.endValue = end; - this.endChange.emit(end); + if (this.isDifferentDate(this.endValue, end)) { + const clone = end.clone(); + this.endValue = clone; + this.endChange.emit(this.endValue); + } } get end() { return this.endValue; } - - constructor() { } - - ngOnInit() { - } } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index e2cc8f7d17..1add9bcce5 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -31,7 +31,7 @@ export abstract class MetricsAction implements Action { export class FetchCFMetricsAction extends MetricsAction { public cfGuid: string; - constructor(public guid: string, public query: string) { + constructor(public guid: string, public query: string, queryType: MetricQueryType = MetricQueryType.QUERY) { super(guid, query); this.cfGuid = guid; this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; From 456bafb87d03de67c4329c834033df8d1f7eaa7d Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 6 Sep 2018 11:53:24 +0100 Subject: [PATCH 007/114] WIP Getting date-time into mertrics chart --- .../components/date-time/date-time.component.ts | 13 ++++++++++++- .../metrics-chart/metrics-chart.component.html | 3 ++- .../start-end-date/start-end-date.component.ts | 5 +++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/frontend/app/shared/components/date-time/date-time.component.ts b/src/frontend/app/shared/components/date-time/date-time.component.ts index e48b397879..fe282fb7e8 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.ts +++ b/src/frontend/app/shared/components/date-time/date-time.component.ts @@ -38,12 +38,14 @@ export class DateTimeComponent implements OnDestroy { } private setupInputSub() { + this.stopInputSub(); this.sub = combineLatest( this.time.valueChanges, this.date.valueChanges, ).pipe( filter(([time, date]) => time && date), map(([time, date]: [string, moment.Moment]) => { + const [hour, minute] = time.split(':'); return [ parseInt(hour, 10), @@ -55,6 +57,7 @@ export class DateTimeComponent implements OnDestroy { return !isNaN(hour + minute); }), tap(([hour, minute, date]: [number, number, moment.Moment]) => { + const newDate = date.clone().set({ hour, minute @@ -66,12 +69,20 @@ export class DateTimeComponent implements OnDestroy { ).subscribe(); } + private stopInputSub() { + if (this.sub) { + this.sub.unsubscribe(); + } + } + private setupChangeSub() { this.changeSub = this.dateTimeChange.pipe( filter(dateTime => !!dateTime), tap(dateTime => { + this.stopInputSub(); this.date.setValue(dateTime); this.time.setValue(dateTime.format('HH:MM')); + this.setupInputSub(); }) ).subscribe(); } @@ -82,7 +93,7 @@ export class DateTimeComponent implements OnDestroy { } ngOnDestroy() { - this.sub.unsubscribe(); + this.stopInputSub(); this.changeSub.unsubscribe(); } } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index b35ef2255b..bea6dd4569 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,6 +1,7 @@

{{title}}

- + + {{start}} {{end}}
diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts index b14971813b..58b5de212d 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -22,7 +22,8 @@ export class StartEndDateComponent { @Input() set start(start: moment.Moment) { if (this.isDifferentDate(this.startValue, start)) { - const clone = start.clone(); + const clone = moment(start); + console.log(this.startValue === clone) this.startValue = clone; this.startChange.emit(this.startValue); } @@ -35,7 +36,7 @@ export class StartEndDateComponent { @Input() set end(end: moment.Moment) { if (this.isDifferentDate(this.endValue, end)) { - const clone = end.clone(); + const clone = moment(end); this.endValue = clone; this.endChange.emit(this.endValue); } From 0ef84a1b93fc84828610452085a4353526dd3c84 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 7 Sep 2018 12:26:44 +0100 Subject: [PATCH 008/114] Fixed metrics chart datetime selectors --- .../metrics-tab/metrics-tab.component.html | 10 ++-- .../date-time/date-time.component.ts | 37 +++++++++--- .../metrics-chart.component.html | 1 - .../metrics-chart/metrics-chart.component.ts | 60 +++++++++++++++---- .../start-end-date.component.ts | 6 +- .../app/store/actions/metrics.actions.ts | 2 +- 6 files changed, 89 insertions(+), 27 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html index 12aa9ab275..ca70406595 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html @@ -1,15 +1,15 @@ + yAxisLabel="CPU Usage (%)" queryRange="true" queryString="firehose_container_metric_cpu_percentage"> - \ No newline at end of file + \ No newline at end of file diff --git a/src/frontend/app/shared/components/date-time/date-time.component.ts b/src/frontend/app/shared/components/date-time/date-time.component.ts index fe282fb7e8..f91cca9af4 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.ts +++ b/src/frontend/app/shared/components/date-time/date-time.component.ts @@ -2,8 +2,8 @@ import { SetupModule } from './../../../features/setup/setup.module'; import { Component, OnInit, Output, OnDestroy, Input, EventEmitter } from '@angular/core'; import { FormControl } from '@angular/forms'; import * as moment from 'moment'; -import { tap, map, filter } from 'rxjs/operators'; -import { combineLatest, Subscription } from 'rxjs'; +import { tap, map, filter, shareReplay } from 'rxjs/operators'; +import { combineLatest, Subscription, Observable } from 'rxjs'; @Component({ selector: 'app-date-time', @@ -18,6 +18,9 @@ export class DateTimeComponent implements OnDestroy { private changeSub: Subscription; private dateTimeValue: moment.Moment; + private dateObservable: Observable; + private timeObservable: Observable; + @Output() public dateTimeChange = new EventEmitter(); @@ -40,12 +43,11 @@ export class DateTimeComponent implements OnDestroy { private setupInputSub() { this.stopInputSub(); this.sub = combineLatest( - this.time.valueChanges, - this.date.valueChanges, + this.timeObservable, + this.dateObservable ).pipe( - filter(([time, date]) => time && date), + filter(([time, date]) => !!(time && date)), map(([time, date]: [string, moment.Moment]) => { - const [hour, minute] = time.split(':'); return [ parseInt(hour, 10), @@ -63,12 +65,20 @@ export class DateTimeComponent implements OnDestroy { minute }); if (this.isDifferentDate(this.dateTime, newDate)) { + this.stopChangeSub(); this.dateTime = newDate; + this.setupChangeSub(); } }) ).subscribe(); } + private replayObservable(obs: Observable) { + return obs.pipe( + shareReplay(1) + ); + } + private stopInputSub() { if (this.sub) { this.sub.unsubscribe(); @@ -76,18 +86,31 @@ export class DateTimeComponent implements OnDestroy { } private setupChangeSub() { + this.stopChangeSub(); this.changeSub = this.dateTimeChange.pipe( filter(dateTime => !!dateTime), tap(dateTime => { this.stopInputSub(); this.date.setValue(dateTime); - this.time.setValue(dateTime.format('HH:MM')); + this.time.setValue(dateTime.format('HH:mm')); this.setupInputSub(); }) ).subscribe(); } + private stopChangeSub() { + if (this.changeSub) { + this.changeSub.unsubscribe(); + } + } + constructor() { + this.dateObservable = this.replayObservable( + this.date.valueChanges + ); + this.timeObservable = this.replayObservable( + this.time.valueChanges + ); this.setupInputSub(); this.setupChangeSub(); } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index bea6dd4569..51629a0fbe 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,7 +1,6 @@

{{title}}

- {{start}} {{end}}
diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index bb6b174da2..fba2204830 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -3,7 +3,7 @@ import { Store } from '@ngrx/store'; import { filter, map } from 'rxjs/operators'; import { Subscription } from 'rxjs'; -import { MetricsAction, MetricQueryType, FetchCFMetricsAction } from '../../../store/actions/metrics.actions'; +import { MetricsAction, MetricQueryType, FetchCFMetricsAction, FetchApplicationMetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types/base-metric.types'; @@ -42,26 +42,67 @@ export class MetricsChartComponent implements OnInit, OnDestroy, OnChanges { public chartTypes = MetricsChartTypes; - @Input() - public start: moment.Moment; - - @Input() - public end: moment.Moment; + private startEnd: [moment.Moment, moment.Moment] = [null, null]; private pollSub: Subscription; private commit: Function = null; public results$; + + private readonly startIndex = 0; + private readonly endIndex = 1; + + private commitDate(date: moment.Moment, type: 'start' | 'end') { + const index = type === 'start' ? this.startIndex : this.endIndex; + const oldDate = this.startEnd[index]; + if (oldDate && date.isSame(oldDate)) { + return; + } + this.startEnd[index] = date; + const [start, end] = this.startEnd; + if (start && end) { + const startUnix = start.unix(); + const endUnix = end.unix(); + const rangeParm = `&start=${startUnix}&end=${endUnix}&step=345`; + const oldAction = this.metricsConfig.metricsAction; + const action = new FetchApplicationMetricsAction( + oldAction.guid, + oldAction.cfGuid, + this.metricsConfig.metricsAction.query + rangeParm, + MetricQueryType.RANGE_QUERY + ); + this.commit = this.getCommitFn(action); + this.commit(); + } + } + + set start(start: moment.Moment) { + this.commitDate(start, 'start'); + } + + get start() { + return this.startEnd[this.startIndex]; + } + + set end(end: moment.Moment) { + this.commitDate(end, 'end'); + } + + get end() { + return this.startEnd[this.endIndex]; + } + constructor( private store: Store, private entityMonitorFactory: EntityMonitorFactory ) { } ngOnInit() { - this.start = moment(moment.now()); - this.end = moment(moment.now()); - this.setup(this.metricsConfig.metricsAction); + const now = moment(moment.now()); + this.start = moment(now).subtract(1, 'weeks'); + this.end = now; + // this.setup(this.metricsConfig.metricsAction); } private setup(action: MetricsAction) { @@ -113,7 +154,6 @@ export class MetricsChartComponent implements OnInit, OnDestroy, OnChanges { private getCommitFn(action: MetricsAction) { return () => { - console.log('commiting') this.setup(action); }; } diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts index 58b5de212d..35f9bc9717 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -23,9 +23,9 @@ export class StartEndDateComponent { set start(start: moment.Moment) { if (this.isDifferentDate(this.startValue, start)) { const clone = moment(start); - console.log(this.startValue === clone) + console.log(clone === this.startValue); this.startValue = clone; - this.startChange.emit(this.startValue); + this.startChange.emit(clone); } } @@ -38,7 +38,7 @@ export class StartEndDateComponent { if (this.isDifferentDate(this.endValue, end)) { const clone = moment(end); this.endValue = clone; - this.endChange.emit(this.endValue); + this.endChange.emit(clone); } } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 1add9bcce5..65c144f8ee 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -32,7 +32,7 @@ export abstract class MetricsAction implements Action { export class FetchCFMetricsAction extends MetricsAction { public cfGuid: string; constructor(public guid: string, public query: string, queryType: MetricQueryType = MetricQueryType.QUERY) { - super(guid, query); + super(guid, query, queryType); this.cfGuid = guid; this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; } From dffe6ee11555975ad8fea2276b271df9c01b46d6 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 7 Sep 2018 14:51:44 +0100 Subject: [PATCH 009/114] Added finer grain query params to later show better graphs --- .../application-instance-chart.component.ts | 4 +- .../metrics-chart.component.html | 5 +- .../metrics-chart/metrics-chart.component.ts | 74 ++++++++----------- .../start-end-date.component.ts | 1 - .../app/store/actions/metrics.actions.ts | 41 ++++++++-- .../app/store/effects/metrics.effects.ts | 8 +- 6 files changed, 73 insertions(+), 60 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts index 2b8df073fa..4e3a743513 100644 --- a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts +++ b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, Input } from '@angular/core'; import { MetricsLineChartConfig } from '../../../../shared/components/metrics-chart/metrics-chart.types'; import { MetricsConfig } from '../../../../shared/components/metrics-chart/metrics-chart.component'; import { IMetricMatrixResult } from '../../../../store/types/base-metric.types'; -import { FetchApplicationMetricsAction, MetricQueryType } from '../../../../store/actions/metrics.actions'; +import { FetchApplicationMetricsAction, MetricQueryType, MetricQueryConfig } from '../../../../store/actions/metrics.actions'; import { MetricsChartHelpers } from '../../../../shared/components/metrics-chart/metrics.component.helpers'; import { IMetricApplication } from '../../../../store/types/metric.types'; @@ -58,7 +58,7 @@ export class ApplicationInstanceChartComponent implements OnInit { metricsAction: new FetchApplicationMetricsAction( this.appGuid, this.endpointGuid, - this.queryString, + new MetricQueryConfig(this.queryString), this.queryRange ? MetricQueryType.RANGE_QUERY : MetricQueryType.QUERY ), }; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 51629a0fbe..98ead19f09 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,12 +1,13 @@

{{title}}

- + -
+
{{ chartConfig.chartType }} chart type not found
+ No results found
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index fba2204830..aef956dc60 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,17 +1,18 @@ -import { Component, Input, OnDestroy, OnInit, OnChanges, SimpleChanges } from '@angular/core'; +import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; -import { filter, map } from 'rxjs/operators'; +import * as moment from 'moment'; import { Subscription } from 'rxjs'; - -import { MetricsAction, MetricQueryType, FetchCFMetricsAction, FetchApplicationMetricsAction } from '../../../store/actions/metrics.actions'; +import { filter, map } from 'rxjs/operators'; +import { FetchApplicationMetricsAction, MetricQueryType, MetricsAction, MetricQueryConfig } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; +import { EntityMonitor } from '../../monitors/entity-monitor'; import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types/base-metric.types'; import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.service'; import { MetricsChartTypes } from './metrics-chart.types'; import { MetricsChartManager } from './metrics.component.manager'; -import * as moment from 'moment'; + export interface MetricsConfig { metricsAction: MetricsAction; @@ -32,7 +33,7 @@ export interface MetricsChartConfig { templateUrl: './metrics-chart.component.html', styleUrls: ['./metrics-chart.component.scss'] }) -export class MetricsChartComponent implements OnInit, OnDestroy, OnChanges { +export class MetricsChartComponent implements OnInit, OnDestroy { @Input() public metricsConfig: MetricsConfig; @Input() @@ -52,6 +53,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy, OnChanges { private readonly startIndex = 0; private readonly endIndex = 1; + public metricsMonitor: EntityMonitor; private commitDate(date: moment.Moment, type: 'start' | 'end') { const index = type === 'start' ? this.startIndex : this.endIndex; @@ -62,14 +64,17 @@ export class MetricsChartComponent implements OnInit, OnDestroy, OnChanges { this.startEnd[index] = date; const [start, end] = this.startEnd; if (start && end) { - const startUnix = start.unix(); - const endUnix = end.unix(); - const rangeParm = `&start=${startUnix}&end=${endUnix}&step=345`; + const startUnix = start.unix() + ''; + const endUnix = end.unix() + ''; const oldAction = this.metricsConfig.metricsAction; const action = new FetchApplicationMetricsAction( oldAction.guid, oldAction.cfGuid, - this.metricsConfig.metricsAction.query + rangeParm, + new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { + start: startUnix, + end: endUnix, + step: '365' + }), MetricQueryType.RANGE_QUERY ); this.commit = this.getCommitFn(action); @@ -99,33 +104,35 @@ export class MetricsChartComponent implements OnInit, OnDestroy, OnChanges { ) { } ngOnInit() { - const now = moment(moment.now()); - this.start = moment(now).subtract(1, 'weeks'); - this.end = now; - // this.setup(this.metricsConfig.metricsAction); - } - - private setup(action: MetricsAction) { - if (this.pollSub) { - this.pollSub.unsubscribe(); - } - const metricsMonitor = this.entityMonitorFactory.create( - action.metricId, + this.metricsMonitor = this.entityMonitorFactory.create( + this.metricsConfig.metricsAction.metricId, metricSchemaKey, entityFactory(metricSchemaKey) ); - this.results$ = metricsMonitor.entity$.pipe( + this.results$ = this.metricsMonitor.entity$.pipe( filter(metrics => !!metrics), map(metrics => { const metricsArray = this.mapMetricsToChartData(metrics, this.metricsConfig); + if (!metricsArray.length) { + return null; + } if (this.metricsConfig.sort) { metricsArray.sort(this.metricsConfig.sort); } return metricsArray; }) ); + const now = moment(moment.now()); + this.start = moment(now).subtract(1, 'weeks'); + this.end = now; + } + + private setup(action: MetricsAction) { + if (this.pollSub) { + this.pollSub.unsubscribe(); + } this.store.dispatch(action); - this.pollSub = metricsMonitor + this.pollSub = this.metricsMonitor .poll( 30000, () => { @@ -157,23 +164,4 @@ export class MetricsChartComponent implements OnInit, OnDestroy, OnChanges { this.setup(action); }; } - - ngOnChanges(changes: SimpleChanges) { - console.log(changes) - if (changes.start || changes.end) { - const startMs = (changes.start.currentValue as moment.Moment).valueOf(); - const endMs = (changes.end.currentValue as moment.Moment).valueOf(); - const rangeParm = `&start=${startMs}&end=${endMs}`; - const oldAction = this.metricsConfig.metricsAction; - const action = new FetchCFMetricsAction( - oldAction.guid, - this.metricsConfig.metricsAction.query + rangeParm, - MetricQueryType.RANGE_QUERY - ); - this.commit = this.getCommitFn(action); - this.commit(); - } else { - this.commit = null; - } - } } diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts index 35f9bc9717..e905741deb 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -23,7 +23,6 @@ export class StartEndDateComponent { set start(start: moment.Moment) { if (this.isDifferentDate(this.startValue, start)) { const clone = moment(start); - console.log(clone === this.startValue); this.startValue = clone; this.startChange.emit(clone); } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 65c144f8ee..f27a9c7e5b 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -1,18 +1,43 @@ import { Action } from '@ngrx/store'; import { environment } from './../../../environments/environment.prod'; -export const METRICS_START = '[Metrics] Start'; -export const METRICS_START_SUCCESS = '[Metrics] Start succeeded'; -export const METRICS_START_FAILED = '[Metrics] Start failed'; +export const METRICS_START = '[Metrics] Fetch Start'; +export const METRICS_START_SUCCESS = '[Metrics] Fetch Succeeded'; +export const METRICS_START_FAILED = '[Metrics] Fetch Failed'; const { proxyAPIVersion } = environment; export enum MetricQueryType { QUERY = 'query', RANGE_QUERY = 'query_range' } +export interface IMetricQueryConfigParams { + window?: string; + [key: string]: string; +} +export class MetricQueryConfig { + constructor( + public metric: string, + public params?: IMetricQueryConfigParams + ) { } + public getFullQuery() { + return this.metric + this.joinParams(); + } + + public joinParams() { + const { + window = '', + step = '365', + ...params + } = this.params; + const paramString = Object.keys(params).reduce((accum, key) => { + return accum + `&${key}=${params[key]}`; + }, ''); + return (window + paramString) || ''; + } +} export abstract class MetricsAction implements Action { - constructor(public guid: string, public query: string, public queryType: MetricQueryType = MetricQueryType.QUERY) { + constructor(public guid: string, public query: MetricQueryConfig, public queryType: MetricQueryType = MetricQueryType.QUERY) { this.metricId = MetricsAction.buildMetricKey(guid, query); } type = METRICS_START; @@ -24,14 +49,14 @@ export abstract class MetricsAction implements Action { } // Builds the key that is used to store the metric in the app state. - static buildMetricKey(guid: string, query: string) { - return `${guid}:${query}`; + static buildMetricKey(guid: string, query: MetricQueryConfig) { + return `${guid}:${query.metric}`; } } export class FetchCFMetricsAction extends MetricsAction { public cfGuid: string; - constructor(public guid: string, public query: string, queryType: MetricQueryType = MetricQueryType.QUERY) { + constructor(public guid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { super(guid, query, queryType); this.cfGuid = guid; this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; @@ -39,7 +64,7 @@ export class FetchCFMetricsAction extends MetricsAction { } export class FetchApplicationMetricsAction extends MetricsAction { - constructor(guid: string, public cfGuid: string, public query: string, queryType: MetricQueryType = MetricQueryType.QUERY) { + constructor(guid: string, public cfGuid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { super(guid, query, queryType); this.url = `${MetricsAction.getBaseMetricsURL()}/cf/app/${guid}`; } diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index b84a88e564..eafbde941c 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -9,7 +9,7 @@ import { METRICS_START, MetricsAction } from '../actions/metrics.actions'; import { AppState } from '../app-state'; import { metricSchemaKey } from '../helpers/entity-factory'; import { IMetricsResponse } from '../types/base-metric.types'; -import { IRequestAction, WrapperRequestActionFailed, WrapperRequestActionSuccess } from './../types/request.types'; +import { IRequestAction, WrapperRequestActionFailed, WrapperRequestActionSuccess, StartRequestAction } from './../types/request.types'; @Injectable() @@ -28,14 +28,14 @@ export class MetricsEffect { guid: action.guid, entityKey: metricSchemaKey } as IRequestAction; + this.store.dispatch(new StartRequestAction(apiAction)); return this.httpClient.get<{ [cfguid: string]: IMetricsResponse }>(fullUrl, { headers: { 'x-cap-cnsi-list': action.cfGuid } }).pipe( map(metrics => { const metric = metrics[action.cfGuid]; - const metricKey = MetricsAction.buildMetricKey(action.guid, action.query); const metricObject = metric ? { - [metricKey]: metric.data + [action.metricId]: metric.data } : {}; return new WrapperRequestActionSuccess( { @@ -59,7 +59,7 @@ export class MetricsEffect { })); private buildFullUrl(action: MetricsAction) { - return `${action.url}/${action.queryType}?query=${action.query}`; + return `${action.url}/${action.queryType}?query=${action.query.getFullQuery()}`; } } From 720a1abf558db29206bc75b4eceaa62354b86f18 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 7 Sep 2018 16:27:52 +0100 Subject: [PATCH 010/114] Fill out chart with emptys when no value is passed --- .../application-instance-chart.component.ts | 2 +- .../metrics-chart/metrics-chart.component.ts | 38 ++++++++++++++----- .../metrics.component.manager.ts | 35 +++++++++++++++++ .../app/store/actions/metrics.actions.ts | 3 +- .../app/store/types/base-metric.types.ts | 2 +- 5 files changed, 67 insertions(+), 13 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts index 4e3a743513..ccc5546060 100644 --- a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts +++ b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts @@ -67,7 +67,7 @@ export class ApplicationInstanceChartComponent implements OnInit { private mapSeriesItemValue() { switch (this.seriesTranslation) { case 'mb': - return (bytes) => bytes / 1000000; + return (bytes) => (bytes / 1000000).toFixed(2); default: return undefined; } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index aef956dc60..00bb62cd1f 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -64,20 +64,20 @@ export class MetricsChartComponent implements OnInit, OnDestroy { this.startEnd[index] = date; const [start, end] = this.startEnd; if (start && end) { - const startUnix = start.unix() + ''; - const endUnix = end.unix() + ''; + const startUnix = start.unix(); + const endUnix = end.unix(); const oldAction = this.metricsConfig.metricsAction; - const action = new FetchApplicationMetricsAction( + this.metricsConfig.metricsAction = new FetchApplicationMetricsAction( oldAction.guid, oldAction.cfGuid, new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { start: startUnix, end: endUnix, - step: '365' + step: (endUnix - startUnix) / 100 }), MetricQueryType.RANGE_QUERY ); - this.commit = this.getCommitFn(action); + this.commit = this.getCommitFn(this.metricsConfig.metricsAction); this.commit(); } } @@ -103,6 +103,29 @@ export class MetricsChartComponent implements OnInit, OnDestroy { private entityMonitorFactory: EntityMonitorFactory ) { } + private postFetchMiddleware(metricsArray: ChartSeries[]) { + if (this.metricsConfig.sort) { + const newMetricsArray = [ + ...metricsArray + ]; + newMetricsArray.sort(this.metricsConfig.sort); + if ( + this.metricsConfig.metricsAction.query.params && + this.metricsConfig.metricsAction.query.params.start && + this.metricsConfig.metricsAction.query.params.end + ) { + return MetricsChartManager.fillOutTimeOrderedChartSeries( + newMetricsArray, + this.metricsConfig.metricsAction.query.params.start as number, + this.metricsConfig.metricsAction.query.params.end as number, + this.metricsConfig.metricsAction.query.params.step as number, + this.metricsConfig, + ) + } + } + return metricsArray; + } + ngOnInit() { this.metricsMonitor = this.entityMonitorFactory.create( this.metricsConfig.metricsAction.metricId, @@ -116,10 +139,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { if (!metricsArray.length) { return null; } - if (this.metricsConfig.sort) { - metricsArray.sort(this.metricsConfig.sort); - } - return metricsArray; + return this.postFetchMiddleware(metricsArray); }) ); const now = moment(moment.now()); diff --git a/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts b/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts index 00bc700be9..2e1035fe47 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts @@ -1,5 +1,6 @@ import { MetricsConfig } from './metrics-chart.component'; import { IMetrics, ChartSeries } from '../../../store/types/base-metric.types'; +import { MetricsChartHelpers } from './metrics.component.helpers'; export class MetricsChartManager { static mapMatrix(metrics: IMetrics, metricsConfig: MetricsConfig): ChartSeries[] { @@ -24,4 +25,38 @@ export class MetricsChartManager { }) ); } + static fillOutTimeOrderedChartSeries(timeOrdered: ChartSeries[], start: number, end: number, step: number, metricConfig: MetricsConfig) { + if (!timeOrdered || !timeOrdered.length) { + return timeOrdered; + } + return timeOrdered.reduce((allSeries, series) => { + let pos = 0; + const data = series.series; + for (let t = start; t <= end; t += step) { + const current = data[pos]; + const name = metricConfig.mapSeriesItemName ? metricConfig.mapSeriesItemName(t) : t + ''; + if (!current) { + data.push({ + name: metricConfig.mapSeriesItemName ? metricConfig.mapSeriesItemName(t) : t + '', + value: 0 + }); + } else { + if (current.name < name) { + data.splice(pos, 0, { + name: metricConfig.mapSeriesItemName ? metricConfig.mapSeriesItemName(t) : t + '', + value: 0 + }); + } else { + data.splice(pos + 1, 0, { + name: metricConfig.mapSeriesItemName ? metricConfig.mapSeriesItemName(t) : t + '', + value: 0 + }); + pos += 2; + } + } + } + allSeries.push(series); + return allSeries; + }, []); + } } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index f27a9c7e5b..c3b0234084 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -12,7 +12,7 @@ export enum MetricQueryType { } export interface IMetricQueryConfigParams { window?: string; - [key: string]: string; + [key: string]: string | number; } export class MetricQueryConfig { constructor( @@ -26,7 +26,6 @@ export class MetricQueryConfig { public joinParams() { const { window = '', - step = '365', ...params } = this.params; const paramString = Object.keys(params).reduce((accum, key) => { diff --git a/src/frontend/app/store/types/base-metric.types.ts b/src/frontend/app/store/types/base-metric.types.ts index ca3aa9658f..baa9e59e05 100644 --- a/src/frontend/app/store/types/base-metric.types.ts +++ b/src/frontend/app/store/types/base-metric.types.ts @@ -36,7 +36,7 @@ export type IMetricStringsResult = IMetricSample[]; export interface ChartSeries { name: string; series: { - name: string; + name: string | Date; value: T; }[]; } From 278f6d906a365d8c9dd885bc9d7c2254e3c16010 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 7 Sep 2018 16:36:52 +0100 Subject: [PATCH 011/114] Update package json --- package-lock.json | 5034 +++++++++++++++++++++++---------------------- package.json | 3 +- 2 files changed, 2523 insertions(+), 2514 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1228b3e384..25332fbc4b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dev": true, "requires": { "@angular-devkit/core": "0.7.4", - "rxjs": "6.2.0" + "rxjs": "^6.0.0" } }, "@angular-devkit/build-angular": { @@ -25,50 +25,50 @@ "@angular-devkit/build-webpack": "0.7.4", "@angular-devkit/core": "0.7.4", "@ngtools/webpack": "6.1.4", - "ajv": "6.4.0", - "autoprefixer": "8.6.5", - "circular-dependency-plugin": "5.0.2", - "clean-css": "4.2.1", - "copy-webpack-plugin": "4.5.2", - "file-loader": "1.1.11", - "glob": "7.1.2", - "html-webpack-plugin": "3.2.0", - "istanbul": "0.4.5", - "istanbul-instrumenter-loader": "3.0.1", - "karma-source-map-support": "1.3.0", - "less": "3.8.1", - "less-loader": "4.1.0", - "license-webpack-plugin": "1.4.0", - "loader-utils": "1.1.0", - "mini-css-extract-plugin": "0.4.1", - "minimatch": "3.0.4", - "node-sass": "4.9.3", - "opn": "5.3.0", - "parse5": "4.0.0", - "portfinder": "1.0.16", - "postcss": "6.0.23", - "postcss-import": "11.1.0", - "postcss-loader": "2.1.6", - "postcss-url": "7.3.2", - "raw-loader": "0.5.1", - "rxjs": "6.2.0", - "sass-loader": "6.0.7", - "semver": "5.5.0", - "source-map-loader": "0.2.4", - "source-map-support": "0.5.6", - "stats-webpack-plugin": "0.6.2", - "style-loader": "0.21.0", - "stylus": "0.54.5", - "stylus-loader": "3.0.2", - "tree-kill": "1.2.0", - "uglifyjs-webpack-plugin": "1.3.0", - "url-loader": "1.1.1", - "webpack": "4.9.2", - "webpack-dev-middleware": "3.1.3", - "webpack-dev-server": "3.1.5", - "webpack-merge": "4.1.4", - "webpack-sources": "1.1.0", - "webpack-subresource-integrity": "1.1.0-rc.4" + "ajv": "~6.4.0", + "autoprefixer": "^8.4.1", + "circular-dependency-plugin": "^5.0.2", + "clean-css": "^4.1.11", + "copy-webpack-plugin": "^4.5.2", + "file-loader": "^1.1.11", + "glob": "^7.0.3", + "html-webpack-plugin": "^3.0.6", + "istanbul": "^0.4.5", + "istanbul-instrumenter-loader": "^3.0.1", + "karma-source-map-support": "^1.2.0", + "less": "^3.7.1", + "less-loader": "^4.1.0", + "license-webpack-plugin": "^1.3.1", + "loader-utils": "^1.1.0", + "mini-css-extract-plugin": "~0.4.0", + "minimatch": "^3.0.4", + "node-sass": "^4.9.3", + "opn": "^5.1.0", + "parse5": "^4.0.0", + "portfinder": "^1.0.13", + "postcss": "^6.0.22", + "postcss-import": "^11.1.0", + "postcss-loader": "^2.1.5", + "postcss-url": "^7.3.2", + "raw-loader": "^0.5.1", + "rxjs": "^6.0.0", + "sass-loader": "~6.0.7", + "semver": "^5.5.0", + "source-map-loader": "^0.2.3", + "source-map-support": "^0.5.0", + "stats-webpack-plugin": "^0.6.2", + "style-loader": "^0.21.0", + "stylus": "^0.54.5", + "stylus-loader": "^3.0.2", + "tree-kill": "^1.2.0", + "uglifyjs-webpack-plugin": "^1.2.5", + "url-loader": "^1.0.1", + "webpack": "~4.9.2", + "webpack-dev-middleware": "^3.1.3", + "webpack-dev-server": "^3.1.4", + "webpack-merge": "^4.1.2", + "webpack-sources": "^1.1.0", + "webpack-subresource-integrity": "^1.1.0-rc.4" } }, "@angular-devkit/build-optimizer": { @@ -77,10 +77,10 @@ "integrity": "sha512-R+Icu9XjIaKcYFscaMBJ1DyBK2prxK3JQSFi0S//0MdNP4gBFIpCtNdOQsNXovCkpVZ7YlgmdE5+vSb39GVHHA==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "source-map": "0.5.6", - "typescript": "2.9.2", - "webpack-sources": "1.1.0" + "loader-utils": "^1.1.0", + "source-map": "^0.5.6", + "typescript": "~2.9.1", + "webpack-sources": "^1.1.0" }, "dependencies": { "typescript": { @@ -99,7 +99,7 @@ "requires": { "@angular-devkit/architect": "0.7.4", "@angular-devkit/core": "0.7.4", - "rxjs": "6.2.0" + "rxjs": "^6.0.0" } }, "@angular-devkit/core": { @@ -108,10 +108,10 @@ "integrity": "sha512-Blh44vzZVzE8B9xIwjRoo7hXPGSDdlrrax0rntvt3DDGVTjsSGm43qT95aDmXiwJruOCJNC5DsaP3+tTAkAyQQ==", "dev": true, "requires": { - "ajv": "6.4.0", - "chokidar": "2.0.3", - "rxjs": "6.2.0", - "source-map": "0.5.6" + "ajv": "~6.4.0", + "chokidar": "^2.0.3", + "rxjs": "^6.0.0", + "source-map": "^0.5.6" } }, "@angular/animations": { @@ -119,7 +119,7 @@ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-6.1.1.tgz", "integrity": "sha512-6o15ZtoTWlvZgu/qTz2xj25A1ZRr+BGRHxkhQDZ4hADEIUyYi96dVQxkUttXTtmACRAhK4oXkL7xleVm5iN6ow==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "@angular/cdk": { @@ -127,7 +127,7 @@ "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-6.4.0.tgz", "integrity": "sha512-JEJ7OsVxoyEgsWG5c48mXLFGOUq0I8Mijar1ktI+TcIqdoLwO1XClcoh8MbgwImp3ZISfOghHVqcjLxhp4UASA==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.7.1" } }, "@angular/cli": { @@ -141,11 +141,11 @@ "@angular-devkit/schematics": "0.7.1", "@schematics/angular": "0.7.1", "@schematics/update": "0.7.1", - "opn": "5.3.0", - "rxjs": "6.2.0", - "semver": "5.5.0", - "symbol-observable": "1.2.0", - "yargs-parser": "10.1.0" + "opn": "^5.3.0", + "rxjs": "^6.0.0", + "semver": "^5.1.0", + "symbol-observable": "^1.2.0", + "yargs-parser": "^10.0.0" }, "dependencies": { "@angular-devkit/architect": { @@ -155,7 +155,7 @@ "dev": true, "requires": { "@angular-devkit/core": "0.7.1", - "rxjs": "6.2.0" + "rxjs": "^6.0.0" } }, "@angular-devkit/core": { @@ -164,10 +164,10 @@ "integrity": "sha512-m+j1d+oMZRu0jUN7UyE4C8Kh8YoY9TP6ltjcrO2SzE89mzHg+apY1taf4EzOYKrrCZxw7Q4viPa8EXeF2AJ1cQ==", "dev": true, "requires": { - "ajv": "6.4.0", - "chokidar": "2.0.3", - "rxjs": "6.2.0", - "source-map": "0.5.6" + "ajv": "~6.4.0", + "chokidar": "^2.0.3", + "rxjs": "^6.0.0", + "source-map": "^0.5.6" } }, "@angular-devkit/schematics": { @@ -177,7 +177,7 @@ "dev": true, "requires": { "@angular-devkit/core": "0.7.1", - "rxjs": "6.2.0" + "rxjs": "^6.0.0" } }, "@schematics/angular": { @@ -188,7 +188,7 @@ "requires": { "@angular-devkit/core": "0.7.1", "@angular-devkit/schematics": "0.7.1", - "typescript": "2.7.2" + "typescript": ">=2.6.2 <2.8" }, "dependencies": { "typescript": { @@ -207,11 +207,11 @@ "requires": { "@angular-devkit/core": "0.7.1", "@angular-devkit/schematics": "0.7.1", - "npm-registry-client": "8.6.0", - "rc": "1.2.8", - "rxjs": "6.2.0", - "semver": "5.5.0", - "semver-intersect": "1.3.1" + "npm-registry-client": "^8.5.1", + "rc": "^1.2.8", + "rxjs": "^6.0.0", + "semver": "^5.3.0", + "semver-intersect": "^1.1.2" } }, "camelcase": { @@ -226,7 +226,7 @@ "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" } } } @@ -236,7 +236,7 @@ "resolved": "https://registry.npmjs.org/@angular/common/-/common-6.1.1.tgz", "integrity": "sha512-mrMG0Q+BUPuiez3RKWkrMCv3r/9iJl1DoTLhLNsgWvpj0IRGcARUyJgmTZqVVVCeacjFQEM+DopbVQ7AjQCkoA==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "@angular/compiler": { @@ -244,7 +244,7 @@ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-6.1.1.tgz", "integrity": "sha512-60qSglzK8lncRI13axHaJryjGvdnue5yI8yXiJEEXMHba+AJ9xfoXH2aPPqBHvUX7bU4p4fsSVGDOe7kryo/ow==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "@angular/compiler-cli": { @@ -253,10 +253,10 @@ "integrity": "sha512-J1FJbeN95Oe3hLNZnNFQUpKFxh5mb11EPKHhGScqajK1+2eZrmKfw6SRi2n4R1FPODerML+q4+uJjywjV5QSOw==", "dev": true, "requires": { - "chokidar": "1.7.0", - "minimist": "1.2.0", - "reflect-metadata": "0.1.12", - "tsickle": "0.32.1" + "chokidar": "^1.4.2", + "minimist": "^1.2.0", + "reflect-metadata": "^0.1.2", + "tsickle": "^0.32.1" }, "dependencies": { "anymatch": { @@ -265,8 +265,8 @@ "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" } }, "arr-diff": { @@ -275,7 +275,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "1.1.0" + "arr-flatten": "^1.0.1" } }, "array-unique": { @@ -290,9 +290,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" } }, "chokidar": { @@ -301,15 +301,15 @@ "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.2.4", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" } }, "expand-brackets": { @@ -318,7 +318,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "0.1.1" + "is-posix-bracket": "^0.1.0" } }, "extglob": { @@ -327,7 +327,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "glob-parent": { @@ -336,7 +336,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^2.0.0" } }, "is-extglob": { @@ -351,7 +351,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "kind-of": { @@ -360,7 +360,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } }, "micromatch": { @@ -369,19 +369,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" } }, "minimist": { @@ -402,11 +402,11 @@ "integrity": "sha512-JW9j+W0SaMSZGejIFZBk0AiPfnhljK3oLx5SaqxrJhjlvzFyPml5zqG1/PuScUj6yTe1muEqwk5CnDK0cOZmKw==", "dev": true, "requires": { - "jasmine-diff": "0.1.3", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map": "0.6.1", - "source-map-support": "0.5.6" + "jasmine-diff": "^0.1.3", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map": "^0.6.0", + "source-map-support": "^0.5.0" } } } @@ -416,7 +416,7 @@ "resolved": "https://registry.npmjs.org/@angular/core/-/core-6.1.1.tgz", "integrity": "sha512-4h/8abB4N5meQHg69IV1wtNKUKe8e0w9z9s/0ZYbvFPVqWB9OkcSihsS2xmfSD3glIgSgS5424/jmiTB9G0Tcw==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "@angular/forms": { @@ -424,7 +424,7 @@ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-6.1.1.tgz", "integrity": "sha512-NiEMJN0INoK6khjS+YJAqt3FS2nXy3JH4J10m55e2gwzldHrsU94bWUDEOEMbPfm7dFC8G1114p/MneleoXTvg==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "@angular/http": { @@ -432,7 +432,7 @@ "resolved": "https://registry.npmjs.org/@angular/http/-/http-6.1.1.tgz", "integrity": "sha512-1VtFTSJeo1Q3pgbQ65NyMZFV3f+Jd8i4XMbmqOf+L7StosI1HE8eiDbm1SgQOuXZv0MFwgKMEXy7lzfnyl9Udw==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "@angular/language-service": { @@ -446,7 +446,15 @@ "resolved": "https://registry.npmjs.org/@angular/material/-/material-6.2.0.tgz", "integrity": "sha512-4vC2Ycrf1ORP6iGIy9qExVpbjqHJ/ObEQd72pVDQyK127O8cxhGhEiX8QvxdeAGREVxqwfhUGEK/vjH3o110fQ==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.7.1" + } + }, + "@angular/material-moment-adapter": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-6.4.7.tgz", + "integrity": "sha512-OGdDtpu/yRioOQXhJFCNuiOF2OgiL9VUj8ewFPi1lDtFGUFfVwU2h3hWkKLn+yuPW+DBVYla11tCNsn5dLElmA==", + "requires": { + "tslib": "^1.7.1" } }, "@angular/platform-browser": { @@ -454,7 +462,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-6.1.1.tgz", "integrity": "sha512-YgFP6NQgk7ZkKKfywmLC3wBZuepYLttjIvzWhbvutJpRahAw1qDQaeCXdv75DU4U8fqHDKkllZS/kIdkc3cxQw==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "@angular/platform-browser-dynamic": { @@ -462,7 +470,7 @@ "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-6.1.1.tgz", "integrity": "sha512-OYXMgVapk6XmRwWgW05fN30dj1fSSiJjCXMzxxG/y+hIMe/O9HLfShADQHee9XRQJhZEdDB0rr7tOFYmH1c5Uw==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "@angular/platform-server": { @@ -470,9 +478,9 @@ "resolved": "https://registry.npmjs.org/@angular/platform-server/-/platform-server-6.1.1.tgz", "integrity": "sha512-fss9CRIAK599lb7X5aOcKdGXZ3qr5t/QfkMEYfIc7A/nJ7wupa5L2pGFvifxClJIkFMB7Q7GMQn57WgnPaU5zw==", "requires": { - "domino": "2.0.2", - "tslib": "1.9.2", - "xhr2": "0.1.4" + "domino": "^2.0.1", + "tslib": "^1.9.0", + "xhr2": "^0.1.4" } }, "@angular/router": { @@ -480,7 +488,7 @@ "resolved": "https://registry.npmjs.org/@angular/router/-/router-6.1.1.tgz", "integrity": "sha512-pYvB0wQI7/TS/BLeEy707t1wMujvM8fAlSw+BJs4iqYJwYuG9nGG3IzKvR90fZDVdseNFNa72WokXaK1BPSs8Q==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "@ngrx/effects": { @@ -510,27 +518,27 @@ "dev": true, "requires": { "@angular-devkit/core": "0.7.4", - "rxjs": "6.2.0", - "tree-kill": "1.2.0", - "webpack-sources": "1.1.0" + "rxjs": "^6.0.0", + "tree-kill": "^1.0.0", + "webpack-sources": "^1.1.0" } }, "@swimlane/ngx-charts": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@swimlane/ngx-charts/-/ngx-charts-8.1.0.tgz", - "integrity": "sha512-jjcIwt5uQXoBaQwCgHPqpzSoNJj68PiKItMYg6RVyqLua67vZ90ZdxzuYPsYpRtDIXJiV5/gXuDtD3ZrjtdQCA==", - "requires": { - "d3-array": "1.2.1", - "d3-brush": "1.0.4", - "d3-color": "1.2.0", - "d3-force": "1.1.0", - "d3-format": "1.3.0", - "d3-hierarchy": "1.1.6", - "d3-interpolate": "1.2.0", - "d3-scale": "1.0.7", - "d3-selection": "1.3.0", - "d3-shape": "1.2.0", - "d3-time-format": "2.1.1" + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@swimlane/ngx-charts/-/ngx-charts-9.0.0.tgz", + "integrity": "sha512-XmiEclr09p5EiZyXPqPyGD//Ai4GUpJbMYw90A24UJdFwvh8BQDDeZ1QUNIuTWkpN5XMZ8TzQ4P4qjaTypsZPw==", + "requires": { + "d3-array": "^1.2.1", + "d3-brush": "^1.0.4", + "d3-color": "^1.0.3", + "d3-force": "^1.1.0", + "d3-format": "^1.2.0", + "d3-hierarchy": "^1.1.5", + "d3-interpolate": "^1.1.5", + "d3-scale": "^1.0.6", + "d3-selection": "^1.1.0", + "d3-shape": "^1.2.0", + "d3-time-format": "^2.1.0" } }, "@tweenjs/tween.js": { @@ -561,7 +569,7 @@ "integrity": "sha512-JAMFhOaHIciYVh8fb5/83nmuO/AHwmto+Hq7a9y8FzLDcC1KCU344XDOMEmahnrTFlHjgh4L0WJFczNIX2GxnQ==", "dev": true, "requires": { - "@types/node": "6.0.111" + "@types/node": "*" } }, "@types/jasmine": { @@ -576,7 +584,7 @@ "integrity": "sha512-hYDVmQZT5VA2kigd4H4bv7vl/OhlympwREUemqBdOqtrYTo5Ytm12a5W5/nGgGYdanGVxj0x/VhZ7J3hOg/YKg==", "dev": true, "requires": { - "@types/jasmine": "2.8.7" + "@types/jasmine": "*" } }, "@types/karma": { @@ -585,8 +593,8 @@ "integrity": "sha512-26J5wva11NjLWWitm4JMRc51NtTnSf912tPonmujsPcISitGXz1KcfwQk6SRcyM1ikb09zq3aGwVcj7HYkmW6A==", "dev": true, "requires": { - "@types/bluebird": "3.5.20", - "@types/node": "6.0.111" + "@types/bluebird": "*", + "@types/node": "*" } }, "@types/node": { @@ -607,10 +615,10 @@ "integrity": "sha512-/KXM5oev+nNCLIgBjkwbk8VqxmzI56woD4VUxn95O+YeQ8hJzcSmIZ1IN3WexiqBb6srzDo2bdMbsXxgXNkz5Q==", "dev": true, "requires": { - "@types/caseless": "0.12.1", - "@types/form-data": "2.2.1", - "@types/node": "6.0.111", - "@types/tough-cookie": "2.3.3" + "@types/caseless": "*", + "@types/form-data": "*", + "@types/node": "*", + "@types/tough-cookie": "*" } }, "@types/selenium-webdriver": { @@ -638,7 +646,7 @@ "requires": { "@webassemblyjs/helper-wasm-bytecode": "1.4.3", "@webassemblyjs/wast-parser": "1.4.3", - "debug": "3.1.0", + "debug": "^3.1.0", "webassemblyjs": "1.4.3" }, "dependencies": { @@ -665,7 +673,7 @@ "integrity": "sha512-e8+KZHh+RV8MUvoSRtuT1sFXskFnWG9vbDy47Oa166xX+l0dD5sERJ21g5/tcH8Yo95e9IN3u7Jc3NbhnUcSkw==", "dev": true, "requires": { - "debug": "3.1.0" + "debug": "^3.1.0" }, "dependencies": { "debug": { @@ -710,7 +718,7 @@ "@webassemblyjs/helper-buffer": "1.4.3", "@webassemblyjs/helper-wasm-bytecode": "1.4.3", "@webassemblyjs/wasm-gen": "1.4.3", - "debug": "3.1.0" + "debug": "^3.1.0" }, "dependencies": { "debug": { @@ -730,7 +738,7 @@ "integrity": "sha512-4u0LJLSPzuRDWHwdqsrThYn+WqMFVqbI2ltNrHvZZkzFPO8XOZ0HFQ5eVc4jY/TNHgXcnwrHjONhPGYuuf//KQ==", "dev": true, "requires": { - "leb": "0.3.0" + "leb": "^0.3.0" } }, "@webassemblyjs/validation": { @@ -756,7 +764,7 @@ "@webassemblyjs/wasm-opt": "1.4.3", "@webassemblyjs/wasm-parser": "1.4.3", "@webassemblyjs/wast-printer": "1.4.3", - "debug": "3.1.0" + "debug": "^3.1.0" }, "dependencies": { "debug": { @@ -791,7 +799,7 @@ "@webassemblyjs/helper-buffer": "1.4.3", "@webassemblyjs/wasm-gen": "1.4.3", "@webassemblyjs/wasm-parser": "1.4.3", - "debug": "3.1.0" + "debug": "^3.1.0" }, "dependencies": { "debug": { @@ -828,7 +836,7 @@ "@webassemblyjs/floating-point-hex-parser": "1.4.3", "@webassemblyjs/helper-code-frame": "1.4.3", "@webassemblyjs/helper-fsm": "1.4.3", - "long": "3.2.0", + "long": "^3.2.0", "webassemblyjs": "1.4.3" } }, @@ -840,7 +848,7 @@ "requires": { "@webassemblyjs/ast": "1.4.3", "@webassemblyjs/wast-parser": "1.4.3", - "long": "3.2.0" + "long": "^3.2.0" } }, "@webpack-contrib/schema-utils": { @@ -849,12 +857,12 @@ "integrity": "sha512-LonryJP+FxQQHsjGBi6W786TQB1Oym+agTpY0c+Kj8alnIw+DLUJb6SI8Y1GHGhLCH1yPRrucjObUmxNICQ1pg==", "dev": true, "requires": { - "ajv": "6.4.0", - "ajv-keywords": "3.2.0", - "chalk": "2.4.1", - "strip-ansi": "4.0.0", - "text-table": "0.2.0", - "webpack-log": "1.2.0" + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chalk": "^2.3.2", + "strip-ansi": "^4.0.0", + "text-table": "^0.2.0", + "webpack-log": "^1.1.2" }, "dependencies": { "ansi-regex": { @@ -869,9 +877,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "strip-ansi": { @@ -880,7 +888,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -897,7 +905,7 @@ "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", "dev": true, "requires": { - "mime-types": "2.1.18", + "mime-types": "~2.1.18", "negotiator": "0.6.1" } }, @@ -913,7 +921,7 @@ "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", "dev": true, "requires": { - "acorn": "5.5.3" + "acorn": "^5.0.0" } }, "acorn-jsx": { @@ -922,7 +930,7 @@ "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", "dev": true, "requires": { - "acorn": "3.3.0" + "acorn": "^3.0.4" }, "dependencies": { "acorn": { @@ -958,7 +966,7 @@ "integrity": "sha512-c+R/U5X+2zz2+UCrCFv6odQzJdoqI+YecuhnAJLa1zYaMc13zPfwMwZrr91Pd1DYNo/yPRbiM4WVf9whgwFsIg==", "dev": true, "requires": { - "es6-promisify": "5.0.0" + "es6-promisify": "^5.0.0" } }, "ajv": { @@ -967,10 +975,10 @@ "integrity": "sha1-06/3jpJ3VJdx2vAWTP9ISCt1T8Y=", "dev": true, "requires": { - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1", - "uri-js": "3.0.2" + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0", + "uri-js": "^3.0.2" } }, "ajv-errors": { @@ -991,9 +999,9 @@ "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", "dev": true, "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" + "kind-of": "^3.0.2", + "longest": "^1.0.1", + "repeat-string": "^1.5.2" }, "dependencies": { "kind-of": { @@ -1002,7 +1010,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -1020,11 +1028,11 @@ "dev": true, "optional": true, "requires": { - "bitsyntax": "0.0.4", - "bluebird": "3.5.1", + "bitsyntax": "~0.0.4", + "bluebird": "^3.4.6", "buffer-more-ints": "0.0.2", - "readable-stream": "1.1.14", - "safe-buffer": "5.1.2" + "readable-stream": "1.x >=1.1.9", + "safe-buffer": "^5.0.1" }, "dependencies": { "isarray": { @@ -1041,10 +1049,10 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -1061,7 +1069,7 @@ "resolved": "https://registry.npmjs.org/angular2-virtual-scroll/-/angular2-virtual-scroll-0.3.2.tgz", "integrity": "sha512-eyPb6FnaA8d4lBh3v31F3YLLblr9Nn8zv25uKVmxPkA2/te6QDg0sPMqy6o8GouwW+U9LKzcUJZZBHa0ICY/rg==", "requires": { - "@tweenjs/tween.js": "17.2.0" + "@tweenjs/tween.js": "^17.1.0" } }, "ansi-colors": { @@ -1070,7 +1078,7 @@ "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", "dev": true, "requires": { - "ansi-wrap": "0.1.0" + "ansi-wrap": "^0.1.0" } }, "ansi-cyan": { @@ -1124,7 +1132,7 @@ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "requires": { - "color-convert": "1.9.1" + "color-convert": "^1.9.0" } }, "ansi-wrap": { @@ -1139,8 +1147,8 @@ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", "dev": true, "requires": { - "micromatch": "3.1.10", - "normalize-path": "2.1.1" + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" } }, "app-root-path": { @@ -1155,7 +1163,7 @@ "integrity": "sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE=", "dev": true, "requires": { - "buffer-equal": "1.0.0" + "buffer-equal": "^1.0.0" } }, "append-transform": { @@ -1164,7 +1172,7 @@ "integrity": "sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw==", "dev": true, "requires": { - "default-require-extensions": "2.0.0" + "default-require-extensions": "^2.0.0" } }, "aproba": { @@ -1185,8 +1193,8 @@ "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "argparse": { @@ -1194,7 +1202,7 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "requires": { - "sprintf-js": "1.0.3" + "sprintf-js": "~1.0.2" } }, "argv": { @@ -1215,7 +1223,7 @@ "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4=", "dev": true, "requires": { - "make-iterator": "1.0.1" + "make-iterator": "^1.0.0" } }, "arr-flatten": { @@ -1230,7 +1238,7 @@ "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ=", "dev": true, "requires": { - "make-iterator": "1.0.1" + "make-iterator": "^1.0.0" } }, "arr-union": { @@ -1263,8 +1271,8 @@ "integrity": "sha1-GEtI9i2S10UrsxsyMWXH+L0CJm0=", "dev": true, "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.12.0" + "define-properties": "^1.1.2", + "es-abstract": "^1.7.0" } }, "array-initial": { @@ -1273,8 +1281,8 @@ "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U=", "dev": true, "requires": { - "array-slice": "1.1.0", - "is-number": "4.0.0" + "array-slice": "^1.0.0", + "is-number": "^4.0.0" }, "dependencies": { "is-number": { @@ -1291,7 +1299,7 @@ "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", "dev": true, "requires": { - "is-number": "4.0.0" + "is-number": "^4.0.0" }, "dependencies": { "is-number": { @@ -1314,9 +1322,9 @@ "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", "dev": true, "requires": { - "default-compare": "1.0.0", - "get-value": "2.0.6", - "kind-of": "5.1.0" + "default-compare": "^1.0.0", + "get-value": "^2.0.6", + "kind-of": "^5.0.2" }, "dependencies": { "kind-of": { @@ -1333,7 +1341,7 @@ "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", "dev": true, "requires": { - "array-uniq": "1.0.3" + "array-uniq": "^1.0.1" } }, "array-uniq": { @@ -1379,9 +1387,9 @@ "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "dev": true, "requires": { - "bn.js": "4.11.8", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, "assert": { @@ -1430,10 +1438,10 @@ "integrity": "sha512-R1BaUeJ4PMoLNJuk+0tLJgjmEqVsdN118+Z8O+alhnQDQgy0kmD5Mqi0DNEmMx2LM0Ed5yekKu+ZXYvIHceicg==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0", - "process-nextick-args": "1.0.7", - "stream-exhaust": "1.0.2" + "end-of-stream": "^1.1.0", + "once": "^1.3.2", + "process-nextick-args": "^1.0.7", + "stream-exhaust": "^1.0.1" }, "dependencies": { "process-nextick-args": { @@ -1469,7 +1477,7 @@ "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs=", "dev": true, "requires": { - "async-done": "1.3.1" + "async-done": "^1.2.2" } }, "asynckit": { @@ -1490,12 +1498,12 @@ "integrity": "sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==", "dev": true, "requires": { - "browserslist": "3.2.8", - "caniuse-lite": "1.0.30000877", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "6.0.23", - "postcss-value-parser": "3.3.0" + "browserslist": "^3.2.8", + "caniuse-lite": "^1.0.30000864", + "normalize-range": "^0.1.2", + "num2fraction": "^1.2.2", + "postcss": "^6.0.23", + "postcss-value-parser": "^3.2.3" } }, "aws-sign2": { @@ -1527,7 +1535,7 @@ "dev": true, "optional": true, "requires": { - "debug": "2.6.9" + "debug": "^2.2.0" } } } @@ -1538,9 +1546,9 @@ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", "dev": true, "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" }, "dependencies": { "ansi-styles": { @@ -1555,11 +1563,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "supports-color": { @@ -1576,14 +1584,14 @@ "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "dev": true, "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.10", - "source-map": "0.5.7", - "trim-right": "1.0.1" + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" }, "dependencies": { "source-map": { @@ -1600,7 +1608,7 @@ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", "dev": true, "requires": { - "babel-runtime": "6.26.0" + "babel-runtime": "^6.22.0" } }, "babel-runtime": { @@ -1609,8 +1617,8 @@ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", "dev": true, "requires": { - "core-js": "2.5.7", - "regenerator-runtime": "0.11.1" + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" } }, "babel-template": { @@ -1619,11 +1627,11 @@ "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.10" + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" } }, "babel-traverse": { @@ -1632,15 +1640,15 @@ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.4", - "lodash": "4.17.10" + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" } }, "babel-types": { @@ -1649,10 +1657,10 @@ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", "dev": true, "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.10", - "to-fast-properties": "1.0.3" + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" } }, "babylon": { @@ -1667,15 +1675,15 @@ "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA=", "dev": true, "requires": { - "arr-filter": "1.1.2", - "arr-flatten": "1.1.0", - "arr-map": "2.0.2", - "array-each": "1.0.1", - "array-initial": "1.1.0", - "array-last": "1.3.0", - "async-done": "1.3.1", - "async-settle": "1.0.0", - "now-and-later": "2.0.0" + "arr-filter": "^1.1.1", + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "array-each": "^1.0.0", + "array-initial": "^1.0.0", + "array-last": "^1.1.1", + "async-done": "^1.2.2", + "async-settle": "^1.0.0", + "now-and-later": "^2.0.0" } }, "backo2": { @@ -1696,13 +1704,13 @@ "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", "dev": true, "requires": { - "cache-base": "1.0.1", - "class-utils": "0.3.6", - "component-emitter": "1.2.1", - "define-property": "1.0.0", - "isobject": "3.0.1", - "mixin-deep": "1.3.1", - "pascalcase": "0.1.1" + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" }, "dependencies": { "define-property": { @@ -1711,7 +1719,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -1720,7 +1728,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -1729,7 +1737,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -1738,9 +1746,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -1776,7 +1784,7 @@ "dev": true, "optional": true, "requires": { - "tweetnacl": "0.14.5" + "tweetnacl": "^0.14.3" } }, "better-assert": { @@ -1817,7 +1825,7 @@ "dev": true, "optional": true, "requires": { - "readable-stream": "2.0.6" + "readable-stream": "~2.0.5" }, "dependencies": { "process-nextick-args": { @@ -1834,12 +1842,12 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" } }, "string_decoder": { @@ -1864,7 +1872,7 @@ "dev": true, "optional": true, "requires": { - "inherits": "2.0.3" + "inherits": "~2.0.0" } }, "blocking-proxy": { @@ -1873,7 +1881,7 @@ "integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==", "dev": true, "requires": { - "minimist": "1.2.0" + "minimist": "^1.2.0" }, "dependencies": { "minimist": { @@ -1903,15 +1911,15 @@ "dev": true, "requires": { "bytes": "3.0.0", - "content-type": "1.0.4", + "content-type": "~1.0.4", "debug": "2.6.9", - "depd": "1.1.2", - "http-errors": "1.6.3", + "depd": "~1.1.1", + "http-errors": "~1.6.2", "iconv-lite": "0.4.19", - "on-finished": "2.3.0", + "on-finished": "~2.3.0", "qs": "6.5.1", "raw-body": "2.3.2", - "type-is": "1.6.16" + "type-is": "~1.6.15" }, "dependencies": { "qs": { @@ -1928,12 +1936,12 @@ "integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "dev": true, "requires": { - "array-flatten": "2.1.1", - "deep-equal": "1.0.1", - "dns-equal": "1.0.0", - "dns-txt": "2.0.2", - "multicast-dns": "6.2.3", - "multicast-dns-service-types": "1.1.0" + "array-flatten": "^2.1.0", + "deep-equal": "^1.0.1", + "dns-equal": "^1.0.0", + "dns-txt": "^2.0.2", + "multicast-dns": "^6.0.1", + "multicast-dns-service-types": "^1.1.0" } }, "boolbase": { @@ -1948,7 +1956,7 @@ "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", "dev": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "brace-expansion": { @@ -1957,7 +1965,7 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -1967,16 +1975,16 @@ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-unique": "0.3.2", - "extend-shallow": "2.0.1", - "fill-range": "4.0.0", - "isobject": "3.0.1", - "repeat-element": "1.1.2", - "snapdragon": "0.8.2", - "snapdragon-node": "2.1.1", - "split-string": "3.1.0", - "to-regex": "3.0.2" + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -1985,7 +1993,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -2002,12 +2010,12 @@ "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", "dev": true, "requires": { - "buffer-xor": "1.0.3", - "cipher-base": "1.0.4", - "create-hash": "1.2.0", - "evp_bytestokey": "1.0.3", - "inherits": "2.0.3", - "safe-buffer": "5.1.2" + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "browserify-cipher": { @@ -2016,9 +2024,9 @@ "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", "dev": true, "requires": { - "browserify-aes": "1.2.0", - "browserify-des": "1.0.2", - "evp_bytestokey": "1.0.3" + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" } }, "browserify-des": { @@ -2027,10 +2035,10 @@ "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", "dev": true, "requires": { - "cipher-base": "1.0.4", - "des.js": "1.0.0", - "inherits": "2.0.3", - "safe-buffer": "5.1.2" + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" } }, "browserify-rsa": { @@ -2039,8 +2047,8 @@ "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "dev": true, "requires": { - "bn.js": "4.11.8", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" } }, "browserify-sign": { @@ -2049,13 +2057,13 @@ "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "dev": true, "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "elliptic": "6.4.1", - "inherits": "2.0.3", - "parse-asn1": "5.1.1" + "bn.js": "^4.1.1", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.2", + "elliptic": "^6.0.0", + "inherits": "^2.0.1", + "parse-asn1": "^5.0.0" } }, "browserify-zlib": { @@ -2064,7 +2072,7 @@ "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", "dev": true, "requires": { - "pako": "1.0.6" + "pako": "~1.0.5" } }, "browserslist": { @@ -2073,8 +2081,8 @@ "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", "dev": true, "requires": { - "caniuse-lite": "1.0.30000877", - "electron-to-chromium": "1.3.58" + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" } }, "browserstack": { @@ -2083,7 +2091,7 @@ "integrity": "sha512-O8VMT64P9NOLhuIoD4YngyxBURefaSdR4QdhG8l6HZ9VxtU7jc3m6jLufFwKA5gaf7fetfB2TnRJnMxyob+heg==", "dev": true, "requires": { - "https-proxy-agent": "2.2.1" + "https-proxy-agent": "^2.2.1" } }, "buffer": { @@ -2092,9 +2100,9 @@ "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", "dev": true, "requires": { - "base64-js": "1.3.0", - "ieee754": "1.1.12", - "isarray": "1.0.0" + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" } }, "buffer-crc32": { @@ -2188,19 +2196,19 @@ "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==", "dev": true, "requires": { - "bluebird": "3.5.1", - "chownr": "1.0.1", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "lru-cache": "4.1.3", - "mississippi": "2.0.0", - "mkdirp": "0.5.1", - "move-concurrently": "1.0.1", - "promise-inflight": "1.0.1", - "rimraf": "2.6.2", - "ssri": "5.3.0", - "unique-filename": "1.1.0", - "y18n": "4.0.0" + "bluebird": "^3.5.1", + "chownr": "^1.0.1", + "glob": "^7.1.2", + "graceful-fs": "^4.1.11", + "lru-cache": "^4.1.1", + "mississippi": "^2.0.0", + "mkdirp": "^0.5.1", + "move-concurrently": "^1.0.1", + "promise-inflight": "^1.0.1", + "rimraf": "^2.6.2", + "ssri": "^5.2.4", + "unique-filename": "^1.1.0", + "y18n": "^4.0.0" } }, "cache-base": { @@ -2209,15 +2217,15 @@ "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", "dev": true, "requires": { - "collection-visit": "1.0.0", - "component-emitter": "1.2.1", - "get-value": "2.0.6", - "has-value": "1.0.0", - "isobject": "3.0.1", - "set-value": "2.0.0", - "to-object-path": "0.3.0", - "union-value": "1.0.0", - "unset-value": "1.0.0" + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" } }, "caller-path": { @@ -2226,7 +2234,7 @@ "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", "dev": true, "requires": { - "callsites": "0.2.0" + "callsites": "^0.2.0" } }, "callsite": { @@ -2247,8 +2255,8 @@ "integrity": "sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=", "dev": true, "requires": { - "no-case": "2.3.2", - "upper-case": "1.1.3" + "no-case": "^2.2.0", + "upper-case": "^1.1.1" } }, "camelcase": { @@ -2264,8 +2272,8 @@ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", "dev": true, "requires": { - "camelcase": "2.1.1", - "map-obj": "1.0.1" + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" }, "dependencies": { "camelcase": { @@ -2295,8 +2303,8 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" + "align-text": "^0.1.3", + "lazy-cache": "^1.0.3" } }, "chalk": { @@ -2305,9 +2313,9 @@ "integrity": "sha512-LvixLAQ4MYhbf7hgL4o5PeK32gJKvVzDRiSNIApDofQvyhl8adgG2lJVXn4+ekQoK7HL9RF8lqxwerpe0x2pCw==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "4.5.0" + "ansi-styles": "^3.1.0", + "escape-string-regexp": "^1.0.5", + "supports-color": "^4.0.0" }, "dependencies": { "has-flag": { @@ -2322,7 +2330,7 @@ "integrity": "sha1-vnoN5ITexcXN34s9WRJQRJEvY1s=", "dev": true, "requires": { - "has-flag": "2.0.0" + "has-flag": "^2.0.0" } } } @@ -2333,18 +2341,18 @@ "integrity": "sha512-zW8iXYZtXMx4kux/nuZVXjkLP+CyIK5Al5FHnj1OgTKGZfp4Oy6/ymtMSKFv3GD8DviEmUPmJg9eFdJ/JzudMg==", "dev": true, "requires": { - "anymatch": "2.0.0", - "async-each": "1.0.1", - "braces": "2.3.2", - "fsevents": "1.2.4", - "glob-parent": "3.1.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "4.0.0", - "normalize-path": "2.1.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0", - "upath": "1.1.0" + "anymatch": "^2.0.0", + "async-each": "^1.0.0", + "braces": "^2.3.0", + "fsevents": "^1.1.2", + "glob-parent": "^3.1.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^2.1.1", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0", + "upath": "^1.0.0" } }, "chownr": { @@ -2365,8 +2373,8 @@ "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", "dev": true, "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.2" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "circular-dependency-plugin": { @@ -2386,10 +2394,10 @@ "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", "dev": true, "requires": { - "arr-union": "3.1.0", - "define-property": "0.2.5", - "isobject": "3.0.1", - "static-extend": "0.1.2" + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" }, "dependencies": { "define-property": { @@ -2398,7 +2406,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -2409,7 +2417,7 @@ "integrity": "sha512-4ZxI6dy4lrY6FHzfiy1aEOXgu4LIsW2MhwG0VBKdcoGoH/XLFgaHSdLTGr4O8Be6A8r3MOphEiI8Gc1n0ecf3g==", "dev": true, "requires": { - "source-map": "0.6.1" + "source-map": "~0.6.0" }, "dependencies": { "source-map": { @@ -2426,7 +2434,7 @@ "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", "dev": true, "requires": { - "restore-cursor": "1.0.1" + "restore-cursor": "^1.0.1" } }, "cli-width": { @@ -2442,8 +2450,8 @@ "dev": true, "optional": true, "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", + "center-align": "^0.1.1", + "right-align": "^0.1.1", "wordwrap": "0.0.2" }, "dependencies": { @@ -2474,10 +2482,10 @@ "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==", "dev": true, "requires": { - "for-own": "1.0.0", - "is-plain-object": "2.0.4", - "kind-of": "6.0.2", - "shallow-clone": "1.0.0" + "for-own": "^1.0.0", + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.0", + "shallow-clone": "^1.0.0" } }, "clone-stats": { @@ -2492,9 +2500,9 @@ "integrity": "sha512-Bq6+4t+lbM8vhTs/Bef5c5AdEMtapp/iFb6+s4/Hh9MVTt8OLKH7ZOOZSCT+Ys7hsHvqv0GuMPJ1lnQJVHvxpg==", "dev": true, "requires": { - "inherits": "2.0.3", - "process-nextick-args": "2.0.0", - "readable-stream": "2.3.6" + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" } }, "co": { @@ -2516,7 +2524,7 @@ "dev": true, "requires": { "argv": "0.0.2", - "request": "2.87.0", + "request": "^2.81.0", "urlgrey": "0.4.4" } }, @@ -2526,12 +2534,12 @@ "integrity": "sha512-JgFMudx0n50IuE/ydAfnkksCwQkWSVWgYvhDPHZgDUbmsiYC22VuEXKu5l8Hhx9UJsLgjWDLjTAFGj2WaW5DUA==", "dev": true, "requires": { - "app-root-path": "2.1.0", - "css-selector-tokenizer": "0.7.0", - "cssauron": "1.4.0", - "semver-dsl": "1.0.1", - "source-map": "0.5.7", - "sprintf-js": "1.1.1" + "app-root-path": "^2.1.0", + "css-selector-tokenizer": "^0.7.0", + "cssauron": "^1.4.0", + "semver-dsl": "^1.0.1", + "source-map": "^0.5.7", + "sprintf-js": "^1.1.1" }, "dependencies": { "source-map": { @@ -2554,9 +2562,9 @@ "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw=", "dev": true, "requires": { - "arr-map": "2.0.2", - "for-own": "1.0.0", - "make-iterator": "1.0.1" + "arr-map": "^2.0.2", + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" } }, "collection-visit": { @@ -2565,8 +2573,8 @@ "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", "dev": true, "requires": { - "map-visit": "1.0.0", - "object-visit": "1.0.1" + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" } }, "color-convert": { @@ -2575,7 +2583,7 @@ "integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==", "dev": true, "requires": { - "color-name": "1.1.3" + "color-name": "^1.1.1" } }, "color-name": { @@ -2602,7 +2610,7 @@ "integrity": "sha1-RYwH4J4NkA/Ci3Cj/sLazR0st/Y=", "dev": true, "requires": { - "lodash": "4.17.10" + "lodash": "^4.5.0" } }, "combined-stream": { @@ -2611,7 +2619,7 @@ "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", "dev": true, "requires": { - "delayed-stream": "1.0.0" + "delayed-stream": "~1.0.0" } }, "commander": { @@ -2656,7 +2664,7 @@ "integrity": "sha1-MmxfUH+7BV9UEWeCuWmoG2einac=", "dev": true, "requires": { - "mime-db": "1.35.0" + "mime-db": ">= 1.34.0 < 2" }, "dependencies": { "mime-db": { @@ -2673,13 +2681,13 @@ "integrity": "sha512-HSjyBG5N1Nnz7tF2+O7A9XUhyjru71/fwgNb7oIsEVHR0WShfs2tIS/EySLgiTe98aOK18YDlMXpzjCXY/n9mg==", "dev": true, "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.5", "bytes": "3.0.0", - "compressible": "2.0.14", + "compressible": "~2.0.14", "debug": "2.6.9", - "on-headers": "1.0.1", + "on-headers": "~1.0.1", "safe-buffer": "5.1.2", - "vary": "1.1.2" + "vary": "~1.1.2" } }, "concat-map": { @@ -2694,10 +2702,10 @@ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", "dev": true, "requires": { - "buffer-from": "1.1.0", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "typedarray": "0.0.6" + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" } }, "connect": { @@ -2708,7 +2716,7 @@ "requires": { "debug": "2.6.9", "finalhandler": "1.1.0", - "parseurl": "1.3.2", + "parseurl": "~1.3.2", "utils-merge": "1.0.1" }, "dependencies": { @@ -2719,12 +2727,12 @@ "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.3.1", - "unpipe": "1.0.0" + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "unpipe": "~1.0.0" } }, "statuses": { @@ -2747,7 +2755,7 @@ "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", "dev": true, "requires": { - "date-now": "0.1.4" + "date-now": "^0.1.4" } }, "console-control-strings": { @@ -2798,12 +2806,12 @@ "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", "dev": true, "requires": { - "aproba": "1.2.0", - "fs-write-stream-atomic": "1.0.10", - "iferr": "0.1.5", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" + "aproba": "^1.1.1", + "fs-write-stream-atomic": "^1.0.8", + "iferr": "^0.1.5", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.0" } }, "copy-descriptor": { @@ -2818,8 +2826,8 @@ "integrity": "sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==", "dev": true, "requires": { - "each-props": "1.3.2", - "is-plain-object": "2.0.4" + "each-props": "^1.3.0", + "is-plain-object": "^2.0.1" } }, "copy-webpack-plugin": { @@ -2828,14 +2836,14 @@ "integrity": "sha512-zmC33E8FFSq3AbflTvqvPvBo621H36Afsxlui91d+QyZxPIuXghfnTsa1CuqiAaCPgJoSUWfTFbKJnadZpKEbQ==", "dev": true, "requires": { - "cacache": "10.0.4", - "find-cache-dir": "1.0.0", - "globby": "7.1.1", - "is-glob": "4.0.0", - "loader-utils": "1.1.0", - "minimatch": "3.0.4", - "p-limit": "1.2.0", - "serialize-javascript": "1.5.0" + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "globby": "^7.1.1", + "is-glob": "^4.0.0", + "loader-utils": "^1.1.0", + "minimatch": "^3.0.4", + "p-limit": "^1.0.0", + "serialize-javascript": "^1.4.0" } }, "core-js": { @@ -2855,10 +2863,10 @@ "integrity": "sha512-6e5vDdrXZD+t5v0L8CrurPeybg4Fmf+FCSYxXKYVAqLUtyCSbuyqE059d0kDthTNRzKVjL7QMgNpEUlsoYH3iQ==", "dev": true, "requires": { - "is-directory": "0.3.1", - "js-yaml": "3.11.0", - "parse-json": "4.0.0", - "require-from-string": "2.0.2" + "is-directory": "^0.3.1", + "js-yaml": "^3.9.0", + "parse-json": "^4.0.0", + "require-from-string": "^2.0.1" }, "dependencies": { "parse-json": { @@ -2867,8 +2875,8 @@ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", "dev": true, "requires": { - "error-ex": "1.3.1", - "json-parse-better-errors": "1.0.2" + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" } } } @@ -2879,8 +2887,8 @@ "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", "dev": true, "requires": { - "bn.js": "4.11.8", - "elliptic": "6.4.1" + "bn.js": "^4.1.0", + "elliptic": "^6.0.0" } }, "create-hash": { @@ -2889,11 +2897,11 @@ "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", "dev": true, "requires": { - "cipher-base": "1.0.4", - "inherits": "2.0.3", - "md5.js": "1.3.4", - "ripemd160": "2.0.2", - "sha.js": "2.4.11" + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" } }, "create-hmac": { @@ -2902,12 +2910,12 @@ "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", "dev": true, "requires": { - "cipher-base": "1.0.4", - "create-hash": "1.2.0", - "inherits": "2.0.3", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.2", - "sha.js": "2.4.11" + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, "cross-spawn": { @@ -2917,8 +2925,8 @@ "dev": true, "optional": true, "requires": { - "lru-cache": "4.1.3", - "which": "1.3.1" + "lru-cache": "^4.0.1", + "which": "^1.2.9" } }, "cryptiles": { @@ -2928,7 +2936,7 @@ "dev": true, "optional": true, "requires": { - "boom": "2.10.1" + "boom": "2.x.x" } }, "crypto-browserify": { @@ -2937,17 +2945,17 @@ "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", "dev": true, "requires": { - "browserify-cipher": "1.0.1", - "browserify-sign": "4.0.4", - "create-ecdh": "4.0.3", - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "diffie-hellman": "5.0.3", - "inherits": "2.0.3", - "pbkdf2": "3.0.16", - "public-encrypt": "4.0.2", - "randombytes": "2.0.6", - "randomfill": "1.0.4" + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" } }, "css-parse": { @@ -2962,10 +2970,10 @@ "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", "dev": true, "requires": { - "boolbase": "1.0.0", - "css-what": "2.1.0", + "boolbase": "~1.0.0", + "css-what": "2.1", "domutils": "1.5.1", - "nth-check": "1.0.1" + "nth-check": "~1.0.1" } }, "css-selector-tokenizer": { @@ -2974,9 +2982,9 @@ "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", "dev": true, "requires": { - "cssesc": "0.1.0", - "fastparse": "1.1.1", - "regexpu-core": "1.0.0" + "cssesc": "^0.1.0", + "fastparse": "^1.1.1", + "regexpu-core": "^1.0.0" } }, "css-what": { @@ -2991,7 +2999,7 @@ "integrity": "sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg=", "dev": true, "requires": { - "through": "2.3.8" + "through": "X.X.X" } }, "cssesc": { @@ -3012,7 +3020,7 @@ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", "dev": true, "requires": { - "array-find-index": "1.0.2" + "array-find-index": "^1.0.1" } }, "custom-event": { @@ -3033,150 +3041,150 @@ "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", "dev": true, "requires": { - "es5-ext": "0.10.44" + "es5-ext": "^0.10.9" } }, "d3-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.1.tgz", - "integrity": "sha512-CyINJQ0SOUHojDdFDH4JEM0552vCR1utGyLHegJHyYH0JyCpSeTPxi4OBqHMA2jJZq4NH782LtaJWBImqI/HBw==" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" }, "d3-brush": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz", - "integrity": "sha1-AMLyOAGfJPbAoZSibUGhUw/+e8Q=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.6.tgz", + "integrity": "sha512-lGSiF5SoSqO5/mYGD5FAeGKKS62JdA1EV7HPrU2b5rTX4qEJJtpjaGLJngjnkewQy7UnGstnFd3168wpf5z76w==", "requires": { - "d3-dispatch": "1.0.3", - "d3-drag": "1.2.1", - "d3-interpolate": "1.2.0", - "d3-selection": "1.3.0", - "d3-transition": "1.1.1" + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" } }, "d3-collection": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.4.tgz", - "integrity": "sha1-NC39EoN8kJdPM/HMCnha6lcNzcI=" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" }, "d3-color": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.0.tgz", - "integrity": "sha512-dmL9Zr/v39aSSMnLOTd58in2RbregCg4UtGyUArvEKTTN6S3HKEy+ziBWVYo9PTzRyVW+pUBHUtRKz0HYX+SQg==" + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.2.3.tgz", + "integrity": "sha512-x37qq3ChOTLd26hnps36lexMRhNXEtVxZ4B25rL0DVdDsGQIJGB18S7y9XDwlDD6MD/ZBzITCf4JjGMM10TZkw==" }, "d3-dispatch": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz", - "integrity": "sha1-RuFJHqqbWMNY/OW+TovtYm54cfg=" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.5.tgz", + "integrity": "sha512-vwKx+lAqB1UuCeklr6Jh1bvC4SZgbSqbkGBLClItFBIYH4vqDJCA7qfoy14lXmJdnBOdxndAMxjCbImJYW7e6g==" }, "d3-drag": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.1.tgz", - "integrity": "sha512-Cg8/K2rTtzxzrb0fmnYOUeZHvwa4PHzwXOLZZPwtEs2SKLLKLXeYwZKBB+DlOxUvFmarOnmt//cU4+3US2lyyQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.3.tgz", + "integrity": "sha512-8S3HWCAg+ilzjJsNtWW1Mutl74Nmzhb9yU6igspilaJzeZVFktmY6oO9xOh5TDk+BM2KrNFjttZNoJJmDnkjkg==", "requires": { - "d3-dispatch": "1.0.3", - "d3-selection": "1.3.0" + "d3-dispatch": "1", + "d3-selection": "1" } }, "d3-ease": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz", - "integrity": "sha1-aL+8NJM4o4DETYrMT7wzBKotjA4=" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.5.tgz", + "integrity": "sha512-Ct1O//ly5y5lFM9YTdu+ygq7LleSgSE4oj7vUt9tPLHUi8VCV7QoizGpdWRWAwCO9LdYzIrQDg97+hGVdsSGPQ==" }, "d3-force": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.0.tgz", - "integrity": "sha512-2HVQz3/VCQs0QeRNZTYb7GxoUCeb6bOzMp/cGcLa87awY9ZsPvXOGeZm0iaGBjXic6I1ysKwMn+g+5jSAdzwcg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.1.2.tgz", + "integrity": "sha512-p1vcHAUF1qH7yR+e8ip7Bs61AHjLeKkIn8Z2gzwU2lwEf2wkSpWdjXG0axudTHsVFnYGlMkFaEsVy2l8tAg1Gw==", "requires": { - "d3-collection": "1.0.4", - "d3-dispatch": "1.0.3", - "d3-quadtree": "1.0.3", - "d3-timer": "1.0.7" + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" } }, "d3-format": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.0.tgz", - "integrity": "sha512-ycfLEIzHVZC3rOvuBOKVyQXSiUyCDjeAPIj9n/wugrr+s5AcTQC2Bz6aKkubG7rQaQF0SGW/OV4UEJB9nfioFg==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.3.2.tgz", + "integrity": "sha512-Z18Dprj96ExragQ0DeGi+SYPQ7pPfRMtUXtsg/ChVIKNBCzjO8XYJvRTC1usblx52lqge56V5ect+frYTQc8WQ==" }, "d3-hierarchy": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.6.tgz", - "integrity": "sha512-nn4bhBnwWnMSoZgkBXD7vRyZ0xVUsNMQRKytWYHhP1I4qHw+qzApCTgSQTZqMdf4XXZbTMqA59hFusga+THA/g==" + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.8.tgz", + "integrity": "sha512-L+GHMSZNwTpiq4rt9GEsNcpLa4M96lXMR8M/nMG9p5hBE0jy6C+3hWtyZMenPQdwla249iJy7Nx0uKt3n+u9+w==" }, "d3-interpolate": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.2.0.tgz", - "integrity": "sha512-zLvTk8CREPFfc/2XglPQriAsXkzoRDAyBzndtKJWrZmHw7kmOWHNS11e40kPTd/oGk8P5mFJW5uBbcFQ+ybxyA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.3.2.tgz", + "integrity": "sha512-NlNKGopqaz9qM1PXh9gBF1KSCVh+jSFErrSlD/4hybwoNX/gt1d8CDbDW+3i+5UOHhjC6s6nMvRxcuoMVNgL2w==", "requires": { - "d3-color": "1.2.0" + "d3-color": "1" } }, "d3-path": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz", - "integrity": "sha1-JB6xhJvZ6egCHA0KeZ+KDo5EF2Q=" + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.7.tgz", + "integrity": "sha512-q0cW1RpvA5c5ma2rch62mX8AYaiLX0+bdaSM2wxSU9tXjU4DNvkx9qiUvjkuWCj3p22UO/hlPivujqMiR9PDzA==" }, "d3-quadtree": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz", - "integrity": "sha1-rHmH4+I/6AWpkPKOG1DTj8uCJDg=" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.5.tgz", + "integrity": "sha512-U2tjwDFbZ75JRAg8A+cqMvqPg1G3BE7UTJn3h8DHjY/pnsAfWdbJKgyfcy7zKjqGtLAmI0q8aDSeG1TVIKRaHQ==" }, "d3-scale": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.7.tgz", "integrity": "sha512-KvU92czp2/qse5tUfGms6Kjig0AhHOwkzXG0+PqIJB3ke0WUv088AHMZI0OssO9NCkXt4RP8yju9rpH8aGB7Lw==", "requires": { - "d3-array": "1.2.1", - "d3-collection": "1.0.4", - "d3-color": "1.2.0", - "d3-format": "1.3.0", - "d3-interpolate": "1.2.0", - "d3-time": "1.0.8", - "d3-time-format": "2.1.1" + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-color": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" } }, "d3-selection": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.0.tgz", - "integrity": "sha512-qgpUOg9tl5CirdqESUAu0t9MU/t3O9klYfGfyKsXEmhyxyzLpzpeh08gaxBUTQw1uXIOkr/30Ut2YRjSSxlmHA==" + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.3.2.tgz", + "integrity": "sha512-OoXdv1nZ7h2aKMVg3kaUFbLLK5jXUFAMLD/Tu5JA96mjf8f2a9ZUESGY+C36t8R1WFeWk/e55hy54Ml2I62CRQ==" }, "d3-shape": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.0.tgz", - "integrity": "sha1-RdAVOPBkuv0F6j1tLLdI/YxB93c=", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.2.2.tgz", + "integrity": "sha512-hUGEozlKecFZ2bOSNt7ENex+4Tk9uc/m0TtTEHBvitCBxUNjhzm5hS2GrrVRD/ae4IylSmxGeqX5tWC2rASMlQ==", "requires": { - "d3-path": "1.0.5" + "d3-path": "1" } }, "d3-time": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.8.tgz", - "integrity": "sha512-YRZkNhphZh3KcnBfitvF3c6E0JOFGikHZ4YqD+Lzv83ZHn1/u6yGenRU1m+KAk9J1GnZMnKcrtfvSktlA1DXNQ==" + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.10.tgz", + "integrity": "sha512-hF+NTLCaJHF/JqHN5hE8HVGAXPStEq6/omumPE/SxyHVrR7/qQxusFDo0t0c/44+sCGHthC7yNGFZIEgju0P8g==" }, "d3-time-format": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.1.tgz", - "integrity": "sha512-8kAkymq2WMfzW7e+s/IUNAtN/y3gZXGRrdGfo6R8NKPAA85UBTxZg5E61bR6nLwjPjj4d3zywSQe1CkYLPFyrw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.1.3.tgz", + "integrity": "sha512-6k0a2rZryzGm5Ihx+aFMuO1GgelgIz+7HhB4PH4OEndD5q2zGn1mDfRdNrulspOfR6JXkb2sThhDK41CSK85QA==", "requires": { - "d3-time": "1.0.8" + "d3-time": "1" } }, "d3-timer": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.7.tgz", - "integrity": "sha512-vMZXR88XujmG/L5oB96NNKH5lCWwiLM/S2HyyAQLcjWJCloK5shxta4CwOFYLZoY3AWX73v8Lgv4cCAdWtRmOA==" + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.9.tgz", + "integrity": "sha512-rT34J5HnQUHhcLvhSB9GjCkN0Ddd5Y8nCwDBG2u6wQEeYxT/Lf51fTFFkldeib/sE/J0clIe0pnCfs6g/lRbyg==" }, "d3-transition": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.1.tgz", - "integrity": "sha512-xeg8oggyQ+y5eb4J13iDgKIjUcEfIOZs2BqV/eEmXm2twx80wTzJ4tB4vaZ5BKfz7XsI/DFmQL5me6O27/5ykQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.1.3.tgz", + "integrity": "sha512-tEvo3qOXL6pZ1EzcXxFcPNxC/Ygivu5NoBY6mbzidATAeML86da+JfVIUzon3dNM6UX6zjDx+xbYDmMVtTSjuA==", "requires": { - "d3-color": "1.2.0", - "d3-dispatch": "1.0.3", - "d3-ease": "1.0.3", - "d3-interpolate": "1.2.0", - "d3-selection": "1.3.0", - "d3-timer": "1.0.7" + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" } }, "dashdash": { @@ -3185,7 +3193,7 @@ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", "dev": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "data-uri-to-buffer": { @@ -3258,7 +3266,7 @@ "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", "dev": true, "requires": { - "kind-of": "5.1.0" + "kind-of": "^5.0.2" }, "dependencies": { "kind-of": { @@ -3275,7 +3283,7 @@ "integrity": "sha1-9fj7sYp9bVCyH2QfZJ67Uiz+JPc=", "dev": true, "requires": { - "strip-bom": "3.0.0" + "strip-bom": "^3.0.0" }, "dependencies": { "strip-bom": { @@ -3298,8 +3306,8 @@ "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", "dev": true, "requires": { - "foreach": "2.0.5", - "object-keys": "1.0.11" + "foreach": "^2.0.5", + "object-keys": "^1.0.8" } }, "define-property": { @@ -3308,8 +3316,8 @@ "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", "dev": true, "requires": { - "is-descriptor": "1.0.2", - "isobject": "3.0.1" + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" }, "dependencies": { "is-accessor-descriptor": { @@ -3318,7 +3326,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -3327,7 +3335,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -3336,9 +3344,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -3350,9 +3358,9 @@ "dev": true, "optional": true, "requires": { - "ast-types": "0.11.5", - "escodegen": "1.8.1", - "esprima": "3.1.3" + "ast-types": "0.x.x", + "escodegen": "1.x.x", + "esprima": "3.x.x" }, "dependencies": { "esprima": { @@ -3370,12 +3378,12 @@ "integrity": "sha1-U+z2mf/LyzljdpGrE7rxYIGXZuU=", "dev": true, "requires": { - "globby": "6.1.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.1", - "p-map": "1.2.0", - "pify": "3.0.0", - "rimraf": "2.6.2" + "globby": "^6.1.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "p-map": "^1.1.1", + "pify": "^3.0.0", + "rimraf": "^2.2.8" }, "dependencies": { "globby": { @@ -3384,11 +3392,11 @@ "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", "dev": true, "requires": { - "array-union": "1.0.2", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" }, "dependencies": { "pify": { @@ -3419,12 +3427,12 @@ "integrity": "sha1-fP7PXvffuAL60nq49b5a5UbFt54=", "dev": true, "requires": { - "async": "1.5.2", - "bluebird": "3.5.1", - "extend-shallow": "2.0.1", - "lazy-cache": "1.0.4", - "matched": "0.4.4", - "rimraf": "2.6.2" + "async": "^1.5.2", + "bluebird": "^3.3.5", + "extend-shallow": "^2.0.1", + "lazy-cache": "^1.0.4", + "matched": "^0.4.1", + "rimraf": "^2.5.2" }, "dependencies": { "extend-shallow": { @@ -3433,7 +3441,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -3450,8 +3458,8 @@ "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=", "dev": true, "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" } }, "destroy": { @@ -3472,7 +3480,7 @@ "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, "detect-node": { @@ -3499,9 +3507,9 @@ "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", "dev": true, "requires": { - "bn.js": "4.11.8", - "miller-rabin": "4.0.1", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" } }, "dir-glob": { @@ -3510,8 +3518,8 @@ "integrity": "sha512-37qirFDz8cA5fimp9feo43fSuRo2gHwaIn6dXL8Ber1dGwUosDrGZeCCXq57WnIqE4aQ+u3eQZzsk1yOzhdwag==", "dev": true, "requires": { - "arrify": "1.0.1", - "path-type": "3.0.0" + "arrify": "^1.0.1", + "path-type": "^3.0.0" } }, "dns-equal": { @@ -3526,8 +3534,8 @@ "integrity": "sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg==", "dev": true, "requires": { - "ip": "1.1.5", - "safe-buffer": "5.1.2" + "ip": "^1.1.0", + "safe-buffer": "^5.0.1" } }, "dns-txt": { @@ -3536,7 +3544,7 @@ "integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", "dev": true, "requires": { - "buffer-indexof": "1.1.1" + "buffer-indexof": "^1.0.0" } }, "doctrine": { @@ -3545,8 +3553,8 @@ "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", "dev": true, "requires": { - "esutils": "2.0.2", - "isarray": "1.0.0" + "esutils": "^2.0.2", + "isarray": "^1.0.0" } }, "dom-converter": { @@ -3555,7 +3563,7 @@ "integrity": "sha1-pF71cnuJDJv/5tfIduexnLDhfzs=", "dev": true, "requires": { - "utila": "0.3.3" + "utila": "~0.3" }, "dependencies": { "utila": { @@ -3572,10 +3580,10 @@ "integrity": "sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs=", "dev": true, "requires": { - "custom-event": "1.0.1", - "ent": "2.2.0", - "extend": "3.0.1", - "void-elements": "2.0.1" + "custom-event": "~1.0.0", + "ent": "~2.2.0", + "extend": "^3.0.0", + "void-elements": "^2.0.0" } }, "dom-serializer": { @@ -3584,8 +3592,8 @@ "integrity": "sha1-BzxpdUbOB4DOI75KKOKT5AvDDII=", "dev": true, "requires": { - "domelementtype": "1.1.3", - "entities": "1.1.1" + "domelementtype": "~1.1.1", + "entities": "~1.1.1" }, "dependencies": { "domelementtype": { @@ -3614,7 +3622,7 @@ "integrity": "sha1-0mRvXlf2w7qxHPbLBdPArPdBJZQ=", "dev": true, "requires": { - "domelementtype": "1.3.0" + "domelementtype": "1" } }, "domino": { @@ -3628,8 +3636,8 @@ "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", "dev": true, "requires": { - "dom-serializer": "0.1.0", - "domelementtype": "1.3.0" + "dom-serializer": "0", + "domelementtype": "1" } }, "double-ended-queue": { @@ -3645,10 +3653,10 @@ "integrity": "sha512-fO3Di4tBKJpYTFHAxTU00BcfWMY9w24r/x21a6rZRbsD/ToUgGxsMbiGRmB7uVAXeGKXD9MwiLZa5E97EVgIRQ==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "stream-shift": "1.0.0" + "end-of-stream": "^1.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.0.0", + "stream-shift": "^1.0.0" } }, "each-props": { @@ -3657,8 +3665,8 @@ "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", "dev": true, "requires": { - "is-plain-object": "2.0.4", - "object.defaults": "1.1.0" + "is-plain-object": "^2.0.1", + "object.defaults": "^1.1.0" } }, "ecc-jsbn": { @@ -3668,7 +3676,7 @@ "dev": true, "optional": true, "requires": { - "jsbn": "0.1.1" + "jsbn": "~0.1.0" } }, "ee-first": { @@ -3695,13 +3703,13 @@ "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", "dev": true, "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0", - "hash.js": "1.1.5", - "hmac-drbg": "1.0.1", - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1", - "minimalistic-crypto-utils": "1.0.1" + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" } }, "emojis-list": { @@ -3722,7 +3730,7 @@ "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", "dev": true, "requires": { - "once": "1.4.0" + "once": "^1.4.0" } }, "engine.io": { @@ -3731,13 +3739,13 @@ "integrity": "sha512-D06ivJkYxyRrcEe0bTpNnBQNgP9d3xog+qZlLbui8EsMr/DouQpf5o9FzJnWYHEYE0YsFHllUv2R1dkgYZXHcA==", "dev": true, "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.4", "base64id": "1.0.0", "cookie": "0.3.1", - "debug": "3.1.0", - "engine.io-parser": "2.1.2", - "uws": "9.14.0", - "ws": "3.3.3" + "debug": "~3.1.0", + "engine.io-parser": "~2.1.0", + "uws": "~9.14.0", + "ws": "~3.3.1" }, "dependencies": { "debug": { @@ -3759,14 +3767,14 @@ "requires": { "component-emitter": "1.2.1", "component-inherit": "0.0.3", - "debug": "3.1.0", - "engine.io-parser": "2.1.2", + "debug": "~3.1.0", + "engine.io-parser": "~2.1.1", "has-cors": "1.1.0", "indexof": "0.0.1", "parseqs": "0.0.5", "parseuri": "0.0.5", - "ws": "3.3.3", - "xmlhttprequest-ssl": "1.5.5", + "ws": "~3.3.1", + "xmlhttprequest-ssl": "~1.5.4", "yeast": "0.1.2" }, "dependencies": { @@ -3788,10 +3796,10 @@ "dev": true, "requires": { "after": "0.8.2", - "arraybuffer.slice": "0.0.7", + "arraybuffer.slice": "~0.0.7", "base64-arraybuffer": "0.1.5", "blob": "0.0.4", - "has-binary2": "1.0.3" + "has-binary2": "~1.0.2" } }, "enhanced-resolve": { @@ -3800,9 +3808,9 @@ "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.4.1", - "tapable": "1.0.0" + "graceful-fs": "^4.1.2", + "memory-fs": "^0.4.0", + "tapable": "^1.0.0" } }, "ent": { @@ -3823,7 +3831,7 @@ "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", "dev": true, "requires": { - "prr": "1.0.1" + "prr": "~1.0.1" } }, "error-ex": { @@ -3832,7 +3840,7 @@ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=", "dev": true, "requires": { - "is-arrayish": "0.2.1" + "is-arrayish": "^0.2.1" } }, "error-stack-parser": { @@ -3840,7 +3848,7 @@ "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.1.tgz", "integrity": "sha1-oyArj7AxFKqbQKDjZp5IsrZaAQo=", "requires": { - "stackframe": "1.0.4" + "stackframe": "^1.0.3" } }, "es-abstract": { @@ -3849,11 +3857,11 @@ "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==", "dev": true, "requires": { - "es-to-primitive": "1.1.1", - "function-bind": "1.1.1", - "has": "1.0.3", - "is-callable": "1.1.4", - "is-regex": "1.0.4" + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" } }, "es-to-primitive": { @@ -3862,9 +3870,9 @@ "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", "dev": true, "requires": { - "is-callable": "1.1.4", - "is-date-object": "1.0.1", - "is-symbol": "1.0.1" + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" } }, "es5-ext": { @@ -3873,9 +3881,9 @@ "integrity": "sha512-TO4Vt9IhW3FzDKLDOpoA8VS9BCV4b9WTf6BqvMOgfoa8wX73F3Kh3y2J7yTstTaXlQ0k1vq4DH2vw6RSs42z+g==", "dev": true, "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1", - "next-tick": "1.0.0" + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.1", + "next-tick": "1" } }, "es6-iterator": { @@ -3884,9 +3892,9 @@ "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.44", - "es6-symbol": "3.1.1" + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" } }, "es6-map": { @@ -3895,12 +3903,12 @@ "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.44", - "es6-iterator": "2.0.3", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", + "es6-set": "~0.1.5", + "es6-symbol": "~3.1.1", + "event-emitter": "~0.3.5" } }, "es6-promise": { @@ -3915,7 +3923,7 @@ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", "dev": true, "requires": { - "es6-promise": "4.2.4" + "es6-promise": "^4.0.3" } }, "es6-set": { @@ -3924,11 +3932,11 @@ "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.44", - "es6-iterator": "2.0.3", + "d": "1", + "es5-ext": "~0.10.14", + "es6-iterator": "~2.0.1", "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" + "event-emitter": "~0.3.5" } }, "es6-symbol": { @@ -3937,8 +3945,8 @@ "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.44" + "d": "1", + "es5-ext": "~0.10.14" } }, "es6-weak-map": { @@ -3947,10 +3955,10 @@ "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.44", - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" + "d": "1", + "es5-ext": "^0.10.14", + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" } }, "escape-html": { @@ -3971,11 +3979,11 @@ "integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=", "dev": true, "requires": { - "esprima": "2.7.3", - "estraverse": "1.9.3", - "esutils": "2.0.2", - "optionator": "0.8.2", - "source-map": "0.2.0" + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.2.0" }, "dependencies": { "esprima": { @@ -3991,7 +3999,7 @@ "dev": true, "optional": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -4002,10 +4010,10 @@ "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", "dev": true, "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.1", - "estraverse": "4.2.0" + "es6-map": "^0.1.3", + "es6-weak-map": "^2.0.1", + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" }, "dependencies": { "estraverse": { @@ -4022,39 +4030,39 @@ "integrity": "sha1-5MyPoPAJ+4KaquI4VaKTYL4fbBE=", "dev": true, "requires": { - "chalk": "1.1.3", - "concat-stream": "1.6.2", - "debug": "2.6.9", - "doctrine": "1.5.0", - "es6-map": "0.1.5", - "escope": "3.6.0", - "espree": "3.5.4", - "estraverse": "4.2.0", - "esutils": "2.0.2", - "file-entry-cache": "1.3.1", - "glob": "7.1.2", - "globals": "9.18.0", - "ignore": "3.3.8", - "imurmurhash": "0.1.4", - "inquirer": "0.12.0", - "is-my-json-valid": "2.17.2", - "is-resolvable": "1.1.0", - "js-yaml": "3.11.0", - "json-stable-stringify": "1.0.1", - "levn": "0.3.0", - "lodash": "4.17.10", - "mkdirp": "0.5.1", - "optionator": "0.8.2", - "path-is-absolute": "1.0.1", - "path-is-inside": "1.0.2", - "pluralize": "1.2.1", - "progress": "1.1.8", - "require-uncached": "1.0.3", - "shelljs": "0.6.1", - "strip-json-comments": "1.0.4", - "table": "3.8.3", - "text-table": "0.2.0", - "user-home": "2.0.0" + "chalk": "^1.1.3", + "concat-stream": "^1.4.6", + "debug": "^2.1.1", + "doctrine": "^1.2.2", + "es6-map": "^0.1.3", + "escope": "^3.6.0", + "espree": "^3.1.6", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^1.1.1", + "glob": "^7.0.3", + "globals": "^9.2.0", + "ignore": "^3.1.2", + "imurmurhash": "^0.1.4", + "inquirer": "^0.12.0", + "is-my-json-valid": "^2.10.0", + "is-resolvable": "^1.0.0", + "js-yaml": "^3.5.1", + "json-stable-stringify": "^1.0.0", + "levn": "^0.3.0", + "lodash": "^4.0.0", + "mkdirp": "^0.5.0", + "optionator": "^0.8.1", + "path-is-absolute": "^1.0.0", + "path-is-inside": "^1.0.1", + "pluralize": "^1.2.1", + "progress": "^1.1.8", + "require-uncached": "^1.0.2", + "shelljs": "^0.6.0", + "strip-json-comments": "~1.0.1", + "table": "^3.7.8", + "text-table": "~0.2.0", + "user-home": "^2.0.0" }, "dependencies": { "ansi-styles": { @@ -4069,11 +4077,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "estraverse": { @@ -4096,8 +4104,8 @@ "integrity": "sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA==", "dev": true, "requires": { - "esrecurse": "4.2.1", - "estraverse": "4.2.0" + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" }, "dependencies": { "estraverse": { @@ -4114,8 +4122,8 @@ "integrity": "sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A==", "dev": true, "requires": { - "acorn": "5.5.3", - "acorn-jsx": "3.0.1" + "acorn": "^5.5.0", + "acorn-jsx": "^3.0.0" } }, "esprima": { @@ -4129,7 +4137,7 @@ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "dev": true, "requires": { - "estraverse": "4.2.0" + "estraverse": "^4.1.0" }, "dependencies": { "estraverse": { @@ -4164,8 +4172,8 @@ "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", "dev": true, "requires": { - "d": "1.0.0", - "es5-ext": "0.10.44" + "d": "1", + "es5-ext": "~0.10.14" } }, "eventemitter3": { @@ -4186,7 +4194,7 @@ "integrity": "sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI=", "dev": true, "requires": { - "original": "1.0.2" + "original": ">=0.0.5" } }, "evp_bytestokey": { @@ -4195,8 +4203,8 @@ "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", "dev": true, "requires": { - "md5.js": "1.3.4", - "safe-buffer": "5.1.2" + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" } }, "execa": { @@ -4205,13 +4213,13 @@ "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", "dev": true, "requires": { - "cross-spawn": "5.1.0", - "get-stream": "3.0.0", - "is-stream": "1.1.0", - "npm-run-path": "2.0.2", - "p-finally": "1.0.0", - "signal-exit": "3.0.2", - "strip-eof": "1.0.0" + "cross-spawn": "^5.0.1", + "get-stream": "^3.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" }, "dependencies": { "cross-spawn": { @@ -4220,9 +4228,9 @@ "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", "dev": true, "requires": { - "lru-cache": "4.1.3", - "shebang-command": "1.2.0", - "which": "1.3.1" + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" } } } @@ -4245,9 +4253,9 @@ "integrity": "sha1-SIsdHSRRyz06axks/AMPRMWFX+o=", "dev": true, "requires": { - "array-slice": "0.2.3", - "array-unique": "0.2.1", - "braces": "0.1.5" + "array-slice": "^0.2.3", + "array-unique": "^0.2.1", + "braces": "^0.1.2" }, "dependencies": { "array-slice": { @@ -4268,7 +4276,7 @@ "integrity": "sha1-wIVxEIUpHYt1/ddOqw+FlygHEeY=", "dev": true, "requires": { - "expand-range": "0.1.1" + "expand-range": "^0.1.0" } }, "expand-range": { @@ -4277,8 +4285,8 @@ "integrity": "sha1-TLjtoJk8pW+k9B/ELzy7TMrf8EQ=", "dev": true, "requires": { - "is-number": "0.1.1", - "repeat-string": "0.2.2" + "is-number": "^0.1.1", + "repeat-string": "^0.2.2" } }, "is-number": { @@ -4301,13 +4309,13 @@ "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "dev": true, "requires": { - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "posix-character-classes": "0.1.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -4316,7 +4324,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -4325,7 +4333,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -4336,7 +4344,7 @@ "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", "dev": true, "requires": { - "fill-range": "2.2.4" + "fill-range": "^2.1.0" }, "dependencies": { "fill-range": { @@ -4345,11 +4353,11 @@ "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", "dev": true, "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "3.0.0", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" + "is-number": "^2.1.0", + "isobject": "^2.0.0", + "randomatic": "^3.0.0", + "repeat-element": "^1.1.2", + "repeat-string": "^1.5.2" } }, "is-number": { @@ -4358,7 +4366,7 @@ "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" } }, "isobject": { @@ -4376,7 +4384,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -4387,7 +4395,7 @@ "integrity": "sha1-C4HrqJflo9MdHD0QL48BRB5VlEk=", "dev": true, "requires": { - "os-homedir": "1.0.2" + "os-homedir": "^1.0.1" } }, "express": { @@ -4396,36 +4404,36 @@ "integrity": "sha1-avilAjUNsyRuzEvs9rWjTSL37VM=", "dev": true, "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.5", "array-flatten": "1.1.1", "body-parser": "1.18.2", "content-disposition": "0.5.2", - "content-type": "1.0.4", + "content-type": "~1.0.4", "cookie": "0.3.1", "cookie-signature": "1.0.6", "debug": "2.6.9", - "depd": "1.1.2", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "finalhandler": "1.1.1", "fresh": "0.5.2", "merge-descriptors": "1.0.1", - "methods": "1.1.2", - "on-finished": "2.3.0", - "parseurl": "1.3.2", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", "path-to-regexp": "0.1.7", - "proxy-addr": "2.0.4", + "proxy-addr": "~2.0.3", "qs": "6.5.1", - "range-parser": "1.2.0", + "range-parser": "~1.2.0", "safe-buffer": "5.1.1", "send": "0.16.2", "serve-static": "1.13.2", "setprototypeof": "1.1.0", - "statuses": "1.4.0", - "type-is": "1.6.16", + "statuses": "~1.4.0", + "type-is": "~1.6.16", "utils-merge": "1.0.1", - "vary": "1.1.2" + "vary": "~1.1.2" }, "dependencies": { "array-flatten": { @@ -4460,8 +4468,8 @@ "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", "dev": true, "requires": { - "assign-symbols": "1.0.0", - "is-extendable": "1.0.1" + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -4470,7 +4478,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -4481,14 +4489,14 @@ "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", "dev": true, "requires": { - "array-unique": "0.3.2", - "define-property": "1.0.0", - "expand-brackets": "2.1.4", - "extend-shallow": "2.0.1", - "fragment-cache": "0.2.1", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" }, "dependencies": { "define-property": { @@ -4497,7 +4505,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "extend-shallow": { @@ -4506,7 +4514,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "is-accessor-descriptor": { @@ -4515,7 +4523,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -4524,7 +4532,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -4533,9 +4541,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -4552,9 +4560,9 @@ "integrity": "sha1-9BEl49hPLn2JpD0G2VjI94vha+E=", "dev": true, "requires": { - "ansi-gray": "0.1.1", - "color-support": "1.1.3", - "time-stamp": "1.1.0" + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "time-stamp": "^1.0.0" } }, "fast-deep-equal": { @@ -4587,7 +4595,7 @@ "integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "dev": true, "requires": { - "websocket-driver": "0.7.0" + "websocket-driver": ">=0.5.1" } }, "figures": { @@ -4596,8 +4604,8 @@ "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", "dev": true, "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" } }, "file-entry-cache": { @@ -4606,8 +4614,8 @@ "integrity": "sha1-RMYepgeuS+nBQC9B9EJwy/4zT/g=", "dev": true, "requires": { - "flat-cache": "1.3.0", - "object-assign": "4.1.1" + "flat-cache": "^1.2.1", + "object-assign": "^4.0.1" } }, "file-loader": { @@ -4616,8 +4624,8 @@ "integrity": "sha512-TGR4HU7HUsGg6GCOPJnFk06RhWgEWFLAGWiT6rcD+GRC2keU3s9RGJ+b3Z6/U73jwwNb2gKLJ7YCrp+jvU4ALg==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.4.7" + "loader-utils": "^1.0.2", + "schema-utils": "^0.4.5" } }, "file-uri-to-path": { @@ -4639,8 +4647,8 @@ "integrity": "sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=", "dev": true, "requires": { - "glob": "7.1.2", - "minimatch": "3.0.4" + "glob": "^7.0.3", + "minimatch": "^3.0.3" } }, "fill-range": { @@ -4649,10 +4657,10 @@ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-number": "3.0.0", - "repeat-string": "1.6.1", - "to-regex-range": "2.1.1" + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" }, "dependencies": { "extend-shallow": { @@ -4661,7 +4669,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -4673,12 +4681,12 @@ "dev": true, "requires": { "debug": "2.6.9", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "on-finished": "2.3.0", - "parseurl": "1.3.2", - "statuses": "1.4.0", - "unpipe": "1.0.0" + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.4.0", + "unpipe": "~1.0.0" } }, "find-cache-dir": { @@ -4687,9 +4695,9 @@ "integrity": "sha1-kojj6ePMN0hxfTnq3hfPcfww7m8=", "dev": true, "requires": { - "commondir": "1.0.1", - "make-dir": "1.3.0", - "pkg-dir": "2.0.0" + "commondir": "^1.0.1", + "make-dir": "^1.0.0", + "pkg-dir": "^2.0.0" } }, "find-up": { @@ -4698,7 +4706,7 @@ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", "dev": true, "requires": { - "locate-path": "2.0.0" + "locate-path": "^2.0.0" } }, "findup-sync": { @@ -4707,10 +4715,10 @@ "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", "dev": true, "requires": { - "detect-file": "1.0.0", - "is-glob": "3.1.0", - "micromatch": "3.1.10", - "resolve-dir": "1.0.1" + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" }, "dependencies": { "expand-tilde": { @@ -4719,7 +4727,7 @@ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "homedir-polyfill": "1.0.1" + "homedir-polyfill": "^1.0.1" } }, "global-modules": { @@ -4728,9 +4736,9 @@ "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, "requires": { - "global-prefix": "1.0.2", - "is-windows": "1.0.2", - "resolve-dir": "1.0.1" + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" } }, "global-prefix": { @@ -4739,11 +4747,11 @@ "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "dev": true, "requires": { - "expand-tilde": "2.0.2", - "homedir-polyfill": "1.0.1", - "ini": "1.3.5", - "is-windows": "1.0.2", - "which": "1.3.1" + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" } }, "is-glob": { @@ -4752,7 +4760,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.0" } }, "resolve-dir": { @@ -4761,8 +4769,8 @@ "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "dev": true, "requires": { - "expand-tilde": "2.0.2", - "global-modules": "1.0.0" + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" } } } @@ -4773,11 +4781,11 @@ "integrity": "sha1-s33IRLdqL15wgeiE98CuNE8VNHY=", "dev": true, "requires": { - "expand-tilde": "2.0.2", - "is-plain-object": "2.0.4", - "object.defaults": "1.1.0", - "object.pick": "1.3.0", - "parse-filepath": "1.0.2" + "expand-tilde": "^2.0.2", + "is-plain-object": "^2.0.3", + "object.defaults": "^1.1.0", + "object.pick": "^1.2.0", + "parse-filepath": "^1.0.1" }, "dependencies": { "expand-tilde": { @@ -4786,7 +4794,7 @@ "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "dev": true, "requires": { - "homedir-polyfill": "1.0.1" + "homedir-polyfill": "^1.0.1" } } } @@ -4803,10 +4811,10 @@ "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", "dev": true, "requires": { - "circular-json": "0.3.3", - "del": "2.2.2", - "graceful-fs": "4.1.11", - "write": "0.2.1" + "circular-json": "^0.3.1", + "del": "^2.0.2", + "graceful-fs": "^4.1.2", + "write": "^0.2.1" }, "dependencies": { "circular-json": { @@ -4821,13 +4829,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.1", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" } }, "globby": { @@ -4836,12 +4844,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "pify": { @@ -4858,8 +4866,8 @@ "integrity": "sha512-calZMC10u0FMUqoiunI2AiGIIUtUIvifNwkHhNupZH4cbNnW1Itkoh/Nf5HFYmDrwWPjrUxpkZT0KhuCq0jmGw==", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.6" + "inherits": "^2.0.1", + "readable-stream": "^2.0.4" } }, "follow-redirects": { @@ -4868,7 +4876,7 @@ "integrity": "sha512-fdrt472/9qQ6Kgjvb935ig6vJCuofpBUD14f9Vb+SLlm7xIe4Qva5gey8EKtv8lp7ahE1wilg3xL1znpVGtZIA==", "dev": true, "requires": { - "debug": "3.1.0" + "debug": "^3.1.0" }, "dependencies": { "debug": { @@ -4894,7 +4902,7 @@ "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=", "dev": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } }, "foreach": { @@ -4915,9 +4923,9 @@ "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", "dev": true, "requires": { - "asynckit": "0.4.0", + "asynckit": "^0.4.0", "combined-stream": "1.0.6", - "mime-types": "2.1.18" + "mime-types": "^2.1.12" } }, "forwarded": { @@ -4932,7 +4940,7 @@ "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", "dev": true, "requires": { - "map-cache": "0.2.2" + "map-cache": "^0.2.2" } }, "fresh": { @@ -4947,8 +4955,8 @@ "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.6" + "inherits": "^2.0.1", + "readable-stream": "^2.0.0" } }, "front-matter": { @@ -4957,7 +4965,7 @@ "integrity": "sha1-91mDufL0E75ljJPf172M5AePXNs=", "dev": true, "requires": { - "js-yaml": "3.11.0" + "js-yaml": "^3.4.6" } }, "fs-access": { @@ -4966,7 +4974,7 @@ "integrity": "sha1-1qh/JiJxzv6+wwxVNAf7mV2od3o=", "dev": true, "requires": { - "null-check": "1.0.0" + "null-check": "^1.0.0" } }, "fs-exists-sync": { @@ -4981,9 +4989,9 @@ "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "3.0.1", - "universalify": "0.1.1" + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" } }, "fs-mkdirp-stream": { @@ -4992,8 +5000,8 @@ "integrity": "sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "through2": "2.0.3" + "graceful-fs": "^4.1.11", + "through2": "^2.0.3" } }, "fs-write-stream-atomic": { @@ -5002,10 +5010,10 @@ "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "iferr": "0.1.5", - "imurmurhash": "0.1.4", - "readable-stream": "2.3.6" + "graceful-fs": "^4.1.2", + "iferr": "^0.1.5", + "imurmurhash": "^0.1.4", + "readable-stream": "1 || 2" } }, "fs.realpath": { @@ -5021,8 +5029,8 @@ "dev": true, "optional": true, "requires": { - "nan": "2.10.0", - "node-pre-gyp": "0.10.0" + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" }, "dependencies": { "abbrev": { @@ -5052,8 +5060,8 @@ "dev": true, "optional": true, "requires": { - "delegates": "1.0.0", - "readable-stream": "2.3.6" + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" } }, "balanced-match": { @@ -5068,7 +5076,7 @@ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "balanced-match": "1.0.0", + "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, @@ -5142,7 +5150,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "fs.realpath": { @@ -5159,14 +5167,14 @@ "dev": true, "optional": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "glob": { @@ -5176,12 +5184,12 @@ "dev": true, "optional": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-unicode": { @@ -5198,7 +5206,7 @@ "dev": true, "optional": true, "requires": { - "safer-buffer": "2.1.2" + "safer-buffer": "^2.1.0" } }, "ignore-walk": { @@ -5208,7 +5216,7 @@ "dev": true, "optional": true, "requires": { - "minimatch": "3.0.4" + "minimatch": "^3.0.4" } }, "inflight": { @@ -5218,8 +5226,8 @@ "dev": true, "optional": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -5241,7 +5249,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "isarray": { @@ -5257,7 +5265,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -5272,8 +5280,8 @@ "integrity": "sha512-hzXIWWet/BzWhYs2b+u7dRHlruXhwdgvlTMDKC6Cb1U7ps6Ac6yQlR39xsbjWJE377YTCtKwIXIpJ5oP+j5y8g==", "dev": true, "requires": { - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "safe-buffer": "^5.1.1", + "yallist": "^3.0.0" } }, "minizlib": { @@ -5283,7 +5291,7 @@ "dev": true, "optional": true, "requires": { - "minipass": "2.2.4" + "minipass": "^2.2.1" } }, "mkdirp": { @@ -5309,9 +5317,9 @@ "dev": true, "optional": true, "requires": { - "debug": "2.6.9", - "iconv-lite": "0.4.21", - "sax": "1.2.4" + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" } }, "node-pre-gyp": { @@ -5321,16 +5329,16 @@ "dev": true, "optional": true, "requires": { - "detect-libc": "1.0.3", - "mkdirp": "0.5.1", - "needle": "2.2.0", - "nopt": "4.0.1", - "npm-packlist": "1.1.10", - "npmlog": "4.1.2", - "rc": "1.2.7", - "rimraf": "2.6.2", - "semver": "5.5.0", - "tar": "4.4.1" + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.0", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.1.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" } }, "nopt": { @@ -5340,8 +5348,8 @@ "dev": true, "optional": true, "requires": { - "abbrev": "1.1.1", - "osenv": "0.1.5" + "abbrev": "1", + "osenv": "^0.1.4" } }, "npm-bundled": { @@ -5358,8 +5366,8 @@ "dev": true, "optional": true, "requires": { - "ignore-walk": "3.0.1", - "npm-bundled": "1.0.3" + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" } }, "npmlog": { @@ -5369,10 +5377,10 @@ "dev": true, "optional": true, "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "number-is-nan": { @@ -5394,7 +5402,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "os-homedir": { @@ -5418,8 +5426,8 @@ "dev": true, "optional": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "path-is-absolute": { @@ -5443,10 +5451,10 @@ "dev": true, "optional": true, "requires": { - "deep-extend": "0.5.1", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.5.1", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -5465,13 +5473,13 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.1", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "rimraf": { @@ -5481,7 +5489,7 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "safe-buffer": { @@ -5531,9 +5539,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -5543,7 +5551,7 @@ "dev": true, "optional": true, "requires": { - "safe-buffer": "5.1.1" + "safe-buffer": "~5.1.0" } }, "strip-ansi": { @@ -5552,7 +5560,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-json-comments": { @@ -5569,13 +5577,13 @@ "dev": true, "optional": true, "requires": { - "chownr": "1.0.1", - "fs-minipass": "1.2.5", - "minipass": "2.2.4", - "minizlib": "1.1.0", - "mkdirp": "0.5.1", - "safe-buffer": "5.1.1", - "yallist": "3.0.2" + "chownr": "^1.0.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.2.4", + "minizlib": "^1.1.0", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.1", + "yallist": "^3.0.2" } }, "util-deprecate": { @@ -5592,7 +5600,7 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2" } }, "wrappy": { @@ -5615,10 +5623,10 @@ "integrity": "sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.2" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" } }, "ftp": { @@ -5628,7 +5636,7 @@ "dev": true, "optional": true, "requires": { - "readable-stream": "1.1.14", + "readable-stream": "1.1.x", "xregexp": "2.0.0" }, "dependencies": { @@ -5646,10 +5654,10 @@ "dev": true, "optional": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -5673,14 +5681,14 @@ "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "requires": { - "aproba": "1.2.0", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.3" + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" } }, "gaze": { @@ -5690,7 +5698,7 @@ "dev": true, "optional": true, "requires": { - "globule": "1.2.1" + "globule": "^1.0.0" } }, "generate-function": { @@ -5705,7 +5713,7 @@ "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", "dev": true, "requires": { - "is-property": "1.0.2" + "is-property": "^1.0.0" } }, "get-caller-file": { @@ -5733,12 +5741,12 @@ "dev": true, "optional": true, "requires": { - "data-uri-to-buffer": "1.2.0", - "debug": "2.6.9", - "extend": "3.0.1", - "file-uri-to-path": "1.0.0", - "ftp": "0.3.10", - "readable-stream": "2.3.6" + "data-uri-to-buffer": "1", + "debug": "2", + "extend": "3", + "file-uri-to-path": "1", + "ftp": "~0.3.10", + "readable-stream": "2" } }, "get-value": { @@ -5753,7 +5761,7 @@ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", "dev": true, "requires": { - "assert-plus": "1.0.0" + "assert-plus": "^1.0.0" } }, "glob": { @@ -5762,12 +5770,12 @@ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "glob-base": { @@ -5776,8 +5784,8 @@ "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", "dev": true, "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" + "glob-parent": "^2.0.0", + "is-glob": "^2.0.0" }, "dependencies": { "glob-parent": { @@ -5786,7 +5794,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^2.0.0" } }, "is-extglob": { @@ -5801,7 +5809,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } } } @@ -5812,8 +5820,8 @@ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", "dev": true, "requires": { - "is-glob": "3.1.0", - "path-dirname": "1.0.2" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" }, "dependencies": { "is-glob": { @@ -5822,7 +5830,7 @@ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.0" } } } @@ -5833,16 +5841,16 @@ "integrity": "sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ=", "dev": true, "requires": { - "extend": "3.0.1", - "glob": "7.1.2", - "glob-parent": "3.1.0", - "is-negated-glob": "1.0.0", - "ordered-read-streams": "1.0.1", - "pumpify": "1.5.1", - "readable-stream": "2.3.6", - "remove-trailing-separator": "1.1.0", - "to-absolute-glob": "2.0.2", - "unique-stream": "2.2.1" + "extend": "^3.0.0", + "glob": "^7.1.1", + "glob-parent": "^3.1.0", + "is-negated-glob": "^1.0.0", + "ordered-read-streams": "^1.0.0", + "pumpify": "^1.3.5", + "readable-stream": "^2.1.5", + "remove-trailing-separator": "^1.0.1", + "to-absolute-glob": "^2.0.0", + "unique-stream": "^2.0.2" } }, "glob-watcher": { @@ -5851,10 +5859,10 @@ "integrity": "sha512-fK92r2COMC199WCyGUblrZKhjra3cyVMDiypDdqg1vsSDmexnbYivK1kNR4QItiNXLKmGlqan469ks67RtNa2g==", "dev": true, "requires": { - "async-done": "1.3.1", - "chokidar": "2.0.3", - "just-debounce": "1.0.0", - "object.defaults": "1.1.0" + "async-done": "^1.2.0", + "chokidar": "^2.0.0", + "just-debounce": "^1.0.0", + "object.defaults": "^1.1.0" } }, "global-modules": { @@ -5863,8 +5871,8 @@ "integrity": "sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0=", "dev": true, "requires": { - "global-prefix": "0.1.5", - "is-windows": "0.2.0" + "global-prefix": "^0.1.4", + "is-windows": "^0.2.0" }, "dependencies": { "is-windows": { @@ -5881,10 +5889,10 @@ "integrity": "sha1-jTvGuNo8qBEqFg2NSW/wRiv+948=", "dev": true, "requires": { - "homedir-polyfill": "1.0.1", - "ini": "1.3.5", - "is-windows": "0.2.0", - "which": "1.3.1" + "homedir-polyfill": "^1.0.0", + "ini": "^1.3.4", + "is-windows": "^0.2.0", + "which": "^1.2.12" }, "dependencies": { "is-windows": { @@ -5907,12 +5915,12 @@ "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", "dev": true, "requires": { - "array-union": "1.0.2", - "dir-glob": "2.0.0", - "glob": "7.1.2", - "ignore": "3.3.8", - "pify": "3.0.0", - "slash": "1.0.0" + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" } }, "globule": { @@ -5921,9 +5929,9 @@ "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==", "dev": true, "requires": { - "glob": "7.1.2", - "lodash": "4.17.10", - "minimatch": "3.0.4" + "glob": "~7.1.1", + "lodash": "~4.17.10", + "minimatch": "~3.0.2" } }, "glogg": { @@ -5932,7 +5940,7 @@ "integrity": "sha512-ynYqXLoluBKf9XGR1gA59yEJisIL7YHEH4xr3ZziHB5/yl4qWfaK8Js9jGe6gBGCSCKVqiyO30WnRZADvemUNw==", "dev": true, "requires": { - "sparkles": "1.0.1" + "sparkles": "^1.0.0" } }, "gonzales-pe-sl": { @@ -5941,7 +5949,7 @@ "integrity": "sha1-aoaLw4BkXxQf7rBCxvl/zHG1n+Y=", "dev": true, "requires": { - "minimist": "1.1.3" + "minimist": "1.1.x" }, "dependencies": { "minimist": { @@ -5964,10 +5972,10 @@ "integrity": "sha1-lXZsYB2t5Kd+0+eyttwDiBtZY2Y=", "dev": true, "requires": { - "glob-watcher": "5.0.1", - "gulp-cli": "2.0.1", - "undertaker": "1.2.0", - "vinyl-fs": "3.0.3" + "glob-watcher": "^5.0.0", + "gulp-cli": "^2.0.0", + "undertaker": "^1.0.0", + "vinyl-fs": "^3.0.0" }, "dependencies": { "camelcase": { @@ -5982,9 +5990,9 @@ "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" } }, "gulp-cli": { @@ -5993,24 +6001,24 @@ "integrity": "sha512-RxujJJdN8/O6IW2nPugl7YazhmrIEjmiVfPKrWt68r71UCaLKS71Hp0gpKT+F6qOUFtr7KqtifDKaAJPRVvMYQ==", "dev": true, "requires": { - "ansi-colors": "1.1.0", - "archy": "1.0.0", - "array-sort": "1.0.0", - "color-support": "1.1.3", - "concat-stream": "1.6.2", - "copy-props": "2.0.4", - "fancy-log": "1.3.2", - "gulplog": "1.0.0", - "interpret": "1.1.0", - "isobject": "3.0.1", - "liftoff": "2.5.0", - "matchdep": "2.0.0", - "mute-stdout": "1.0.0", - "pretty-hrtime": "1.0.3", - "replace-homedir": "1.0.0", - "semver-greatest-satisfied-range": "1.1.0", - "v8flags": "3.1.0", - "yargs": "7.1.0" + "ansi-colors": "^1.0.1", + "archy": "^1.0.0", + "array-sort": "^1.0.0", + "color-support": "^1.1.3", + "concat-stream": "^1.6.0", + "copy-props": "^2.0.1", + "fancy-log": "^1.3.2", + "gulplog": "^1.0.0", + "interpret": "^1.1.0", + "isobject": "^3.0.1", + "liftoff": "^2.5.0", + "matchdep": "^2.0.0", + "mute-stdout": "^1.0.0", + "pretty-hrtime": "^1.0.0", + "replace-homedir": "^1.0.0", + "semver-greatest-satisfied-range": "^1.1.0", + "v8flags": "^3.0.1", + "yargs": "^7.1.0" } }, "y18n": { @@ -6025,19 +6033,19 @@ "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=", "dev": true, "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "5.0.0" + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" } } } @@ -6048,11 +6056,11 @@ "integrity": "sha512-I+697f6jf+PncdTrqfuwoauxgnLG1yHRg3vlmvDgmJuEnlEHy4meBktJ/oHgfyg4tp6X25wuZqUOraVeVg97wQ==", "dev": true, "requires": { - "get-stream": "3.0.0", - "plugin-error": "0.1.2", - "through2": "2.0.3", - "vinyl": "2.1.0", - "yazl": "2.4.3" + "get-stream": "^3.0.0", + "plugin-error": "^0.1.2", + "through2": "^2.0.1", + "vinyl": "^2.1.0", + "yazl": "^2.1.0" } }, "gulplog": { @@ -6061,7 +6069,7 @@ "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", "dev": true, "requires": { - "glogg": "1.0.1" + "glogg": "^1.0.0" } }, "hammerjs": { @@ -6081,10 +6089,10 @@ "integrity": "sha1-Ywo13+ApS8KB7a5v/F0yn8eYLcw=", "dev": true, "requires": { - "async": "1.5.2", - "optimist": "0.6.1", - "source-map": "0.4.4", - "uglify-js": "2.8.29" + "async": "^1.4.0", + "optimist": "^0.6.1", + "source-map": "^0.4.4", + "uglify-js": "^2.6" }, "dependencies": { "source-map": { @@ -6093,7 +6101,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } }, "uglify-js": { @@ -6103,9 +6111,9 @@ "dev": true, "optional": true, "requires": { - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" + "source-map": "~0.5.1", + "uglify-to-browserify": "~1.0.0", + "yargs": "~3.10.0" }, "dependencies": { "source-map": { @@ -6131,8 +6139,8 @@ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", "dev": true, "requires": { - "ajv": "5.5.2", - "har-schema": "2.0.0" + "ajv": "^5.1.0", + "har-schema": "^2.0.0" }, "dependencies": { "ajv": { @@ -6141,10 +6149,10 @@ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } } } @@ -6155,7 +6163,7 @@ "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", "dev": true, "requires": { - "function-bind": "1.1.1" + "function-bind": "^1.1.1" } }, "has-ansi": { @@ -6164,7 +6172,7 @@ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "has-binary2": { @@ -6202,7 +6210,7 @@ "integrity": "sha1-omHEwqbGZ+DHe3AKfyl8Oe86pYk=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^2.0.1" }, "dependencies": { "is-extglob": { @@ -6217,7 +6225,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } } } @@ -6240,9 +6248,9 @@ "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "1.0.0", - "isobject": "3.0.1" + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" } }, "has-values": { @@ -6251,8 +6259,8 @@ "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", "dev": true, "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" + "is-number": "^3.0.0", + "kind-of": "^4.0.0" }, "dependencies": { "kind-of": { @@ -6261,7 +6269,7 @@ "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -6272,8 +6280,8 @@ "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "dev": true, "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.2" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "hash.js": { @@ -6282,8 +6290,8 @@ "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", "dev": true, "requires": { - "inherits": "2.0.3", - "minimalistic-assert": "1.0.1" + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" } }, "hawk": { @@ -6293,10 +6301,10 @@ "dev": true, "optional": true, "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" + "boom": "2.x.x", + "cryptiles": "2.x.x", + "hoek": "2.x.x", + "sntp": "1.x.x" } }, "he": { @@ -6312,8 +6320,8 @@ "dev": true, "optional": true, "requires": { - "lodash": "4.17.10", - "request": "2.87.0" + "lodash": "^4.0.0", + "request": "^2.0.0" } }, "hmac-drbg": { @@ -6322,9 +6330,9 @@ "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "dev": true, "requires": { - "hash.js": "1.1.5", - "minimalistic-assert": "1.0.1", - "minimalistic-crypto-utils": "1.0.1" + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" } }, "hoek": { @@ -6339,7 +6347,7 @@ "integrity": "sha1-TCu8inWJmP7r9e1oWA921GdotLw=", "dev": true, "requires": { - "parse-passwd": "1.0.0" + "parse-passwd": "^1.0.0" } }, "hosted-git-info": { @@ -6354,10 +6362,10 @@ "integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "dev": true, "requires": { - "inherits": "2.0.3", - "obuf": "1.1.2", - "readable-stream": "2.3.6", - "wbuf": "1.7.3" + "inherits": "^2.0.1", + "obuf": "^1.0.0", + "readable-stream": "^2.0.1", + "wbuf": "^1.1.0" } }, "html-entities": { @@ -6372,13 +6380,13 @@ "integrity": "sha512-Qr2JC9nsjK8oCrEmuB430ZIA8YWbF3D5LSjywD75FTuXmeqacwHgIM8wp3vHYzzPbklSjp53RdmDuzR4ub2HzA==", "dev": true, "requires": { - "camel-case": "3.0.0", - "clean-css": "4.1.11", - "commander": "2.16.0", - "he": "1.1.1", - "param-case": "2.1.1", - "relateurl": "0.2.7", - "uglify-js": "3.4.7" + "camel-case": "3.0.x", + "clean-css": "4.1.x", + "commander": "2.16.x", + "he": "1.1.x", + "param-case": "2.1.x", + "relateurl": "0.2.x", + "uglify-js": "3.4.x" }, "dependencies": { "clean-css": { @@ -6387,7 +6395,7 @@ "integrity": "sha1-Ls3xRaujj1R0DybO/Q/z4D4SXWo=", "dev": true, "requires": { - "source-map": "0.5.6" + "source-map": "0.5.x" } }, "commander": { @@ -6404,12 +6412,12 @@ "integrity": "sha1-sBq71yOsqqeze2r0SS69oD2d03s=", "dev": true, "requires": { - "html-minifier": "3.5.19", - "loader-utils": "0.2.17", - "lodash": "4.17.10", - "pretty-error": "2.1.1", - "tapable": "1.0.0", - "toposort": "1.0.7", + "html-minifier": "^3.2.3", + "loader-utils": "^0.2.16", + "lodash": "^4.17.3", + "pretty-error": "^2.0.2", + "tapable": "^1.0.0", + "toposort": "^1.0.0", "util.promisify": "1.0.0" }, "dependencies": { @@ -6419,10 +6427,10 @@ "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", "dev": true, "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" } } } @@ -6433,10 +6441,10 @@ "integrity": "sha1-zHDQWln2VC5D8OaFyYLhTJJKnv4=", "dev": true, "requires": { - "domelementtype": "1.3.0", - "domhandler": "2.1.0", - "domutils": "1.1.6", - "readable-stream": "1.0.34" + "domelementtype": "1", + "domhandler": "2.1", + "domutils": "1.1", + "readable-stream": "1.0" }, "dependencies": { "domutils": { @@ -6445,7 +6453,7 @@ "integrity": "sha1-vdw94Jm5ou+sxRxiPyj0FuzFdIU=", "dev": true, "requires": { - "domelementtype": "1.3.0" + "domelementtype": "1" } }, "isarray": { @@ -6460,10 +6468,10 @@ "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", "isarray": "0.0.1", - "string_decoder": "0.10.31" + "string_decoder": "~0.10.x" } }, "string_decoder": { @@ -6486,10 +6494,10 @@ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", "dev": true, "requires": { - "depd": "1.1.2", + "depd": "~1.1.2", "inherits": "2.0.3", "setprototypeof": "1.1.0", - "statuses": "1.4.0" + "statuses": ">= 1.4.0 < 2" } }, "http-parser-js": { @@ -6504,9 +6512,9 @@ "integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==", "dev": true, "requires": { - "eventemitter3": "3.1.0", - "follow-redirects": "1.5.0", - "requires-port": "1.0.0" + "eventemitter3": "^3.0.0", + "follow-redirects": "^1.0.0", + "requires-port": "^1.0.0" } }, "http-proxy-agent": { @@ -6515,7 +6523,7 @@ "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", "dev": true, "requires": { - "agent-base": "4.2.0", + "agent-base": "4", "debug": "3.1.0" }, "dependencies": { @@ -6536,10 +6544,10 @@ "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { - "http-proxy": "1.17.0", - "is-glob": "4.0.0", - "lodash": "4.17.10", - "micromatch": "3.1.10" + "http-proxy": "^1.16.2", + "is-glob": "^4.0.0", + "lodash": "^4.17.5", + "micromatch": "^3.1.9" } }, "http-signature": { @@ -6548,9 +6556,9 @@ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", "dev": true, "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.14.1" + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "httpntlm": { @@ -6559,8 +6567,8 @@ "integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=", "dev": true, "requires": { - "httpreq": "0.4.24", - "underscore": "1.7.0" + "httpreq": ">=0.4.22", + "underscore": "~1.7.0" } }, "httpreq": { @@ -6581,8 +6589,8 @@ "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==", "dev": true, "requires": { - "agent-base": "4.2.0", - "debug": "3.1.0" + "agent-base": "^4.1.0", + "debug": "^3.1.0" }, "dependencies": { "debug": { @@ -6639,7 +6647,7 @@ "integrity": "sha1-qmzzbnInYShcs3HsZRn1PiQ1sKk=", "dev": true, "requires": { - "import-from": "2.1.0" + "import-from": "^2.1.0" } }, "import-from": { @@ -6648,7 +6656,7 @@ "integrity": "sha1-M1238qev/VOqpHHUuAId7ja387E=", "dev": true, "requires": { - "resolve-from": "3.0.0" + "resolve-from": "^3.0.0" } }, "import-local": { @@ -6657,8 +6665,8 @@ "integrity": "sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==", "dev": true, "requires": { - "pkg-dir": "2.0.0", - "resolve-cwd": "2.0.0" + "pkg-dir": "^2.0.0", + "resolve-cwd": "^2.0.0" } }, "imurmurhash": { @@ -6680,7 +6688,7 @@ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", "dev": true, "requires": { - "repeating": "2.0.1" + "repeating": "^2.0.0" } }, "indexof": { @@ -6702,8 +6710,8 @@ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" + "once": "^1.3.0", + "wrappy": "1" } }, "inherits": { @@ -6724,19 +6732,19 @@ "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", "dev": true, "requires": { - "ansi-escapes": "1.4.0", - "ansi-regex": "2.1.1", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-width": "2.2.0", - "figures": "1.7.0", - "lodash": "4.17.10", - "readline2": "1.0.1", - "run-async": "0.1.0", - "rx-lite": "3.1.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "through": "2.3.8" + "ansi-escapes": "^1.1.0", + "ansi-regex": "^2.0.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "readline2": "^1.0.1", + "run-async": "^0.1.0", + "rx-lite": "^3.1.2", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" }, "dependencies": { "ansi-styles": { @@ -6751,11 +6759,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "supports-color": { @@ -6772,7 +6780,7 @@ "integrity": "sha1-rp+/k7mEh4eF1QqN4bNWlWBYz1w=", "dev": true, "requires": { - "meow": "3.7.0" + "meow": "^3.3.0" } }, "interpret": { @@ -6787,7 +6795,7 @@ "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "dev": true, "requires": { - "loose-envify": "1.3.1" + "loose-envify": "^1.0.0" } }, "invert-kv": { @@ -6814,8 +6822,8 @@ "integrity": "sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA==", "dev": true, "requires": { - "is-relative": "1.0.0", - "is-windows": "1.0.2" + "is-relative": "^1.0.0", + "is-windows": "^1.0.1" } }, "is-accessor-descriptor": { @@ -6824,7 +6832,7 @@ "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -6833,7 +6841,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -6850,7 +6858,7 @@ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", "dev": true, "requires": { - "binary-extensions": "1.11.0" + "binary-extensions": "^1.0.0" } }, "is-buffer": { @@ -6865,7 +6873,7 @@ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=", "dev": true, "requires": { - "builtin-modules": "1.1.1" + "builtin-modules": "^1.0.0" } }, "is-callable": { @@ -6880,7 +6888,7 @@ "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -6889,7 +6897,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -6906,9 +6914,9 @@ "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, "requires": { - "is-accessor-descriptor": "0.1.6", - "is-data-descriptor": "0.1.4", - "kind-of": "5.1.0" + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" }, "dependencies": { "kind-of": { @@ -6937,7 +6945,7 @@ "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", "dev": true, "requires": { - "is-primitive": "2.0.0" + "is-primitive": "^2.0.0" } }, "is-extendable": { @@ -6958,7 +6966,7 @@ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-fullwidth-code-point": { @@ -6967,7 +6975,7 @@ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "requires": { - "number-is-nan": "1.0.1" + "number-is-nan": "^1.0.0" } }, "is-glob": { @@ -6976,7 +6984,7 @@ "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", "dev": true, "requires": { - "is-extglob": "2.1.1" + "is-extglob": "^2.1.1" } }, "is-my-ip-valid": { @@ -6991,11 +6999,11 @@ "integrity": "sha512-IBhBslgngMQN8DDSppmgDv7RNrlFotuuDsKcrCP3+HbFaVivIBU7u9oiiErw8sH4ynx3+gOGQ3q2otkgiSi6kg==", "dev": true, "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "is-my-ip-valid": "1.0.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" + "generate-function": "^2.0.0", + "generate-object-property": "^1.1.0", + "is-my-ip-valid": "^1.0.0", + "jsonpointer": "^4.0.0", + "xtend": "^4.0.0" } }, "is-negated-glob": { @@ -7010,7 +7018,7 @@ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -7019,7 +7027,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -7030,7 +7038,7 @@ "integrity": "sha512-OTiixgpZAT1M4NHgS5IguFp/Vz2VI3U7Goh4/HA1adtwyLtSBrxYlcSYkhpAE07s4fKEcjrFxyvtQBND4vFQyQ==", "dev": true, "requires": { - "is-number": "4.0.0" + "is-number": "^4.0.0" }, "dependencies": { "is-number": { @@ -7053,7 +7061,7 @@ "integrity": "sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ==", "dev": true, "requires": { - "is-path-inside": "1.0.1" + "is-path-inside": "^1.0.0" } }, "is-path-inside": { @@ -7062,7 +7070,7 @@ "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", "dev": true, "requires": { - "path-is-inside": "1.0.2" + "path-is-inside": "^1.0.1" } }, "is-plain-object": { @@ -7071,7 +7079,7 @@ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" } }, "is-posix-bracket": { @@ -7098,7 +7106,7 @@ "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", "dev": true, "requires": { - "has": "1.0.3" + "has": "^1.0.1" } }, "is-relative": { @@ -7107,7 +7115,7 @@ "integrity": "sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA==", "dev": true, "requires": { - "is-unc-path": "1.0.0" + "is-unc-path": "^1.0.0" } }, "is-resolvable": { @@ -7140,7 +7148,7 @@ "integrity": "sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ==", "dev": true, "requires": { - "unc-path-regex": "0.1.2" + "unc-path-regex": "^0.1.2" } }, "is-utf8": { @@ -7203,20 +7211,20 @@ "integrity": "sha1-ZcfXPUxNqE1POsMQuRj7C4Azczs=", "dev": true, "requires": { - "abbrev": "1.0.9", - "async": "1.5.2", - "escodegen": "1.8.1", - "esprima": "2.7.3", - "glob": "5.0.15", - "handlebars": "4.0.11", - "js-yaml": "3.11.0", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "once": "1.4.0", - "resolve": "1.1.7", - "supports-color": "3.2.3", - "which": "1.3.1", - "wordwrap": "1.0.0" + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" }, "dependencies": { "esprima": { @@ -7231,11 +7239,11 @@ "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=", "dev": true, "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "has-flag": { @@ -7256,7 +7264,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "^1.0.0" } } } @@ -7267,18 +7275,18 @@ "integrity": "sha512-duj6AlLcsWNwUpfyfHt0nWIeRiZpuShnP40YTxOGQgtaN8fd6JYSxsvxUphTDy8V5MfDXo4s/xVCIIvVCO808g==", "dev": true, "requires": { - "async": "2.6.1", - "compare-versions": "3.3.0", - "fileset": "2.0.3", - "istanbul-lib-coverage": "1.2.0", - "istanbul-lib-hook": "1.2.1", - "istanbul-lib-instrument": "1.10.1", - "istanbul-lib-report": "1.1.4", - "istanbul-lib-source-maps": "1.2.5", - "istanbul-reports": "1.3.0", - "js-yaml": "3.11.0", - "mkdirp": "0.5.1", - "once": "1.4.0" + "async": "^2.1.4", + "compare-versions": "^3.1.0", + "fileset": "^2.0.2", + "istanbul-lib-coverage": "^1.2.0", + "istanbul-lib-hook": "^1.2.0", + "istanbul-lib-instrument": "^1.10.1", + "istanbul-lib-report": "^1.1.4", + "istanbul-lib-source-maps": "^1.2.4", + "istanbul-reports": "^1.3.0", + "js-yaml": "^3.7.0", + "mkdirp": "^0.5.1", + "once": "^1.4.0" }, "dependencies": { "async": { @@ -7287,7 +7295,7 @@ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "dev": true, "requires": { - "lodash": "4.17.10" + "lodash": "^4.17.10" } } } @@ -7298,10 +7306,10 @@ "integrity": "sha512-a5SPObZgS0jB/ixaKSMdn6n/gXSrK2S6q/UfRJBT3e6gQmVjwZROTODQsYW5ZNwOu78hG62Y3fWlebaVOL0C+w==", "dev": true, "requires": { - "convert-source-map": "1.5.1", - "istanbul-lib-instrument": "1.10.1", - "loader-utils": "1.1.0", - "schema-utils": "0.3.0" + "convert-source-map": "^1.5.0", + "istanbul-lib-instrument": "^1.7.3", + "loader-utils": "^1.1.0", + "schema-utils": "^0.3.0" }, "dependencies": { "ajv": { @@ -7310,10 +7318,10 @@ "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", "dev": true, "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.1.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" } }, "schema-utils": { @@ -7322,7 +7330,7 @@ "integrity": "sha1-9YdyIs4+kx7a4DnxfrNxbnE3+M8=", "dev": true, "requires": { - "ajv": "5.5.2" + "ajv": "^5.0.0" } } } @@ -7339,7 +7347,7 @@ "integrity": "sha512-eLAMkPG9FU0v5L02lIkcj/2/Zlz9OuluaXikdr5iStk8FDbSwAixTK9TkYxbF0eNnzAJTwM2fkV2A1tpsIp4Jg==", "dev": true, "requires": { - "append-transform": "1.0.0" + "append-transform": "^1.0.0" } }, "istanbul-lib-instrument": { @@ -7348,13 +7356,13 @@ "integrity": "sha512-1dYuzkOCbuR5GRJqySuZdsmsNKPL3PTuyPevQfoCXJePT9C8y1ga75neU+Tuy9+yS3G/dgx8wgOmp2KLpgdoeQ==", "dev": true, "requires": { - "babel-generator": "6.26.1", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "istanbul-lib-coverage": "1.2.0", - "semver": "5.5.0" + "babel-generator": "^6.18.0", + "babel-template": "^6.16.0", + "babel-traverse": "^6.18.0", + "babel-types": "^6.18.0", + "babylon": "^6.18.0", + "istanbul-lib-coverage": "^1.2.0", + "semver": "^5.3.0" } }, "istanbul-lib-report": { @@ -7363,10 +7371,10 @@ "integrity": "sha512-Azqvq5tT0U09nrncK3q82e/Zjkxa4tkFZv7E6VcqP0QCPn6oNljDPfrZEC/umNXds2t7b8sRJfs6Kmpzt8m2kA==", "dev": true, "requires": { - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "path-parse": "1.0.5", - "supports-color": "3.2.3" + "istanbul-lib-coverage": "^1.2.0", + "mkdirp": "^0.5.1", + "path-parse": "^1.0.5", + "supports-color": "^3.1.2" }, "dependencies": { "has-flag": { @@ -7381,7 +7389,7 @@ "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", "dev": true, "requires": { - "has-flag": "1.0.0" + "has-flag": "^1.0.0" } } } @@ -7392,11 +7400,11 @@ "integrity": "sha512-8O2T/3VhrQHn0XcJbP1/GN7kXMiRAlPi+fj3uEHrjBD8Oz7Py0prSC25C09NuAZS6bgW1NNKAvCSHZXB0irSGA==", "dev": true, "requires": { - "debug": "3.1.0", - "istanbul-lib-coverage": "1.2.0", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "source-map": "0.5.6" + "debug": "^3.1.0", + "istanbul-lib-coverage": "^1.2.0", + "mkdirp": "^0.5.1", + "rimraf": "^2.6.1", + "source-map": "^0.5.3" }, "dependencies": { "debug": { @@ -7416,7 +7424,7 @@ "integrity": "sha512-y2Z2IMqE1gefWUaVjrBm0mSKvUkaBy9Vqz8iwr/r40Y9hBbIteH5wqHG/9DLTfJ9xUnUT2j7A3+VVJ6EaYBllA==", "dev": true, "requires": { - "handlebars": "4.0.11" + "handlebars": "^4.0.3" } }, "jasmine": { @@ -7425,9 +7433,9 @@ "integrity": "sha1-awicChFXax8W3xG4AUbZHU6Lij4=", "dev": true, "requires": { - "exit": "0.1.2", - "glob": "7.1.2", - "jasmine-core": "2.8.0" + "exit": "^0.1.2", + "glob": "^7.0.6", + "jasmine-core": "~2.8.0" }, "dependencies": { "jasmine-core": { @@ -7450,7 +7458,7 @@ "integrity": "sha1-k8zC3MQQKMXd1GBlWAdIOfLe6qg=", "dev": true, "requires": { - "diff": "3.5.0" + "diff": "^3.2.0" } }, "jasmine-spec-reporter": { @@ -7486,8 +7494,8 @@ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.11.0.tgz", "integrity": "sha512-saJstZWv7oNeOyBh3+Dx1qWzhW0+e6/8eDzo7p5rDFqxntSztloLtuKu+Ejhtq82jsilwOIZYsCz+lIjthg1Hw==", "requires": { - "argparse": "1.0.10", - "esprima": "4.0.0" + "argparse": "^1.0.7", + "esprima": "^4.0.0" } }, "jsbn": { @@ -7527,7 +7535,7 @@ "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", "dev": true, "requires": { - "jsonify": "0.0.0" + "jsonify": "~0.0.0" } }, "json-stringify-safe": { @@ -7554,7 +7562,7 @@ "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.6" } }, "jsonify": { @@ -7587,11 +7595,11 @@ "integrity": "sha512-5W8NUaFRFRqTOL7ZDDrx5qWHJyBXy6velVudIzQUSoqAAYqzSh2Z7/m0Rf1QbmQJccegD0r+YZxBjzqoBiEeJQ==", "dev": true, "requires": { - "core-js": "2.3.0", - "es6-promise": "3.0.2", - "lie": "3.1.1", - "pako": "1.0.6", - "readable-stream": "2.0.6" + "core-js": "~2.3.0", + "es6-promise": "~3.0.2", + "lie": "~3.1.0", + "pako": "~1.0.2", + "readable-stream": "~2.0.6" }, "dependencies": { "core-js": { @@ -7618,12 +7626,12 @@ "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" } }, "string_decoder": { @@ -7646,31 +7654,31 @@ "integrity": "sha1-TS25QChQpmVR+nhLAWT7CCTtjEs=", "dev": true, "requires": { - "bluebird": "3.5.1", - "body-parser": "1.18.2", - "chokidar": "1.7.0", - "colors": "1.1.2", - "combine-lists": "1.0.1", - "connect": "3.6.6", - "core-js": "2.5.7", - "di": "0.0.1", - "dom-serialize": "2.2.1", - "expand-braces": "0.1.2", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "http-proxy": "1.17.0", - "isbinaryfile": "3.0.2", - "lodash": "4.17.10", - "log4js": "2.7.0", - "mime": "1.6.0", - "minimatch": "3.0.4", - "optimist": "0.6.1", - "qjobs": "1.2.0", - "range-parser": "1.2.0", - "rimraf": "2.6.2", - "safe-buffer": "5.1.2", + "bluebird": "^3.3.0", + "body-parser": "^1.16.1", + "chokidar": "^1.4.1", + "colors": "^1.1.0", + "combine-lists": "^1.0.0", + "connect": "^3.6.0", + "core-js": "^2.2.0", + "di": "^0.0.1", + "dom-serialize": "^2.2.0", + "expand-braces": "^0.1.1", + "glob": "^7.1.1", + "graceful-fs": "^4.1.2", + "http-proxy": "^1.13.0", + "isbinaryfile": "^3.0.0", + "lodash": "^4.17.4", + "log4js": "^2.3.9", + "mime": "^1.3.4", + "minimatch": "^3.0.2", + "optimist": "^0.6.1", + "qjobs": "^1.1.4", + "range-parser": "^1.2.0", + "rimraf": "^2.6.0", + "safe-buffer": "^5.0.1", "socket.io": "2.0.4", - "source-map": "0.6.1", + "source-map": "^0.6.1", "tmp": "0.0.33", "useragent": "2.2.1" }, @@ -7681,8 +7689,8 @@ "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", "dev": true, "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" + "micromatch": "^2.1.5", + "normalize-path": "^2.0.0" } }, "arr-diff": { @@ -7691,7 +7699,7 @@ "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", "dev": true, "requires": { - "arr-flatten": "1.1.0" + "arr-flatten": "^1.0.1" } }, "array-unique": { @@ -7706,9 +7714,9 @@ "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", "dev": true, "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" + "expand-range": "^1.8.1", + "preserve": "^0.2.0", + "repeat-element": "^1.1.2" } }, "chokidar": { @@ -7717,15 +7725,15 @@ "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", "dev": true, "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.2.4", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" + "anymatch": "^1.3.0", + "async-each": "^1.0.0", + "fsevents": "^1.0.0", + "glob-parent": "^2.0.0", + "inherits": "^2.0.1", + "is-binary-path": "^1.0.0", + "is-glob": "^2.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.0.0" } }, "expand-brackets": { @@ -7734,7 +7742,7 @@ "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", "dev": true, "requires": { - "is-posix-bracket": "0.1.1" + "is-posix-bracket": "^0.1.0" } }, "extglob": { @@ -7743,7 +7751,7 @@ "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "glob-parent": { @@ -7752,7 +7760,7 @@ "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", "dev": true, "requires": { - "is-glob": "2.0.1" + "is-glob": "^2.0.0" } }, "is-extglob": { @@ -7767,7 +7775,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } }, "kind-of": { @@ -7776,7 +7784,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } }, "micromatch": { @@ -7785,19 +7793,19 @@ "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", "dev": true, "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" + "arr-diff": "^2.0.0", + "array-unique": "^0.2.1", + "braces": "^1.8.2", + "expand-brackets": "^0.1.4", + "extglob": "^0.3.1", + "filename-regex": "^2.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.1", + "kind-of": "^3.0.2", + "normalize-path": "^2.0.1", + "object.omit": "^2.0.0", + "parse-glob": "^3.0.4", + "regex-cache": "^0.4.2" } }, "source-map": { @@ -7814,8 +7822,8 @@ "integrity": "sha1-IWh5xorATY1RQOmWGboEtZr9Rs8=", "dev": true, "requires": { - "fs-access": "1.0.1", - "which": "1.3.1" + "fs-access": "^1.0.0", + "which": "^1.2.1" } }, "karma-cli": { @@ -7824,7 +7832,7 @@ "integrity": "sha1-rmw8WKMTodALRRZMRVubhs4X+WA=", "dev": true, "requires": { - "resolve": "1.7.1" + "resolve": "^1.1.6" } }, "karma-coverage-istanbul-reporter": { @@ -7833,8 +7841,8 @@ "integrity": "sha512-UcgrHkFehI5+ivMouD8NH/UOHiX4oCAtwaANylzPFdcAuD52fnCUuelacq2gh8tZ4ydhU3+xiXofSq7j5Ehygw==", "dev": true, "requires": { - "istanbul-api": "1.3.1", - "minimatch": "3.0.4" + "istanbul-api": "^1.3.1", + "minimatch": "^3.0.4" } }, "karma-jasmine": { @@ -7855,7 +7863,7 @@ "integrity": "sha512-HcPqdAusNez/ywa+biN4EphGz62MmQyPggUsDfsHqa7tSe4jdsxgvTKuDfIazjL+IOxpVWyT7Pr4dhAV+sxX5Q==", "dev": true, "requires": { - "source-map-support": "0.5.6" + "source-map-support": "^0.5.5" } }, "karma-spec-reporter": { @@ -7864,7 +7872,7 @@ "integrity": "sha1-SDDccUihVcfXoYbmMjOaDYD63sM=", "dev": true, "requires": { - "colors": "1.1.2" + "colors": "^1.1.2" } }, "killable": { @@ -7885,7 +7893,7 @@ "integrity": "sha1-PTvNhgDnv971MjHHOf8FOu1WDkQ=", "dev": true, "requires": { - "graceful-fs": "4.1.11" + "graceful-fs": "^4.1.11" } }, "known-css-properties": { @@ -7900,8 +7908,8 @@ "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls=", "dev": true, "requires": { - "default-resolution": "2.0.0", - "es6-weak-map": "2.0.2" + "default-resolution": "^2.0.0", + "es6-weak-map": "^2.0.1" } }, "lazy-cache": { @@ -7916,7 +7924,7 @@ "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", "dev": true, "requires": { - "readable-stream": "2.3.6" + "readable-stream": "^2.0.5" } }, "lcid": { @@ -7925,7 +7933,7 @@ "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", "dev": true, "requires": { - "invert-kv": "1.0.0" + "invert-kv": "^1.0.0" } }, "lead": { @@ -7934,7 +7942,7 @@ "integrity": "sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI=", "dev": true, "requires": { - "flush-write-stream": "1.0.3" + "flush-write-stream": "^1.0.2" } }, "leb": { @@ -7949,15 +7957,15 @@ "integrity": "sha512-8HFGuWmL3FhQR0aH89escFNBQH/nEiYPP2ltDFdQw2chE28Yx2E3lhAIq9Y2saYwLSwa699s4dBVEfCY8Drf7Q==", "dev": true, "requires": { - "clone": "2.1.2", - "errno": "0.1.7", - "graceful-fs": "4.1.11", - "image-size": "0.5.5", - "mime": "1.6.0", - "mkdirp": "0.5.1", - "promise": "7.3.1", - "request": "2.87.0", - "source-map": "0.6.1" + "clone": "^2.1.2", + "errno": "^0.1.1", + "graceful-fs": "^4.1.2", + "image-size": "~0.5.0", + "mime": "^1.4.1", + "mkdirp": "^0.5.0", + "promise": "^7.1.1", + "request": "^2.83.0", + "source-map": "~0.6.0" }, "dependencies": { "clone": { @@ -7981,9 +7989,9 @@ "integrity": "sha512-KNTsgCE9tMOM70+ddxp9yyt9iHqgmSs0yTZc5XH5Wo+g80RWRIYNqE58QJKm/yMud5wZEvz50ugRDuzVIkyahg==", "dev": true, "requires": { - "clone": "2.1.1", - "loader-utils": "1.1.0", - "pify": "3.0.0" + "clone": "^2.1.1", + "loader-utils": "^1.1.0", + "pify": "^3.0.0" } }, "levn": { @@ -7992,8 +8000,8 @@ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" } }, "libbase64": { @@ -8033,7 +8041,7 @@ "integrity": "sha512-iwuNFMWbXS76WiQXJBTs8/7Tby4NQnY8AIkBMuJG5El79UT8zWrJQMfpW+KRXt4Y2Bs5uk+Myg/MO7ROSF8jzA==", "dev": true, "requires": { - "ejs": "2.6.1" + "ejs": "^2.5.7" } }, "lie": { @@ -8042,7 +8050,7 @@ "integrity": "sha1-mkNrLMd0bKWd56QfpGmz77dr2H4=", "dev": true, "requires": { - "immediate": "3.0.6" + "immediate": "~3.0.5" } }, "liftoff": { @@ -8051,14 +8059,14 @@ "integrity": "sha1-IAkpG7Mc6oYbvxCnwVooyvdcMew=", "dev": true, "requires": { - "extend": "3.0.1", - "findup-sync": "2.0.0", - "fined": "1.1.0", - "flagged-respawn": "1.0.0", - "is-plain-object": "2.0.4", - "object.map": "1.0.1", - "rechoir": "0.6.2", - "resolve": "1.7.1" + "extend": "^3.0.0", + "findup-sync": "^2.0.0", + "fined": "^1.0.1", + "flagged-respawn": "^1.0.0", + "is-plain-object": "^2.0.4", + "object.map": "^1.0.0", + "rechoir": "^0.6.2", + "resolve": "^1.1.7" } }, "load-json-file": { @@ -8067,11 +8075,11 @@ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "parse-json": "2.2.0", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "strip-bom": "2.0.0" + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" }, "dependencies": { "pify": { @@ -8094,9 +8102,9 @@ "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", "dev": true, "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0" } }, "locate-path": { @@ -8105,8 +8113,8 @@ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", "dev": true, "requires": { - "p-locate": "2.0.0", - "path-exists": "3.0.0" + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" } }, "lodash": { @@ -8159,7 +8167,7 @@ "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", "dev": true, "requires": { - "chalk": "2.2.2" + "chalk": "^2.0.1" } }, "log4js": { @@ -8168,18 +8176,18 @@ "integrity": "sha512-FyTwaPJfbfiK2AHc9ct/oFHNN4bJj0IQeqdO/LaDHhfjeBi8fnZU5rPcHOZhkYV0Aes31Ow+St1YTCluPtzs5g==", "dev": true, "requires": { - "amqplib": "0.5.2", - "axios": "0.15.3", - "circular-json": "0.5.4", - "date-format": "1.2.0", - "debug": "3.1.0", - "hipchat-notifier": "1.1.0", - "loggly": "1.1.1", - "mailgun-js": "0.18.0", - "nodemailer": "2.7.2", - "redis": "2.8.0", - "semver": "5.5.0", - "slack-node": "0.2.0", + "amqplib": "^0.5.2", + "axios": "^0.15.3", + "circular-json": "^0.5.4", + "date-format": "^1.2.0", + "debug": "^3.1.0", + "hipchat-notifier": "^1.1.0", + "loggly": "^1.1.0", + "mailgun-js": "^0.18.0", + "nodemailer": "^2.5.0", + "redis": "^2.7.1", + "semver": "^5.5.0", + "slack-node": "~0.2.0", "streamroller": "0.7.0" }, "dependencies": { @@ -8201,9 +8209,9 @@ "dev": true, "optional": true, "requires": { - "json-stringify-safe": "5.0.1", - "request": "2.75.0", - "timespan": "2.3.0" + "json-stringify-safe": "5.0.x", + "request": "2.75.x", + "timespan": "2.3.x" }, "dependencies": { "ansi-styles": { @@ -8241,11 +8249,11 @@ "dev": true, "optional": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "form-data": { @@ -8255,9 +8263,9 @@ "dev": true, "optional": true, "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.6", - "mime-types": "2.1.18" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.5", + "mime-types": "^2.1.11" } }, "har-validator": { @@ -8267,10 +8275,10 @@ "dev": true, "optional": true, "requires": { - "chalk": "1.1.3", - "commander": "2.15.1", - "is-my-json-valid": "2.17.2", - "pinkie-promise": "2.0.1" + "chalk": "^1.1.1", + "commander": "^2.9.0", + "is-my-json-valid": "^2.12.4", + "pinkie-promise": "^2.0.0" } }, "http-signature": { @@ -8280,9 +8288,9 @@ "dev": true, "optional": true, "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.14.1" + "assert-plus": "^0.2.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" } }, "node-uuid": { @@ -8306,27 +8314,27 @@ "dev": true, "optional": true, "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.7.0", - "bl": "1.1.2", - "caseless": "0.11.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.0.0", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", - "node-uuid": "1.4.8", - "oauth-sign": "0.8.2", - "qs": "6.2.3", - "stringstream": "0.0.6", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.4.3" + "aws-sign2": "~0.6.0", + "aws4": "^1.2.1", + "bl": "~1.1.2", + "caseless": "~0.11.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.0", + "forever-agent": "~0.6.1", + "form-data": "~2.0.0", + "har-validator": "~2.0.6", + "hawk": "~3.1.3", + "http-signature": "~1.1.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.7", + "node-uuid": "~1.4.7", + "oauth-sign": "~0.8.1", + "qs": "~6.2.0", + "stringstream": "~0.0.4", + "tough-cookie": "~2.3.0", + "tunnel-agent": "~0.4.1" } }, "supports-color": { @@ -8357,8 +8365,8 @@ "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==", "dev": true, "requires": { - "es6-symbol": "3.1.1", - "object.assign": "4.1.0" + "es6-symbol": "^3.1.1", + "object.assign": "^4.1.0" } }, "long": { @@ -8379,7 +8387,7 @@ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", "dev": true, "requires": { - "js-tokens": "3.0.2" + "js-tokens": "^3.0.0" } }, "loud-rejection": { @@ -8388,8 +8396,8 @@ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", "dev": true, "requires": { - "currently-unhandled": "0.4.1", - "signal-exit": "3.0.2" + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" } }, "lower-case": { @@ -8404,8 +8412,8 @@ "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", "dev": true, "requires": { - "pseudomap": "1.0.2", - "yallist": "2.1.2" + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "mailcomposer": { @@ -8426,15 +8434,15 @@ "dev": true, "optional": true, "requires": { - "async": "2.6.1", - "debug": "3.1.0", - "form-data": "2.3.2", - "inflection": "1.12.0", - "is-stream": "1.1.0", - "path-proxy": "1.0.0", - "promisify-call": "2.0.4", - "proxy-agent": "3.0.0", - "tsscmp": "1.0.5" + "async": "~2.6.0", + "debug": "~3.1.0", + "form-data": "~2.3.0", + "inflection": "~1.12.0", + "is-stream": "^1.1.0", + "path-proxy": "~1.0.0", + "promisify-call": "^2.0.2", + "proxy-agent": "~3.0.0", + "tsscmp": "~1.0.0" }, "dependencies": { "async": { @@ -8444,7 +8452,7 @@ "dev": true, "optional": true, "requires": { - "lodash": "4.17.10" + "lodash": "^4.17.10" } }, "debug": { @@ -8465,7 +8473,7 @@ "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", "dev": true, "requires": { - "pify": "3.0.0" + "pify": "^3.0.0" } }, "make-error": { @@ -8480,7 +8488,7 @@ "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.2" } }, "map-cache": { @@ -8501,7 +8509,7 @@ "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", "dev": true, "requires": { - "object-visit": "1.0.1" + "object-visit": "^1.0.0" } }, "matchdep": { @@ -8510,9 +8518,9 @@ "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4=", "dev": true, "requires": { - "findup-sync": "2.0.0", - "micromatch": "3.1.10", - "resolve": "1.7.1", + "findup-sync": "^2.0.0", + "micromatch": "^3.0.4", + "resolve": "^1.4.0", "stack-trace": "0.0.10" } }, @@ -8522,15 +8530,15 @@ "integrity": "sha1-Vte36xgDPwz5vFLrIJD6x9weifo=", "dev": true, "requires": { - "arr-union": "3.1.0", - "async-array-reduce": "0.2.1", - "extend-shallow": "2.0.1", - "fs-exists-sync": "0.1.0", - "glob": "7.1.2", - "has-glob": "0.1.1", - "is-valid-glob": "0.3.0", - "lazy-cache": "2.0.2", - "resolve-dir": "0.1.1" + "arr-union": "^3.1.0", + "async-array-reduce": "^0.2.0", + "extend-shallow": "^2.0.1", + "fs-exists-sync": "^0.1.0", + "glob": "^7.0.5", + "has-glob": "^0.1.1", + "is-valid-glob": "^0.3.0", + "lazy-cache": "^2.0.1", + "resolve-dir": "^0.1.0" }, "dependencies": { "extend-shallow": { @@ -8539,7 +8547,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "lazy-cache": { @@ -8548,7 +8556,7 @@ "integrity": "sha1-uRkKT5EzVGlIQIWfio9whNiCImQ=", "dev": true, "requires": { - "set-getter": "0.1.0" + "set-getter": "^0.1.0" } } } @@ -8565,8 +8573,8 @@ "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", "dev": true, "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, "media-typer": { @@ -8581,7 +8589,7 @@ "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", "dev": true, "requires": { - "mimic-fn": "1.2.0" + "mimic-fn": "^1.0.0" } }, "memory-fs": { @@ -8590,8 +8598,8 @@ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", "dev": true, "requires": { - "errno": "0.1.7", - "readable-stream": "2.3.6" + "errno": "^0.1.3", + "readable-stream": "^2.0.1" } }, "meow": { @@ -8600,16 +8608,16 @@ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", "dev": true, "requires": { - "camelcase-keys": "2.1.0", - "decamelize": "1.2.0", - "loud-rejection": "1.6.0", - "map-obj": "1.0.1", - "minimist": "1.2.0", - "normalize-package-data": "2.4.0", - "object-assign": "4.1.1", - "read-pkg-up": "1.0.1", - "redent": "1.0.0", - "trim-newlines": "1.0.0" + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" }, "dependencies": { "minimist": { @@ -8644,19 +8652,19 @@ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "braces": "2.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "extglob": "2.0.4", - "fragment-cache": "0.2.1", - "kind-of": "6.0.2", - "nanomatch": "1.2.9", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" } }, "miller-rabin": { @@ -8665,8 +8673,8 @@ "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", "dev": true, "requires": { - "bn.js": "4.11.8", - "brorand": "1.1.0" + "bn.js": "^4.0.0", + "brorand": "^1.0.1" } }, "mime": { @@ -8687,7 +8695,7 @@ "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", "dev": true, "requires": { - "mime-db": "1.33.0" + "mime-db": "~1.33.0" } }, "mimic-fn": { @@ -8702,9 +8710,9 @@ "integrity": "sha512-XWuB3G61Rtasq/gLe7cp5cuozehE6hN+E4sxCamRR/WDiHTg+f7ZIAS024r8UJQffY+e2gGELXQZgQoFDfNDCg==", "dev": true, "requires": { - "@webpack-contrib/schema-utils": "1.0.0-beta.0", - "loader-utils": "1.1.0", - "webpack-sources": "1.1.0" + "@webpack-contrib/schema-utils": "^1.0.0-beta.0", + "loader-utils": "^1.1.0", + "webpack-sources": "^1.1.0" } }, "minimalistic-assert": { @@ -8725,7 +8733,7 @@ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "brace-expansion": "1.1.11" + "brace-expansion": "^1.1.7" } }, "minimist": { @@ -8740,16 +8748,16 @@ "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==", "dev": true, "requires": { - "concat-stream": "1.6.2", - "duplexify": "3.6.0", - "end-of-stream": "1.4.1", - "flush-write-stream": "1.0.3", - "from2": "2.3.0", - "parallel-transform": "1.1.0", - "pump": "2.0.1", - "pumpify": "1.5.1", - "stream-each": "1.2.3", - "through2": "2.0.3" + "concat-stream": "^1.5.0", + "duplexify": "^3.4.2", + "end-of-stream": "^1.1.0", + "flush-write-stream": "^1.0.0", + "from2": "^2.1.0", + "parallel-transform": "^1.1.0", + "pump": "^2.0.1", + "pumpify": "^1.3.3", + "stream-each": "^1.1.0", + "through2": "^2.0.0" } }, "mixin-deep": { @@ -8758,8 +8766,8 @@ "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", "dev": true, "requires": { - "for-in": "1.0.2", - "is-extendable": "1.0.1" + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" }, "dependencies": { "is-extendable": { @@ -8768,7 +8776,7 @@ "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", "dev": true, "requires": { - "is-plain-object": "2.0.4" + "is-plain-object": "^2.0.4" } } } @@ -8779,8 +8787,8 @@ "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=", "dev": true, "requires": { - "for-in": "0.1.8", - "is-extendable": "0.1.1" + "for-in": "^0.1.3", + "is-extendable": "^0.1.1" }, "dependencies": { "for-in": { @@ -8817,12 +8825,12 @@ "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "dev": true, "requires": { - "aproba": "1.2.0", - "copy-concurrently": "1.0.5", - "fs-write-stream-atomic": "1.0.10", - "mkdirp": "0.5.1", - "rimraf": "2.6.2", - "run-queue": "1.0.3" + "aproba": "^1.1.1", + "copy-concurrently": "^1.0.0", + "fs-write-stream-atomic": "^1.0.8", + "mkdirp": "^0.5.1", + "rimraf": "^2.5.4", + "run-queue": "^1.0.3" } }, "ms": { @@ -8837,8 +8845,8 @@ "integrity": "sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g==", "dev": true, "requires": { - "dns-packet": "1.3.1", - "thunky": "1.0.2" + "dns-packet": "^1.3.1", + "thunky": "^1.0.2" } }, "multicast-dns-service-types": { @@ -8872,18 +8880,18 @@ "integrity": "sha512-n8R9bS8yQ6eSXaV6jHUpKzD8gLsin02w1HSFiegwrs9E098Ylhw5jdyKPaYqvHknHaSCKTPp7C8dGCQ0q9koXA==", "dev": true, "requires": { - "arr-diff": "4.0.0", - "array-unique": "0.3.2", - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "fragment-cache": "0.2.1", - "is-odd": "2.0.0", - "is-windows": "1.0.2", - "kind-of": "6.0.2", - "object.pick": "1.3.0", - "regex-not": "1.0.2", - "snapdragon": "0.8.2", - "to-regex": "3.0.2" + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-odd": "^2.0.0", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" } }, "negotiator": { @@ -8917,7 +8925,7 @@ "integrity": "sha512-IO2zBWMAyAWZgK6zbPhmR3tNRgW+jfi/Z+Xkvaa42w6eYNQ8bEwYv7uxZo/3MQJ5RglxZ+6KsDLXPzjN+ZUEZw==", "dev": true, "requires": { - "deep-freeze-strict": "1.1.1" + "deep-freeze-strict": "^1.1.1" } }, "ngrx-store-logger": { @@ -8931,7 +8939,7 @@ "integrity": "sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==", "dev": true, "requires": { - "lower-case": "1.1.4" + "lower-case": "^1.1.1" } }, "node-forge": { @@ -8953,18 +8961,18 @@ "dev": true, "optional": true, "requires": { - "fstream": "1.0.11", - "glob": "7.1.2", - "graceful-fs": "4.1.11", - "mkdirp": "0.5.1", - "nopt": "3.0.6", - "npmlog": "4.1.2", - "osenv": "0.1.5", - "request": "2.87.0", - "rimraf": "2.6.2", - "semver": "5.3.0", - "tar": "2.2.1", - "which": "1.3.1" + "fstream": "^1.0.0", + "glob": "^7.0.3", + "graceful-fs": "^4.1.2", + "mkdirp": "^0.5.0", + "nopt": "2 || 3", + "npmlog": "0 || 1 || 2 || 3 || 4", + "osenv": "0", + "request": "^2.87.0", + "rimraf": "2", + "semver": "~5.3.0", + "tar": "^2.0.0", + "which": "1" }, "dependencies": { "semver": { @@ -8982,28 +8990,28 @@ "integrity": "sha512-5AzFzdoIMb89hBGMZglEegffzgRg+ZFoUmisQ8HI4j1KDdpx13J0taNp2y9xPbur6W61gepGDDotGBVQ7mfUCg==", "dev": true, "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.2.0", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.12.0", - "domain-browser": "1.2.0", - "events": "1.1.1", - "https-browserify": "1.0.0", - "os-browserify": "0.3.0", + "assert": "^1.1.1", + "browserify-zlib": "^0.2.0", + "buffer": "^4.3.0", + "console-browserify": "^1.1.0", + "constants-browserify": "^1.0.0", + "crypto-browserify": "^3.11.0", + "domain-browser": "^1.1.1", + "events": "^1.0.0", + "https-browserify": "^1.0.0", + "os-browserify": "^0.3.0", "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.6", - "stream-browserify": "2.0.1", - "stream-http": "2.8.3", - "string_decoder": "1.1.1", - "timers-browserify": "2.0.10", + "process": "^0.11.10", + "punycode": "^1.2.4", + "querystring-es3": "^0.2.0", + "readable-stream": "^2.3.3", + "stream-browserify": "^2.0.1", + "stream-http": "^2.7.2", + "string_decoder": "^1.0.0", + "timers-browserify": "^2.0.4", "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.3", + "url": "^0.11.0", + "util": "^0.10.3", "vm-browserify": "0.0.4" }, "dependencies": { @@ -9022,25 +9030,25 @@ "dev": true, "optional": true, "requires": { - "async-foreach": "0.1.3", - "chalk": "1.1.3", - "cross-spawn": "3.0.1", - "gaze": "1.1.3", - "get-stdin": "4.0.1", - "glob": "7.1.2", - "in-publish": "2.0.0", - "lodash.assign": "4.2.0", - "lodash.clonedeep": "4.5.0", - "lodash.mergewith": "4.6.1", - "meow": "3.7.0", - "mkdirp": "0.5.1", - "nan": "2.10.0", - "node-gyp": "3.8.0", - "npmlog": "4.1.2", + "async-foreach": "^0.1.3", + "chalk": "^1.1.1", + "cross-spawn": "^3.0.0", + "gaze": "^1.0.0", + "get-stdin": "^4.0.1", + "glob": "^7.0.3", + "in-publish": "^2.0.0", + "lodash.assign": "^4.2.0", + "lodash.clonedeep": "^4.3.2", + "lodash.mergewith": "^4.6.0", + "meow": "^3.7.0", + "mkdirp": "^0.5.1", + "nan": "^2.10.0", + "node-gyp": "^3.8.0", + "npmlog": "^4.0.0", "request": "2.87.0", - "sass-graph": "2.2.4", - "stdout-stream": "1.4.0", - "true-case-path": "1.0.2" + "sass-graph": "^2.2.4", + "stdout-stream": "^1.4.0", + "true-case-path": "^1.0.2" }, "dependencies": { "ansi-styles": { @@ -9057,11 +9065,11 @@ "dev": true, "optional": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "supports-color": { @@ -9096,8 +9104,8 @@ "dev": true, "optional": true, "requires": { - "ip": "1.1.5", - "smart-buffer": "1.1.15" + "ip": "^1.1.2", + "smart-buffer": "^1.0.4" } } } @@ -9164,7 +9172,7 @@ "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", "dev": true, "requires": { - "abbrev": "1.0.9" + "abbrev": "1" } }, "normalize-package-data": { @@ -9173,10 +9181,10 @@ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==", "dev": true, "requires": { - "hosted-git-info": "2.6.0", - "is-builtin-module": "1.0.0", - "semver": "5.5.0", - "validate-npm-package-license": "3.0.3" + "hosted-git-info": "^2.1.4", + "is-builtin-module": "^1.0.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" } }, "normalize-path": { @@ -9185,7 +9193,7 @@ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "remove-trailing-separator": "1.1.0" + "remove-trailing-separator": "^1.0.1" } }, "normalize-range": { @@ -9205,7 +9213,7 @@ "integrity": "sha1-vGHLtFbXnLMiB85HygUTb/Ln1u4=", "dev": true, "requires": { - "once": "1.4.0" + "once": "^1.3.2" } }, "npm-package-arg": { @@ -9214,10 +9222,10 @@ "integrity": "sha512-zYbhP2k9DbJhA0Z3HKUePUgdB1x7MfIfKssC+WLPFMKTBZKpZh5m13PgexJjCq6KW7j17r0jHWcCpxEqnnncSA==", "dev": true, "requires": { - "hosted-git-info": "2.6.0", - "osenv": "0.1.5", - "semver": "5.5.0", - "validate-npm-package-name": "3.0.0" + "hosted-git-info": "^2.6.0", + "osenv": "^0.1.5", + "semver": "^5.5.0", + "validate-npm-package-name": "^3.0.0" } }, "npm-registry-client": { @@ -9226,18 +9234,18 @@ "integrity": "sha512-Qs6P6nnopig+Y8gbzpeN/dkt+n7IyVd8f45NTMotGk6Qo7GfBmzwYx6jRLoOOgKiMnaQfYxsuyQlD8Mc3guBhg==", "dev": true, "requires": { - "concat-stream": "1.6.2", - "graceful-fs": "4.1.11", - "normalize-package-data": "2.4.0", - "npm-package-arg": "6.1.0", - "npmlog": "4.1.2", - "once": "1.4.0", - "request": "2.87.0", - "retry": "0.10.1", - "safe-buffer": "5.1.2", - "semver": "5.5.0", - "slide": "1.1.6", - "ssri": "5.3.0" + "concat-stream": "^1.5.2", + "graceful-fs": "^4.1.6", + "normalize-package-data": "~1.0.1 || ^2.0.0", + "npm-package-arg": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0", + "npmlog": "2 || ^3.1.0 || ^4.0.0", + "once": "^1.3.3", + "request": "^2.74.0", + "retry": "^0.10.0", + "safe-buffer": "^5.1.1", + "semver": "2 >=2.2.1 || 3.x || 4 || 5", + "slide": "^1.1.3", + "ssri": "^5.2.4" } }, "npm-run-path": { @@ -9246,7 +9254,7 @@ "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", "dev": true, "requires": { - "path-key": "2.0.1" + "path-key": "^2.0.0" } }, "npmlog": { @@ -9255,10 +9263,10 @@ "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "requires": { - "are-we-there-yet": "1.1.5", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" } }, "nth-check": { @@ -9267,7 +9275,7 @@ "integrity": "sha1-mSms32KPwsQQmN6rgqxYDPFJquQ=", "dev": true, "requires": { - "boolbase": "1.0.0" + "boolbase": "~1.0.0" } }, "null-check": { @@ -9312,9 +9320,9 @@ "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", "dev": true, "requires": { - "copy-descriptor": "0.1.1", - "define-property": "0.2.5", - "kind-of": "3.2.2" + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" }, "dependencies": { "define-property": { @@ -9323,7 +9331,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "kind-of": { @@ -9332,7 +9340,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -9349,7 +9357,7 @@ "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.0" } }, "object.assign": { @@ -9358,10 +9366,10 @@ "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", "dev": true, "requires": { - "define-properties": "1.1.2", - "function-bind": "1.1.1", - "has-symbols": "1.0.0", - "object-keys": "1.0.11" + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" } }, "object.defaults": { @@ -9370,10 +9378,10 @@ "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=", "dev": true, "requires": { - "array-each": "1.0.1", - "array-slice": "1.1.0", - "for-own": "1.0.0", - "isobject": "3.0.1" + "array-each": "^1.0.1", + "array-slice": "^1.0.0", + "for-own": "^1.0.0", + "isobject": "^3.0.0" } }, "object.getownpropertydescriptors": { @@ -9382,8 +9390,8 @@ "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", "dev": true, "requires": { - "define-properties": "1.1.2", - "es-abstract": "1.12.0" + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" } }, "object.map": { @@ -9392,8 +9400,8 @@ "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc=", "dev": true, "requires": { - "for-own": "1.0.0", - "make-iterator": "1.0.1" + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" } }, "object.omit": { @@ -9402,8 +9410,8 @@ "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", "dev": true, "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" + "for-own": "^0.1.4", + "is-extendable": "^0.1.1" }, "dependencies": { "for-own": { @@ -9412,7 +9420,7 @@ "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", "dev": true, "requires": { - "for-in": "1.0.2" + "for-in": "^1.0.1" } } } @@ -9423,7 +9431,7 @@ "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", "dev": true, "requires": { - "isobject": "3.0.1" + "isobject": "^3.0.1" } }, "object.reduce": { @@ -9432,8 +9440,8 @@ "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=", "dev": true, "requires": { - "for-own": "1.0.0", - "make-iterator": "1.0.1" + "for-own": "^1.0.0", + "make-iterator": "^1.0.0" } }, "obuf": { @@ -9463,7 +9471,7 @@ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { - "wrappy": "1.0.2" + "wrappy": "1" } }, "onetime": { @@ -9478,7 +9486,7 @@ "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", "dev": true, "requires": { - "is-wsl": "1.1.0" + "is-wsl": "^1.1.0" } }, "optimist": { @@ -9487,8 +9495,8 @@ "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", "dev": true, "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" }, "dependencies": { "wordwrap": { @@ -9505,12 +9513,12 @@ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", "dev": true, "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" } }, "ordered-read-streams": { @@ -9519,7 +9527,7 @@ "integrity": "sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4=", "dev": true, "requires": { - "readable-stream": "2.3.6" + "readable-stream": "^2.0.1" } }, "original": { @@ -9528,7 +9536,7 @@ "integrity": "sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg==", "dev": true, "requires": { - "url-parse": "1.4.3" + "url-parse": "^1.4.3" } }, "os-browserify": { @@ -9549,7 +9557,7 @@ "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", "dev": true, "requires": { - "lcid": "1.0.0" + "lcid": "^1.0.0" } }, "os-tmpdir": { @@ -9564,8 +9572,8 @@ "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" } }, "p-finally": { @@ -9580,7 +9588,7 @@ "integrity": "sha512-Y/OtIaXtUPr4/YpMv1pCL5L5ed0rumAaAeBSj12F+bSlMdys7i8oQF/GUJmfpTS/QoaRrS/k6pma29haJpsMng==", "dev": true, "requires": { - "p-try": "1.0.0" + "p-try": "^1.0.0" } }, "p-locate": { @@ -9589,7 +9597,7 @@ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", "dev": true, "requires": { - "p-limit": "1.2.0" + "p-limit": "^1.1.0" } }, "p-map": { @@ -9611,14 +9619,14 @@ "dev": true, "optional": true, "requires": { - "agent-base": "4.2.0", - "debug": "3.1.0", - "get-uri": "2.0.2", - "http-proxy-agent": "2.1.0", - "https-proxy-agent": "2.2.1", - "pac-resolver": "3.0.0", - "raw-body": "2.3.2", - "socks-proxy-agent": "3.0.1" + "agent-base": "^4.2.0", + "debug": "^3.1.0", + "get-uri": "^2.0.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "pac-resolver": "^3.0.0", + "raw-body": "^2.2.0", + "socks-proxy-agent": "^3.0.0" }, "dependencies": { "debug": { @@ -9640,11 +9648,11 @@ "dev": true, "optional": true, "requires": { - "co": "4.6.0", - "degenerator": "1.0.4", - "ip": "1.1.5", - "netmask": "1.0.6", - "thunkify": "2.1.2" + "co": "^4.6.0", + "degenerator": "^1.0.4", + "ip": "^1.1.5", + "netmask": "^1.0.6", + "thunkify": "^2.1.2" } }, "pako": { @@ -9659,9 +9667,9 @@ "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=", "dev": true, "requires": { - "cyclist": "0.2.2", - "inherits": "2.0.3", - "readable-stream": "2.3.6" + "cyclist": "~0.2.2", + "inherits": "^2.0.3", + "readable-stream": "^2.1.5" } }, "param-case": { @@ -9670,7 +9678,7 @@ "integrity": "sha1-35T9jPZTHs915r75oIWPvHK+Ikc=", "dev": true, "requires": { - "no-case": "2.3.2" + "no-case": "^2.2.0" } }, "parse-asn1": { @@ -9679,11 +9687,11 @@ "integrity": "sha512-KPx7flKXg775zZpnp9SxJlz00gTd4BmJ2yJufSc44gMCRrRQ7NSzAcSJQfifuOLgW6bEi+ftrALtsgALeB2Adw==", "dev": true, "requires": { - "asn1.js": "4.10.1", - "browserify-aes": "1.2.0", - "create-hash": "1.2.0", - "evp_bytestokey": "1.0.3", - "pbkdf2": "3.0.16" + "asn1.js": "^4.0.0", + "browserify-aes": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3" } }, "parse-filepath": { @@ -9692,9 +9700,9 @@ "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE=", "dev": true, "requires": { - "is-absolute": "1.0.0", - "map-cache": "0.2.2", - "path-root": "0.1.1" + "is-absolute": "^1.0.0", + "map-cache": "^0.2.0", + "path-root": "^0.1.1" } }, "parse-glob": { @@ -9703,10 +9711,10 @@ "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", "dev": true, "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" + "glob-base": "^0.3.0", + "is-dotfile": "^1.0.0", + "is-extglob": "^1.0.0", + "is-glob": "^2.0.0" }, "dependencies": { "is-extglob": { @@ -9721,7 +9729,7 @@ "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", "dev": true, "requires": { - "is-extglob": "1.0.0" + "is-extglob": "^1.0.0" } } } @@ -9732,7 +9740,7 @@ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "dev": true, "requires": { - "error-ex": "1.3.1" + "error-ex": "^1.2.0" } }, "parse-passwd": { @@ -9753,7 +9761,7 @@ "integrity": "sha1-1SCKNzjkZ2bikbouoXNoSSGouJ0=", "dev": true, "requires": { - "better-assert": "1.0.2" + "better-assert": "~1.0.0" } }, "parseuri": { @@ -9762,7 +9770,7 @@ "integrity": "sha1-gCBKUNTbt3m/3G6+J3jZDkvOMgo=", "dev": true, "requires": { - "better-assert": "1.0.2" + "better-assert": "~1.0.0" } }, "parseurl": { @@ -9783,8 +9791,8 @@ "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", "dev": true, "requires": { - "process": "0.11.10", - "util": "0.10.3" + "process": "^0.11.1", + "util": "^0.10.3" } }, "path-browserify": { @@ -9836,7 +9844,7 @@ "dev": true, "optional": true, "requires": { - "inflection": "1.3.8" + "inflection": "~1.3.0" }, "dependencies": { "inflection": { @@ -9854,7 +9862,7 @@ "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc=", "dev": true, "requires": { - "path-root-regex": "0.1.2" + "path-root-regex": "^0.1.0" } }, "path-root-regex": { @@ -9875,7 +9883,7 @@ "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", "dev": true, "requires": { - "pify": "3.0.0" + "pify": "^3.0.0" } }, "pbkdf2": { @@ -9884,11 +9892,11 @@ "integrity": "sha512-y4CXP3thSxqf7c0qmOF+9UeOTrifiVTIM+u7NWlq+PRsHbr7r7dpCmvzrZxa96JJUNi0Y5w9VqG5ZNeCVMoDcA==", "dev": true, "requires": { - "create-hash": "1.2.0", - "create-hmac": "1.1.7", - "ripemd160": "2.0.2", - "safe-buffer": "5.1.2", - "sha.js": "2.4.11" + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" } }, "performance-now": { @@ -9915,7 +9923,7 @@ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", "dev": true, "requires": { - "pinkie": "2.0.4" + "pinkie": "^2.0.0" } }, "pkg-dir": { @@ -9924,7 +9932,7 @@ "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", "dev": true, "requires": { - "find-up": "2.1.0" + "find-up": "^2.1.0" } }, "plugin-error": { @@ -9933,11 +9941,11 @@ "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", "dev": true, "requires": { - "ansi-cyan": "0.1.1", - "ansi-red": "0.1.1", - "arr-diff": "1.1.0", - "arr-union": "2.1.0", - "extend-shallow": "1.1.4" + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" }, "dependencies": { "arr-diff": { @@ -9946,8 +9954,8 @@ "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "array-slice": "0.2.3" + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" } }, "arr-union": { @@ -9968,7 +9976,7 @@ "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", "dev": true, "requires": { - "kind-of": "1.1.0" + "kind-of": "^1.1.0" } }, "kind-of": { @@ -9991,9 +9999,9 @@ "integrity": "sha512-icBXCFQxzlK2PMepOM0QeEdPPFSLAaXXeuKOv5AClJlMy1oVCBrkDGJ12IZYesI/BF8mpeVco3vRCmgeBb4+hw==", "dev": true, "requires": { - "async": "1.5.2", - "debug": "2.6.9", - "mkdirp": "0.5.1" + "async": "^1.5.2", + "debug": "^2.2.0", + "mkdirp": "0.5.x" } }, "posix-character-classes": { @@ -10008,9 +10016,9 @@ "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==", "dev": true, "requires": { - "chalk": "2.4.1", - "source-map": "0.6.1", - "supports-color": "5.4.0" + "chalk": "^2.4.1", + "source-map": "^0.6.1", + "supports-color": "^5.4.0" }, "dependencies": { "chalk": { @@ -10019,9 +10027,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "source-map": { @@ -10038,10 +10046,10 @@ "integrity": "sha512-5l327iI75POonjxkXgdRCUS+AlzAdBx4pOvMEhTKTCjb1p8IEeVR9yx3cPbmN7LIWJLbfnIXxAhoB4jpD0c/Cw==", "dev": true, "requires": { - "postcss": "6.0.23", - "postcss-value-parser": "3.3.0", - "read-cache": "1.0.0", - "resolve": "1.7.1" + "postcss": "^6.0.1", + "postcss-value-parser": "^3.2.3", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" } }, "postcss-load-config": { @@ -10050,8 +10058,8 @@ "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==", "dev": true, "requires": { - "cosmiconfig": "4.0.0", - "import-cwd": "2.1.0" + "cosmiconfig": "^4.0.0", + "import-cwd": "^2.0.0" } }, "postcss-loader": { @@ -10060,10 +10068,10 @@ "integrity": "sha512-hgiWSc13xVQAq25cVw80CH0l49ZKlAnU1hKPOdRrNj89bokRr/bZF2nT+hebPPF9c9xs8c3gw3Fr2nxtmXYnNg==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "postcss": "6.0.23", - "postcss-load-config": "2.0.0", - "schema-utils": "0.4.7" + "loader-utils": "^1.1.0", + "postcss": "^6.0.0", + "postcss-load-config": "^2.0.0", + "schema-utils": "^0.4.0" } }, "postcss-url": { @@ -10072,11 +10080,11 @@ "integrity": "sha512-QMV5mA+pCYZQcUEPQkmor9vcPQ2MT+Ipuu8qdi1gVxbNiIiErEGft+eny1ak19qALoBkccS5AHaCaCDzh7b9MA==", "dev": true, "requires": { - "mime": "1.6.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "postcss": "6.0.23", - "xxhashjs": "0.2.2" + "mime": "^1.4.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.0", + "postcss": "^6.0.1", + "xxhashjs": "^0.2.1" } }, "postcss-value-parser": { @@ -10103,8 +10111,8 @@ "integrity": "sha1-X0+HyPkeWuPzuoerTPXgOxoX8aM=", "dev": true, "requires": { - "renderkid": "2.0.1", - "utila": "0.4.0" + "renderkid": "^2.0.1", + "utila": "~0.4" } }, "pretty-hrtime": { @@ -10138,7 +10146,7 @@ "dev": true, "optional": true, "requires": { - "asap": "2.0.6" + "asap": "~2.0.3" } }, "promise-inflight": { @@ -10154,7 +10162,7 @@ "dev": true, "optional": true, "requires": { - "with-callback": "1.0.2" + "with-callback": "^1.0.2" } }, "protractor": { @@ -10163,22 +10171,22 @@ "integrity": "sha512-6TSYqMhUUzxr4/wN0ttSISqPMKvcVRXF4k8jOEpGWD8OioLak4KLgfzHK9FJ49IrjzRrZ+Mx1q2Op8Rk0zEcnQ==", "dev": true, "requires": { - "@types/node": "6.0.111", - "@types/q": "0.0.32", - "@types/selenium-webdriver": "3.0.10", - "blocking-proxy": "1.0.1", - "browserstack": "1.5.1", - "chalk": "1.1.3", - "glob": "7.1.2", + "@types/node": "^6.0.46", + "@types/q": "^0.0.32", + "@types/selenium-webdriver": "^3.0.0", + "blocking-proxy": "^1.0.0", + "browserstack": "^1.5.1", + "chalk": "^1.1.3", + "glob": "^7.0.3", "jasmine": "2.8.0", - "jasminewd2": "2.2.0", - "optimist": "0.6.1", + "jasminewd2": "^2.1.0", + "optimist": "~0.6.0", "q": "1.4.1", - "saucelabs": "1.5.0", + "saucelabs": "^1.5.0", "selenium-webdriver": "3.6.0", - "source-map-support": "0.4.18", + "source-map-support": "~0.4.0", "webdriver-js-extender": "2.0.0", - "webdriver-manager": "12.1.0" + "webdriver-manager": "^12.0.6" }, "dependencies": { "ansi-styles": { @@ -10193,11 +10201,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "del": { @@ -10206,13 +10214,13 @@ "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", "dev": true, "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.1", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" + "globby": "^5.0.0", + "is-path-cwd": "^1.0.0", + "is-path-in-cwd": "^1.0.0", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "rimraf": "^2.2.8" } }, "globby": { @@ -10221,12 +10229,12 @@ "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", "dev": true, "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "minimist": { @@ -10253,7 +10261,7 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "0.5.6" + "source-map": "^0.5.6" } }, "supports-color": { @@ -10268,17 +10276,17 @@ "integrity": "sha512-oEc5fmkpz6Yh6udhwir5m0eN5mgRPq9P/NU5YWuT3Up5slt6Zz+znhLU7q4+8rwCZz/Qq3Fgpr/4oao7NPCm2A==", "dev": true, "requires": { - "adm-zip": "0.4.11", - "chalk": "1.1.3", - "del": "2.2.2", - "glob": "7.1.2", - "ini": "1.3.5", - "minimist": "1.2.0", - "q": "1.4.1", - "request": "2.87.0", - "rimraf": "2.6.2", - "semver": "5.5.0", - "xml2js": "0.4.19" + "adm-zip": "^0.4.9", + "chalk": "^1.1.1", + "del": "^2.2.0", + "glob": "^7.0.3", + "ini": "^1.3.4", + "minimist": "^1.2.0", + "q": "^1.4.1", + "request": "^2.87.0", + "rimraf": "^2.5.2", + "semver": "^5.3.0", + "xml2js": "^0.4.17" } } } @@ -10289,7 +10297,7 @@ "integrity": "sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA==", "dev": true, "requires": { - "forwarded": "0.1.2", + "forwarded": "~0.1.2", "ipaddr.js": "1.8.0" } }, @@ -10300,14 +10308,14 @@ "dev": true, "optional": true, "requires": { - "agent-base": "4.2.0", - "debug": "3.1.0", - "http-proxy-agent": "2.1.0", - "https-proxy-agent": "2.2.1", - "lru-cache": "4.1.3", - "pac-proxy-agent": "2.0.2", - "proxy-from-env": "1.0.0", - "socks-proxy-agent": "3.0.1" + "agent-base": "^4.2.0", + "debug": "^3.1.0", + "http-proxy-agent": "^2.1.0", + "https-proxy-agent": "^2.2.1", + "lru-cache": "^4.1.2", + "pac-proxy-agent": "^2.0.1", + "proxy-from-env": "^1.0.0", + "socks-proxy-agent": "^3.0.0" }, "dependencies": { "debug": { @@ -10347,11 +10355,11 @@ "integrity": "sha512-4kJ5Esocg8X3h8YgJsKAuoesBgB7mqH3eowiDzMUPKiRDDE7E/BqqZD1hnTByIaAFiwAw246YEltSq7tdrOH0Q==", "dev": true, "requires": { - "bn.js": "4.11.8", - "browserify-rsa": "4.0.1", - "create-hash": "1.2.0", - "parse-asn1": "5.1.1", - "randombytes": "2.0.6" + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1" } }, "pump": { @@ -10360,8 +10368,8 @@ "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "once": "1.4.0" + "end-of-stream": "^1.1.0", + "once": "^1.3.1" } }, "pumpify": { @@ -10370,9 +10378,9 @@ "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", "dev": true, "requires": { - "duplexify": "3.6.0", - "inherits": "2.0.3", - "pump": "2.0.1" + "duplexify": "^3.6.0", + "inherits": "^2.0.3", + "pump": "^2.0.0" } }, "punycode": { @@ -10423,9 +10431,9 @@ "integrity": "sha512-VdxFOIEY3mNO5PtSRkkle/hPJDHvQhK21oa73K4yAc9qmp6N429gAyF1gZMOTMeS0/AYzaV/2Trcef+NaIonSA==", "dev": true, "requires": { - "is-number": "4.0.0", - "kind-of": "6.0.2", - "math-random": "1.0.1" + "is-number": "^4.0.0", + "kind-of": "^6.0.0", + "math-random": "^1.0.1" }, "dependencies": { "is-number": { @@ -10442,7 +10450,7 @@ "integrity": "sha512-CIQ5OFxf4Jou6uOKe9t1AOgqpeU5fd70A8NPdHSGeYXqXsPe6peOwI0cUl88RWZ6sP1vPMV3avd/R6cZ5/sP1A==", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "^5.1.0" } }, "randomfill": { @@ -10451,8 +10459,8 @@ "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", "dev": true, "requires": { - "randombytes": "2.0.6", - "safe-buffer": "5.1.2" + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" } }, "range-parser": { @@ -10488,7 +10496,7 @@ "depd": "1.1.1", "inherits": "2.0.3", "setprototypeof": "1.0.3", - "statuses": "1.4.0" + "statuses": ">= 1.3.1 < 2" } }, "setprototypeof": { @@ -10511,10 +10519,10 @@ "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "requires": { - "deep-extend": "0.6.0", - "ini": "1.3.5", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" }, "dependencies": { "minimist": { @@ -10537,7 +10545,7 @@ "integrity": "sha1-5mTvMRYRZsl1HNvo28+GtftY93Q=", "dev": true, "requires": { - "pify": "2.3.0" + "pify": "^2.3.0" }, "dependencies": { "pify": { @@ -10554,9 +10562,9 @@ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", "dev": true, "requires": { - "load-json-file": "1.1.0", - "normalize-package-data": "2.4.0", - "path-type": "1.1.0" + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" }, "dependencies": { "path-type": { @@ -10565,9 +10573,9 @@ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "pify": { @@ -10584,8 +10592,8 @@ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", "dev": true, "requires": { - "find-up": "1.1.2", - "read-pkg": "1.1.0" + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" }, "dependencies": { "find-up": { @@ -10594,8 +10602,8 @@ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", "dev": true, "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" } }, "path-exists": { @@ -10604,7 +10612,7 @@ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", "dev": true, "requires": { - "pinkie-promise": "2.0.1" + "pinkie-promise": "^2.0.0" } } } @@ -10615,13 +10623,13 @@ "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "2.0.0", - "safe-buffer": "5.1.2", - "string_decoder": "1.1.1", - "util-deprecate": "1.0.2" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, "readdirp": { @@ -10630,10 +10638,10 @@ "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", "dev": true, "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.6", - "set-immediate-shim": "1.0.1" + "graceful-fs": "^4.1.2", + "minimatch": "^3.0.2", + "readable-stream": "^2.0.2", + "set-immediate-shim": "^1.0.1" } }, "readline2": { @@ -10642,8 +10650,8 @@ "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", "mute-stream": "0.0.5" } }, @@ -10653,7 +10661,7 @@ "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", "dev": true, "requires": { - "resolve": "1.7.1" + "resolve": "^1.1.6" } }, "redent": { @@ -10662,8 +10670,8 @@ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", "dev": true, "requires": { - "indent-string": "2.1.0", - "strip-indent": "1.0.1" + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" } }, "redis": { @@ -10673,9 +10681,9 @@ "dev": true, "optional": true, "requires": { - "double-ended-queue": "2.1.0-0", - "redis-commands": "1.3.5", - "redis-parser": "2.6.0" + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" } }, "redis-commands": { @@ -10716,7 +10724,7 @@ "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", "dev": true, "requires": { - "is-equal-shallow": "0.1.3" + "is-equal-shallow": "^0.1.3" } }, "regex-not": { @@ -10725,8 +10733,8 @@ "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", "dev": true, "requires": { - "extend-shallow": "3.0.2", - "safe-regex": "1.1.0" + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" } }, "regexpu-core": { @@ -10735,9 +10743,9 @@ "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", "dev": true, "requires": { - "regenerate": "1.4.0", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" } }, "regjsgen": { @@ -10752,7 +10760,7 @@ "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", "dev": true, "requires": { - "jsesc": "0.5.0" + "jsesc": "~0.5.0" }, "dependencies": { "jsesc": { @@ -10775,8 +10783,8 @@ "integrity": "sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ==", "dev": true, "requires": { - "is-buffer": "1.1.6", - "is-utf8": "0.2.1" + "is-buffer": "^1.1.5", + "is-utf8": "^0.2.1" } }, "remove-bom-stream": { @@ -10785,9 +10793,9 @@ "integrity": "sha1-BfGlk/FuQuH7kOv1nejlaVJflSM=", "dev": true, "requires": { - "remove-bom-buffer": "3.0.0", - "safe-buffer": "5.1.2", - "through2": "2.0.3" + "remove-bom-buffer": "^3.0.0", + "safe-buffer": "^5.1.0", + "through2": "^2.0.3" } }, "remove-trailing-separator": { @@ -10802,11 +10810,11 @@ "integrity": "sha1-iYyr/Ivt5Le5ETWj/9Mj5YwNsxk=", "dev": true, "requires": { - "css-select": "1.2.0", - "dom-converter": "0.1.4", - "htmlparser2": "3.3.0", - "strip-ansi": "3.0.1", - "utila": "0.3.3" + "css-select": "^1.1.0", + "dom-converter": "~0.1", + "htmlparser2": "~3.3.0", + "strip-ansi": "^3.0.0", + "utila": "~0.3" }, "dependencies": { "utila": { @@ -10835,7 +10843,7 @@ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", "dev": true, "requires": { - "is-finite": "1.0.2" + "is-finite": "^1.0.0" } }, "replace-ext": { @@ -10850,9 +10858,9 @@ "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw=", "dev": true, "requires": { - "homedir-polyfill": "1.0.1", - "is-absolute": "1.0.0", - "remove-trailing-separator": "1.1.0" + "homedir-polyfill": "^1.0.1", + "is-absolute": "^1.0.0", + "remove-trailing-separator": "^1.1.0" } }, "replace-in-file": { @@ -10861,9 +10869,9 @@ "integrity": "sha512-fto9Ooab00CniGkSjRCZCamER7P5S4mZHQ4w4dLd09nwP3FtFfjUJh8/OVC/In4ki5MEy+dYO5v9r7rtq2DrYQ==", "dev": true, "requires": { - "chalk": "2.4.1", - "glob": "7.1.2", - "yargs": "11.1.0" + "chalk": "^2.3.2", + "glob": "^7.1.2", + "yargs": "^11.0.0" }, "dependencies": { "ansi-regex": { @@ -10884,9 +10892,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } }, "cliui": { @@ -10895,9 +10903,9 @@ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" } }, "is-fullwidth-code-point": { @@ -10912,9 +10920,9 @@ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" } }, "string-width": { @@ -10923,8 +10931,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" } }, "strip-ansi": { @@ -10933,7 +10941,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } }, "which-module": { @@ -10954,18 +10962,18 @@ "integrity": "sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==", "dev": true, "requires": { - "cliui": "4.1.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" } }, "yargs-parser": { @@ -10974,7 +10982,7 @@ "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" } } } @@ -10985,26 +10993,26 @@ "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", "dev": true, "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.7.0", - "caseless": "0.12.0", - "combined-stream": "1.0.6", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.2", - "har-validator": "5.0.3", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.18", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.1.2", - "tough-cookie": "2.3.4", - "tunnel-agent": "0.6.0", - "uuid": "3.2.1" + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" } }, "request-promise-core": { @@ -11013,7 +11021,7 @@ "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", "dev": true, "requires": { - "lodash": "4.17.10" + "lodash": "^4.13.1" } }, "request-promise-native": { @@ -11023,8 +11031,8 @@ "dev": true, "requires": { "request-promise-core": "1.1.1", - "stealthy-require": "1.1.1", - "tough-cookie": "2.3.4" + "stealthy-require": "^1.1.0", + "tough-cookie": ">=2.3.3" } }, "requestretry": { @@ -11034,10 +11042,10 @@ "dev": true, "optional": true, "requires": { - "extend": "3.0.1", - "lodash": "4.17.10", - "request": "2.87.0", - "when": "3.7.8" + "extend": "^3.0.0", + "lodash": "^4.15.0", + "request": "^2.74.0", + "when": "^3.7.7" }, "dependencies": { "when": { @@ -11073,8 +11081,8 @@ "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", "dev": true, "requires": { - "caller-path": "0.1.0", - "resolve-from": "1.0.1" + "caller-path": "^0.1.0", + "resolve-from": "^1.0.0" }, "dependencies": { "resolve-from": { @@ -11102,7 +11110,7 @@ "integrity": "sha512-c7rwLofp8g1U+h1KNyHL/jicrKg1Ek4q+Lr33AL65uZTinUZHe30D5HlyN5V9NW0JX1D5dXQ4jqW5l7Sy/kGfw==", "dev": true, "requires": { - "path-parse": "1.0.5" + "path-parse": "^1.0.5" } }, "resolve-cwd": { @@ -11111,7 +11119,7 @@ "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", "dev": true, "requires": { - "resolve-from": "3.0.0" + "resolve-from": "^3.0.0" } }, "resolve-dir": { @@ -11120,8 +11128,8 @@ "integrity": "sha1-shklmlYC+sXFxJatiUpujMQwJh4=", "dev": true, "requires": { - "expand-tilde": "1.2.2", - "global-modules": "0.2.3" + "expand-tilde": "^1.2.2", + "global-modules": "^0.2.3" } }, "resolve-from": { @@ -11136,7 +11144,7 @@ "integrity": "sha1-MrueOcBtZzONyTeMDW1gdFZq0TE=", "dev": true, "requires": { - "value-or-function": "3.0.0" + "value-or-function": "^3.0.0" } }, "resolve-url": { @@ -11151,8 +11159,8 @@ "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", "dev": true, "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" } }, "ret": { @@ -11174,7 +11182,7 @@ "dev": true, "optional": true, "requires": { - "align-text": "0.1.4" + "align-text": "^0.1.1" } }, "rimraf": { @@ -11183,7 +11191,7 @@ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", "dev": true, "requires": { - "glob": "7.1.2" + "glob": "^7.0.5" } }, "ripemd160": { @@ -11192,8 +11200,8 @@ "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", "dev": true, "requires": { - "hash-base": "3.0.4", - "inherits": "2.0.3" + "hash-base": "^3.0.0", + "inherits": "^2.0.1" } }, "run-async": { @@ -11202,7 +11210,7 @@ "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", "dev": true, "requires": { - "once": "1.4.0" + "once": "^1.3.0" } }, "run-queue": { @@ -11211,7 +11219,7 @@ "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "dev": true, "requires": { - "aproba": "1.2.0" + "aproba": "^1.1.1" } }, "rx-lite": { @@ -11225,7 +11233,7 @@ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.2.0.tgz", "integrity": "sha512-qBzf5uu6eOKiCZuAE0SgZ0/Qp+l54oeVxFfC2t+mJ2SFI6IB8gmMdJHs5DUMu5kqifqcCtsKS2XHjhZu6RKvAw==", "requires": { - "tslib": "1.9.2" + "tslib": "^1.9.0" } }, "rxjs-spy": { @@ -11233,11 +11241,11 @@ "resolved": "https://registry.npmjs.org/rxjs-spy/-/rxjs-spy-7.0.2.tgz", "integrity": "sha512-rSzEEQLH07FPBqmxIC8oeI2Z7tWibvxKxybcKtcc6Cj8Tz178ySDgzgOkoct8dAz5IE/H2jrR2/l+ZYYfpuEXw==", "requires": { - "@types/circular-json": "0.4.0", - "@types/stacktrace-js": "0.0.32", - "circular-json": "0.5.4", - "error-stack-parser": "2.0.1", - "stacktrace-gps": "3.0.2" + "@types/circular-json": "^0.4.0", + "@types/stacktrace-js": "^0.0.32", + "circular-json": "^0.5.0", + "error-stack-parser": "^2.0.1", + "stacktrace-gps": "^3.0.2" } }, "rxjs-tslint": { @@ -11246,10 +11254,10 @@ "integrity": "sha512-odvEAx6VoZSJs5o9gWZ7SxgTuS8ldq0mr4qnprbr6flNtIt4DtNJSYJpBK0WEy0o+f1oe92XVHrmgco1e7Cpdw==", "dev": true, "requires": { - "chalk": "2.4.1", - "optimist": "0.6.1", - "tslint": "5.10.0", - "tsutils": "2.27.1" + "chalk": "^2.4.0", + "optimist": "^0.6.1", + "tslint": "^5.9.1", + "tsutils": "^2.25.0" }, "dependencies": { "chalk": { @@ -11258,9 +11266,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } } } @@ -11282,7 +11290,7 @@ "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", "dev": true, "requires": { - "ret": "0.1.15" + "ret": "~0.1.10" } }, "sass-graph": { @@ -11292,10 +11300,10 @@ "dev": true, "optional": true, "requires": { - "glob": "7.1.2", - "lodash": "4.17.10", - "scss-tokenizer": "0.2.3", - "yargs": "7.1.0" + "glob": "^7.0.0", + "lodash": "^4.0.0", + "scss-tokenizer": "^0.2.3", + "yargs": "^7.0.0" }, "dependencies": { "camelcase": { @@ -11312,9 +11320,9 @@ "dev": true, "optional": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wrap-ansi": "2.1.0" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wrap-ansi": "^2.0.0" } }, "y18n": { @@ -11331,19 +11339,19 @@ "dev": true, "optional": true, "requires": { - "camelcase": "3.0.0", - "cliui": "3.2.0", - "decamelize": "1.2.0", - "get-caller-file": "1.0.2", - "os-locale": "1.4.0", - "read-pkg-up": "1.0.1", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "1.0.2", - "which-module": "1.0.0", - "y18n": "3.2.1", - "yargs-parser": "5.0.0" + "camelcase": "^3.0.0", + "cliui": "^3.2.0", + "decamelize": "^1.1.1", + "get-caller-file": "^1.0.1", + "os-locale": "^1.4.0", + "read-pkg-up": "^1.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^1.0.2", + "which-module": "^1.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^5.0.0" } } } @@ -11354,20 +11362,20 @@ "integrity": "sha1-Yw9pwhaqIGuCMvsqqQe98zNrbYM=", "dev": true, "requires": { - "commander": "2.15.1", - "eslint": "2.13.1", + "commander": "^2.8.1", + "eslint": "^2.7.0", "front-matter": "2.1.2", - "fs-extra": "3.0.1", - "glob": "7.1.2", - "globule": "1.2.1", - "gonzales-pe-sl": "4.2.3", - "js-yaml": "3.11.0", - "known-css-properties": "0.3.0", - "lodash.capitalize": "4.2.1", - "lodash.kebabcase": "4.1.1", - "merge": "1.2.0", - "path-is-absolute": "1.0.1", - "util": "0.10.3" + "fs-extra": "^3.0.1", + "glob": "^7.0.0", + "globule": "^1.0.0", + "gonzales-pe-sl": "^4.2.3", + "js-yaml": "^3.5.4", + "known-css-properties": "^0.3.0", + "lodash.capitalize": "^4.1.0", + "lodash.kebabcase": "^4.0.0", + "merge": "^1.2.0", + "path-is-absolute": "^1.0.0", + "util": "^0.10.3" } }, "sass-loader": { @@ -11376,11 +11384,11 @@ "integrity": "sha512-JoiyD00Yo1o61OJsoP2s2kb19L1/Y2p3QFcCdWdF6oomBGKVYuZyqHWemRBfQ2uGYsk+CH3eCguXNfpjzlcpaA==", "dev": true, "requires": { - "clone-deep": "2.0.2", - "loader-utils": "1.1.0", - "lodash.tail": "4.1.1", - "neo-async": "2.5.2", - "pify": "3.0.0" + "clone-deep": "^2.0.1", + "loader-utils": "^1.0.1", + "lodash.tail": "^4.1.1", + "neo-async": "^2.5.0", + "pify": "^3.0.0" } }, "saucelabs": { @@ -11389,7 +11397,7 @@ "integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==", "dev": true, "requires": { - "https-proxy-agent": "2.2.1" + "https-proxy-agent": "^2.2.1" } }, "sax": { @@ -11404,8 +11412,8 @@ "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", "dev": true, "requires": { - "ajv": "6.4.0", - "ajv-keywords": "3.2.0" + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0" } }, "scss-tokenizer": { @@ -11415,8 +11423,8 @@ "dev": true, "optional": true, "requires": { - "js-base64": "2.4.8", - "source-map": "0.4.4" + "js-base64": "^2.1.8", + "source-map": "^0.4.2" }, "dependencies": { "source-map": { @@ -11426,7 +11434,7 @@ "dev": true, "optional": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -11443,10 +11451,10 @@ "integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==", "dev": true, "requires": { - "jszip": "3.1.5", - "rimraf": "2.6.2", + "jszip": "^3.1.3", + "rimraf": "^2.5.4", "tmp": "0.0.30", - "xml2js": "0.4.19" + "xml2js": "^0.4.17" }, "dependencies": { "tmp": { @@ -11455,7 +11463,7 @@ "integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.1" } } } @@ -11481,7 +11489,7 @@ "integrity": "sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA=", "dev": true, "requires": { - "semver": "5.5.0" + "semver": "^5.3.0" } }, "semver-greatest-satisfied-range": { @@ -11490,7 +11498,7 @@ "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els=", "dev": true, "requires": { - "sver-compat": "1.5.0" + "sver-compat": "^1.5.0" } }, "semver-intersect": { @@ -11499,7 +11507,7 @@ "integrity": "sha1-j6hKnhAovSOeRTDRo+GB5pjYhLo=", "dev": true, "requires": { - "semver": "5.5.0" + "semver": "^5.0.0" } }, "send": { @@ -11509,18 +11517,18 @@ "dev": true, "requires": { "debug": "2.6.9", - "depd": "1.1.2", - "destroy": "1.0.4", - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "etag": "1.8.1", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", "fresh": "0.5.2", - "http-errors": "1.6.3", + "http-errors": "~1.6.2", "mime": "1.4.1", "ms": "2.0.0", - "on-finished": "2.3.0", - "range-parser": "1.2.0", - "statuses": "1.4.0" + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.0" }, "dependencies": { "mime": { @@ -11543,13 +11551,13 @@ "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "dev": true, "requires": { - "accepts": "1.3.5", + "accepts": "~1.3.4", "batch": "0.6.1", "debug": "2.6.9", - "escape-html": "1.0.3", - "http-errors": "1.6.3", - "mime-types": "2.1.18", - "parseurl": "1.3.2" + "escape-html": "~1.0.3", + "http-errors": "~1.6.2", + "mime-types": "~2.1.17", + "parseurl": "~1.3.2" } }, "serve-static": { @@ -11558,9 +11566,9 @@ "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", "dev": true, "requires": { - "encodeurl": "1.0.2", - "escape-html": "1.0.3", - "parseurl": "1.3.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", "send": "0.16.2" } }, @@ -11576,7 +11584,7 @@ "integrity": "sha1-12nBgsnVpR9AkUXy+6guXoboA3Y=", "dev": true, "requires": { - "to-object-path": "0.3.0" + "to-object-path": "^0.3.0" } }, "set-immediate-shim": { @@ -11591,10 +11599,10 @@ "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "split-string": "3.1.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" }, "dependencies": { "extend-shallow": { @@ -11603,7 +11611,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -11626,8 +11634,8 @@ "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", "dev": true, "requires": { - "inherits": "2.0.3", - "safe-buffer": "5.1.2" + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" } }, "shallow-clone": { @@ -11636,9 +11644,9 @@ "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==", "dev": true, "requires": { - "is-extendable": "0.1.1", - "kind-of": "5.1.0", - "mixin-object": "2.0.1" + "is-extendable": "^0.1.1", + "kind-of": "^5.0.0", + "mixin-object": "^2.0.1" }, "dependencies": { "kind-of": { @@ -11655,7 +11663,7 @@ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", "dev": true, "requires": { - "shebang-regex": "1.0.0" + "shebang-regex": "^1.0.0" } }, "shebang-regex": { @@ -11683,7 +11691,7 @@ "dev": true, "optional": true, "requires": { - "requestretry": "1.13.0" + "requestretry": "^1.2.2" } }, "slash": { @@ -11726,14 +11734,14 @@ "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", "dev": true, "requires": { - "base": "0.11.2", - "debug": "2.6.9", - "define-property": "0.2.5", - "extend-shallow": "2.0.1", - "map-cache": "0.2.2", - "source-map": "0.5.6", - "source-map-resolve": "0.5.2", - "use": "3.1.0" + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" }, "dependencies": { "define-property": { @@ -11742,7 +11750,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } }, "extend-shallow": { @@ -11751,7 +11759,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } } } @@ -11762,9 +11770,9 @@ "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", "dev": true, "requires": { - "define-property": "1.0.0", - "isobject": "3.0.1", - "snapdragon-util": "3.0.1" + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" }, "dependencies": { "define-property": { @@ -11773,7 +11781,7 @@ "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "dev": true, "requires": { - "is-descriptor": "1.0.2" + "is-descriptor": "^1.0.0" } }, "is-accessor-descriptor": { @@ -11782,7 +11790,7 @@ "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-data-descriptor": { @@ -11791,7 +11799,7 @@ "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.0" } }, "is-descriptor": { @@ -11800,9 +11808,9 @@ "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", "dev": true, "requires": { - "is-accessor-descriptor": "1.0.0", - "is-data-descriptor": "1.0.0", - "kind-of": "6.0.2" + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" } } } @@ -11813,7 +11821,7 @@ "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.2.0" }, "dependencies": { "kind-of": { @@ -11822,7 +11830,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -11834,7 +11842,7 @@ "dev": true, "optional": true, "requires": { - "hoek": "2.16.3" + "hoek": "2.x.x" } }, "socket.io": { @@ -11843,11 +11851,11 @@ "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=", "dev": true, "requires": { - "debug": "2.6.9", - "engine.io": "3.1.5", - "socket.io-adapter": "1.1.1", + "debug": "~2.6.6", + "engine.io": "~3.1.0", + "socket.io-adapter": "~1.1.0", "socket.io-client": "2.0.4", - "socket.io-parser": "3.1.3" + "socket.io-parser": "~3.1.1" } }, "socket.io-adapter": { @@ -11866,14 +11874,14 @@ "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "2.6.9", - "engine.io-client": "3.1.6", + "debug": "~2.6.4", + "engine.io-client": "~3.1.0", "has-cors": "1.1.0", "indexof": "0.0.1", "object-component": "0.0.3", "parseqs": "0.0.5", "parseuri": "0.0.5", - "socket.io-parser": "3.1.3", + "socket.io-parser": "~3.1.1", "to-array": "0.1.4" } }, @@ -11884,8 +11892,8 @@ "dev": true, "requires": { "component-emitter": "1.2.1", - "debug": "3.1.0", - "has-binary2": "1.0.3", + "debug": "~3.1.0", + "has-binary2": "~1.0.2", "isarray": "2.0.1" }, "dependencies": { @@ -11912,8 +11920,8 @@ "integrity": "sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw==", "dev": true, "requires": { - "faye-websocket": "0.10.0", - "uuid": "3.2.1" + "faye-websocket": "^0.10.0", + "uuid": "^3.0.1" } }, "sockjs-client": { @@ -11922,12 +11930,12 @@ "integrity": "sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM=", "dev": true, "requires": { - "debug": "2.6.9", + "debug": "^2.6.6", "eventsource": "0.1.6", - "faye-websocket": "0.11.1", - "inherits": "2.0.3", - "json3": "3.3.2", - "url-parse": "1.4.3" + "faye-websocket": "~0.11.0", + "inherits": "^2.0.1", + "json3": "^3.3.2", + "url-parse": "^1.1.8" }, "dependencies": { "faye-websocket": { @@ -11936,7 +11944,7 @@ "integrity": "sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg=", "dev": true, "requires": { - "websocket-driver": "0.7.0" + "websocket-driver": ">=0.5.1" } } } @@ -11947,8 +11955,8 @@ "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", "dev": true, "requires": { - "ip": "1.1.5", - "smart-buffer": "1.1.15" + "ip": "^1.1.4", + "smart-buffer": "^1.0.13" } }, "socks-proxy-agent": { @@ -11957,8 +11965,8 @@ "integrity": "sha512-ZwEDymm204mTzvdqyUqOdovVr2YRd2NYskrYrF2LXyZ9qDiMAoFESGK8CRphiO7rtbo2Y757k2Nia3x2hGtalA==", "dev": true, "requires": { - "agent-base": "4.2.0", - "socks": "1.1.10" + "agent-base": "^4.1.0", + "socks": "^1.1.10" } }, "source-list-map": { @@ -11978,8 +11986,8 @@ "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==", "dev": true, "requires": { - "async": "2.6.1", - "loader-utils": "1.1.0" + "async": "^2.5.0", + "loader-utils": "^1.1.0" }, "dependencies": { "async": { @@ -11988,7 +11996,7 @@ "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", "dev": true, "requires": { - "lodash": "4.17.10" + "lodash": "^4.17.10" } } } @@ -11999,11 +12007,11 @@ "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", "dev": true, "requires": { - "atob": "2.1.1", - "decode-uri-component": "0.2.0", - "resolve-url": "0.2.1", - "source-map-url": "0.4.0", - "urix": "0.1.0" + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" } }, "source-map-support": { @@ -12012,8 +12020,8 @@ "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", "dev": true, "requires": { - "buffer-from": "1.1.0", - "source-map": "0.6.1" + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" }, "dependencies": { "source-map": { @@ -12042,8 +12050,8 @@ "integrity": "sha512-N19o9z5cEyc8yQQPukRCZ9EUmb4HUpnrmaL/fxS2pBo2jbfcFRVuFZ/oFC+vZz0MNNk0h80iMn5/S6qGZOL5+g==", "dev": true, "requires": { - "spdx-expression-parse": "3.0.0", - "spdx-license-ids": "3.0.0" + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" } }, "spdx-exceptions": { @@ -12058,8 +12066,8 @@ "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", "dev": true, "requires": { - "spdx-exceptions": "2.1.0", - "spdx-license-ids": "3.0.0" + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, "spdx-license-ids": { @@ -12074,12 +12082,12 @@ "integrity": "sha1-Qv9B7OXMD5mjpsKKq7c/XDsDrLw=", "dev": true, "requires": { - "debug": "2.6.9", - "handle-thing": "1.2.5", - "http-deceiver": "1.2.7", - "safe-buffer": "5.1.2", - "select-hose": "2.0.0", - "spdy-transport": "2.1.0" + "debug": "^2.6.8", + "handle-thing": "^1.2.5", + "http-deceiver": "^1.2.7", + "safe-buffer": "^5.0.1", + "select-hose": "^2.0.0", + "spdy-transport": "^2.0.18" } }, "spdy-transport": { @@ -12088,13 +12096,13 @@ "integrity": "sha512-bpUeGpZcmZ692rrTiqf9/2EUakI6/kXX1Rpe0ib/DyOzbiexVfXkw6GnvI9hVGvIwVaUhkaBojjCZwLNRGQg1g==", "dev": true, "requires": { - "debug": "2.6.9", - "detect-node": "2.0.3", - "hpack.js": "2.1.6", - "obuf": "1.1.2", - "readable-stream": "2.3.6", - "safe-buffer": "5.1.2", - "wbuf": "1.7.3" + "debug": "^2.6.8", + "detect-node": "^2.0.3", + "hpack.js": "^2.1.6", + "obuf": "^1.1.1", + "readable-stream": "^2.2.9", + "safe-buffer": "^5.0.1", + "wbuf": "^1.7.2" } }, "split-string": { @@ -12103,7 +12111,7 @@ "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", "dev": true, "requires": { - "extend-shallow": "3.0.2" + "extend-shallow": "^3.0.0" } }, "sprintf-js": { @@ -12117,14 +12125,14 @@ "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", "dev": true, "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "tweetnacl": "~0.14.0" } }, "ssri": { @@ -12133,7 +12141,7 @@ "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "^5.1.1" } }, "stack-trace": { @@ -12153,7 +12161,7 @@ "integrity": "sha512-9o+nWhiz5wFnrB3hBHs2PTyYrS60M1vvpSzHxwxnIbtY2q9Nt51hZvhrG1+2AxD374ecwyS+IUwfkHRE/2zuGg==", "requires": { "source-map": "0.5.6", - "stackframe": "1.0.4" + "stackframe": "^1.0.4" } }, "static-extend": { @@ -12162,8 +12170,8 @@ "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", "dev": true, "requires": { - "define-property": "0.2.5", - "object-copy": "0.1.0" + "define-property": "^0.2.5", + "object-copy": "^0.1.0" }, "dependencies": { "define-property": { @@ -12172,7 +12180,7 @@ "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "dev": true, "requires": { - "is-descriptor": "0.1.6" + "is-descriptor": "^0.1.0" } } } @@ -12183,7 +12191,7 @@ "integrity": "sha1-LFlJtTHgf4eojm6k3PrFOqjHWis=", "dev": true, "requires": { - "lodash": "4.17.10" + "lodash": "^4.17.4" } }, "statuses": { @@ -12199,7 +12207,7 @@ "dev": true, "optional": true, "requires": { - "readable-stream": "2.3.6" + "readable-stream": "^2.0.1" } }, "stealthy-require": { @@ -12214,10 +12222,10 @@ "integrity": "sha512-4StuTlfzL6LXdx+/r0zIm+ayxyPKP0+8338OBn448WursU5njivXu29Av2WXcqmjzcHH48hmmmfHFeBTPsuJCQ==", "dev": true, "requires": { - "inquirer": "0.11.4", - "minimist": "1.2.0", - "node-fs": "0.1.7", - "path": "0.12.7" + "inquirer": "^0.11.0", + "minimist": "^1.2.0", + "node-fs": "~0.1.7", + "path": "^0.12.7" }, "dependencies": { "ansi-styles": { @@ -12232,11 +12240,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "cli-width": { @@ -12251,19 +12259,19 @@ "integrity": "sha1-geM3ToNhvq/y2XAWIG01nQsy+k0=", "dev": true, "requires": { - "ansi-escapes": "1.4.0", - "ansi-regex": "2.1.1", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-width": "1.1.1", - "figures": "1.7.0", - "lodash": "3.10.1", - "readline2": "1.0.1", - "run-async": "0.1.0", - "rx-lite": "3.1.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "through": "2.3.8" + "ansi-escapes": "^1.1.0", + "ansi-regex": "^2.0.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^1.0.1", + "figures": "^1.3.5", + "lodash": "^3.3.1", + "readline2": "^1.0.1", + "run-async": "^0.1.0", + "rx-lite": "^3.1.2", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" } }, "lodash": { @@ -12292,11 +12300,11 @@ "integrity": "sha512-e87eX4LB+KLVOfEF/TeWpNYM9aQFJTO+jsslym7zCZLYd8xlNWZUjmUf00J0DWn7YpoNGdjv0wzkKPcXJ2c5uw==", "dev": true, "requires": { - "circular-json": "0.3.3", - "fs-extra": "3.0.1", - "klaw-sync": "2.1.0", - "mkdirp": "0.3.5", - "underscore": "1.6.0" + "circular-json": "^0.3.1", + "fs-extra": "^3.0.1", + "klaw-sync": "^2.1.0", + "mkdirp": "~0.3.5", + "underscore": "~1.6.0" }, "dependencies": { "circular-json": { @@ -12325,8 +12333,8 @@ "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", "dev": true, "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.6" + "inherits": "~2.0.1", + "readable-stream": "^2.0.2" } }, "stream-each": { @@ -12335,8 +12343,8 @@ "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", "dev": true, "requires": { - "end-of-stream": "1.4.1", - "stream-shift": "1.0.0" + "end-of-stream": "^1.1.0", + "stream-shift": "^1.0.0" } }, "stream-exhaust": { @@ -12351,11 +12359,11 @@ "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", "dev": true, "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.6", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" + "builtin-status-codes": "^3.0.0", + "inherits": "^2.0.1", + "readable-stream": "^2.3.6", + "to-arraybuffer": "^1.0.0", + "xtend": "^4.0.0" } }, "stream-shift": { @@ -12370,10 +12378,10 @@ "integrity": "sha512-WREzfy0r0zUqp3lGO096wRuUp7ho1X6uo/7DJfTlEi0Iv/4gT7YHqXDjKC2ioVGBZtE8QzsQD9nx1nIuoZ57jQ==", "dev": true, "requires": { - "date-format": "1.2.0", - "debug": "3.1.0", - "mkdirp": "0.5.1", - "readable-stream": "2.3.6" + "date-format": "^1.2.0", + "debug": "^3.1.0", + "mkdirp": "^0.5.1", + "readable-stream": "^2.3.0" }, "dependencies": { "debug": { @@ -12393,9 +12401,9 @@ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" } }, "string_decoder": { @@ -12404,7 +12412,7 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "~5.1.0" } }, "stringstream": { @@ -12420,7 +12428,7 @@ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "requires": { - "ansi-regex": "2.1.1" + "ansi-regex": "^2.0.0" } }, "strip-bom": { @@ -12429,7 +12437,7 @@ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", "dev": true, "requires": { - "is-utf8": "0.2.1" + "is-utf8": "^0.2.0" } }, "strip-eof": { @@ -12444,7 +12452,7 @@ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", "dev": true, "requires": { - "get-stdin": "4.0.1" + "get-stdin": "^4.0.1" } }, "strip-json-comments": { @@ -12459,8 +12467,8 @@ "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "schema-utils": "0.4.7" + "loader-utils": "^1.1.0", + "schema-utils": "^0.4.5" } }, "stylus": { @@ -12469,12 +12477,12 @@ "integrity": "sha1-QrlWCTHKcJDOhRWnmLqeaqPW3Hk=", "dev": true, "requires": { - "css-parse": "1.7.0", - "debug": "2.6.9", - "glob": "7.0.6", - "mkdirp": "0.5.1", - "sax": "0.5.8", - "source-map": "0.1.43" + "css-parse": "1.7.x", + "debug": "*", + "glob": "7.0.x", + "mkdirp": "0.5.x", + "sax": "0.5.x", + "source-map": "0.1.x" }, "dependencies": { "glob": { @@ -12483,12 +12491,12 @@ "integrity": "sha1-IRuvr0nlJbjNkyYNFKsTYVKz9Xo=", "dev": true, "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } }, "source-map": { @@ -12497,7 +12505,7 @@ "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -12508,9 +12516,9 @@ "integrity": "sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "lodash.clonedeep": "4.5.0", - "when": "3.6.4" + "loader-utils": "^1.0.2", + "lodash.clonedeep": "^4.5.0", + "when": "~3.6.x" } }, "supports-color": { @@ -12519,7 +12527,7 @@ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { - "has-flag": "3.0.0" + "has-flag": "^3.0.0" } }, "sver-compat": { @@ -12528,8 +12536,8 @@ "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg=", "dev": true, "requires": { - "es6-iterator": "2.0.3", - "es6-symbol": "3.1.1" + "es6-iterator": "^2.0.1", + "es6-symbol": "^3.1.1" } }, "symbol-observable": { @@ -12544,12 +12552,12 @@ "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", "dev": true, "requires": { - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "chalk": "1.1.3", - "lodash": "4.17.10", + "ajv": "^4.7.0", + "ajv-keywords": "^1.0.0", + "chalk": "^1.1.1", + "lodash": "^4.0.0", "slice-ansi": "0.0.4", - "string-width": "2.1.1" + "string-width": "^2.0.0" }, "dependencies": { "ajv": { @@ -12558,8 +12566,8 @@ "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", "dev": true, "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" + "co": "^4.6.0", + "json-stable-stringify": "^1.0.1" } }, "ajv-keywords": { @@ -12586,11 +12594,11 @@ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "dev": true, "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "is-fullwidth-code-point": { @@ -12605,8 +12613,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "dependencies": { "strip-ansi": { @@ -12615,7 +12623,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -12641,9 +12649,9 @@ "dev": true, "optional": true, "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" + "block-stream": "*", + "fstream": "^1.0.2", + "inherits": "2" } }, "text-table": { @@ -12664,8 +12672,8 @@ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", "dev": true, "requires": { - "readable-stream": "2.3.6", - "xtend": "4.0.1" + "readable-stream": "^2.1.5", + "xtend": "~4.0.1" } }, "through2-filter": { @@ -12674,8 +12682,8 @@ "integrity": "sha1-YLxVoNrLdghdsfna6Zq0P4PWIuw=", "dev": true, "requires": { - "through2": "2.0.3", - "xtend": "4.0.1" + "through2": "~2.0.0", + "xtend": "~4.0.0" } }, "thunkify": { @@ -12703,7 +12711,7 @@ "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==", "dev": true, "requires": { - "setimmediate": "1.0.5" + "setimmediate": "^1.0.4" } }, "timespan": { @@ -12719,7 +12727,7 @@ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", "dev": true, "requires": { - "os-tmpdir": "1.0.2" + "os-tmpdir": "~1.0.2" } }, "to-absolute-glob": { @@ -12728,8 +12736,8 @@ "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs=", "dev": true, "requires": { - "is-absolute": "1.0.0", - "is-negated-glob": "1.0.0" + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" } }, "to-array": { @@ -12756,7 +12764,7 @@ "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", "dev": true, "requires": { - "kind-of": "3.2.2" + "kind-of": "^3.0.2" }, "dependencies": { "kind-of": { @@ -12765,7 +12773,7 @@ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "dev": true, "requires": { - "is-buffer": "1.1.6" + "is-buffer": "^1.1.5" } } } @@ -12776,10 +12784,10 @@ "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", "dev": true, "requires": { - "define-property": "2.0.2", - "extend-shallow": "3.0.2", - "regex-not": "1.0.2", - "safe-regex": "1.1.0" + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" } }, "to-regex-range": { @@ -12788,8 +12796,8 @@ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", "dev": true, "requires": { - "is-number": "3.0.0", - "repeat-string": "1.6.1" + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" } }, "to-through": { @@ -12798,7 +12806,7 @@ "integrity": "sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY=", "dev": true, "requires": { - "through2": "2.0.3" + "through2": "^2.0.3" } }, "toposort": { @@ -12813,7 +12821,7 @@ "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", "dev": true, "requires": { - "punycode": "1.4.1" + "punycode": "^1.4.1" }, "dependencies": { "punycode": { @@ -12849,7 +12857,7 @@ "dev": true, "optional": true, "requires": { - "glob": "6.0.4" + "glob": "^6.0.4" }, "dependencies": { "glob": { @@ -12859,11 +12867,11 @@ "dev": true, "optional": true, "requires": { - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" } } } @@ -12874,16 +12882,16 @@ "integrity": "sha1-u9KOOK9Kqj6WB2xGbhsiAZfBo84=", "dev": true, "requires": { - "arrify": "1.0.1", - "chalk": "2.2.2", - "diff": "3.5.0", - "make-error": "1.3.4", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18", - "tsconfig": "6.0.0", - "v8flags": "3.1.0", - "yn": "2.0.0" + "arrify": "^1.0.0", + "chalk": "^2.0.0", + "diff": "^3.1.0", + "make-error": "^1.1.1", + "minimist": "^1.2.0", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.0", + "tsconfig": "^6.0.0", + "v8flags": "^3.0.0", + "yn": "^2.0.0" }, "dependencies": { "minimist": { @@ -12898,7 +12906,7 @@ "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", "dev": true, "requires": { - "source-map": "0.5.6" + "source-map": "^0.5.6" } } } @@ -12909,8 +12917,8 @@ "integrity": "sha1-aw6DdgA9evGGT434+J3QBZ/80DI=", "dev": true, "requires": { - "strip-bom": "3.0.0", - "strip-json-comments": "2.0.1" + "strip-bom": "^3.0.0", + "strip-json-comments": "^2.0.0" }, "dependencies": { "strip-bom": { @@ -12938,18 +12946,18 @@ "integrity": "sha1-EeJrzLiK+gLdDZlWyuPUVAtfVMM=", "dev": true, "requires": { - "babel-code-frame": "6.26.0", - "builtin-modules": "1.1.1", - "chalk": "2.4.1", - "commander": "2.15.1", - "diff": "3.5.0", - "glob": "7.1.2", - "js-yaml": "3.11.0", - "minimatch": "3.0.4", - "resolve": "1.7.1", - "semver": "5.5.0", - "tslib": "1.9.2", - "tsutils": "2.27.1" + "babel-code-frame": "^6.22.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^3.2.0", + "glob": "^7.1.1", + "js-yaml": "^3.7.0", + "minimatch": "^3.0.4", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.8.0", + "tsutils": "^2.12.1" }, "dependencies": { "chalk": { @@ -12958,9 +12966,9 @@ "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", "dev": true, "requires": { - "ansi-styles": "3.2.1", - "escape-string-regexp": "1.0.5", - "supports-color": "5.4.0" + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" } } } @@ -12978,7 +12986,7 @@ "integrity": "sha512-AE/7uzp32MmaHvNNFES85hhUDHFdFZp6OAiZcd6y4ZKKIg6orJTm8keYWBhIhrJQH3a4LzNKat7ZPXZt5aTf6w==", "dev": true, "requires": { - "tslib": "1.9.2" + "tslib": "^1.8.1" } }, "tty-browserify": { @@ -12993,7 +13001,7 @@ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "dev": true, "requires": { - "safe-buffer": "5.1.2" + "safe-buffer": "^5.0.1" } }, "tweetnacl": { @@ -13009,7 +13017,7 @@ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", "dev": true, "requires": { - "prelude-ls": "1.1.2" + "prelude-ls": "~1.1.2" } }, "type-is": { @@ -13019,7 +13027,7 @@ "dev": true, "requires": { "media-typer": "0.3.0", - "mime-types": "2.1.18" + "mime-types": "~2.1.18" } }, "typedarray": { @@ -13040,8 +13048,8 @@ "integrity": "sha512-J0M2i1mQA+ze3EdN9SBi751DNdAXmeFLfJrd/MDIkRc3G3Gbb9OPVSx7GIQvVwfWxQARcYV2DTxIkMyDAk3o9Q==", "dev": true, "requires": { - "commander": "2.16.0", - "source-map": "0.6.1" + "commander": "~2.16.0", + "source-map": "~0.6.1" }, "dependencies": { "commander": { @@ -13071,14 +13079,14 @@ "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==", "dev": true, "requires": { - "cacache": "10.0.4", - "find-cache-dir": "1.0.0", - "schema-utils": "0.4.7", - "serialize-javascript": "1.5.0", - "source-map": "0.6.1", - "uglify-es": "3.3.9", - "webpack-sources": "1.1.0", - "worker-farm": "1.6.0" + "cacache": "^10.0.4", + "find-cache-dir": "^1.0.0", + "schema-utils": "^0.4.5", + "serialize-javascript": "^1.4.0", + "source-map": "^0.6.1", + "uglify-es": "^3.3.4", + "webpack-sources": "^1.1.0", + "worker-farm": "^1.5.2" }, "dependencies": { "commander": { @@ -13099,8 +13107,8 @@ "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==", "dev": true, "requires": { - "commander": "2.13.0", - "source-map": "0.6.1" + "commander": "~2.13.0", + "source-map": "~0.6.1" } } } @@ -13129,15 +13137,15 @@ "integrity": "sha1-M52kZGJS0ILcN45wgGcpl1DhG0k=", "dev": true, "requires": { - "arr-flatten": "1.1.0", - "arr-map": "2.0.2", - "bach": "1.2.0", - "collection-map": "1.0.0", - "es6-weak-map": "2.0.2", - "last-run": "1.1.1", - "object.defaults": "1.1.0", - "object.reduce": "1.0.1", - "undertaker-registry": "1.0.1" + "arr-flatten": "^1.0.1", + "arr-map": "^2.0.0", + "bach": "^1.0.0", + "collection-map": "^1.0.0", + "es6-weak-map": "^2.0.1", + "last-run": "^1.1.0", + "object.defaults": "^1.0.0", + "object.reduce": "^1.0.0", + "undertaker-registry": "^1.0.0" } }, "undertaker-registry": { @@ -13152,10 +13160,10 @@ "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", "dev": true, "requires": { - "arr-union": "3.1.0", - "get-value": "2.0.6", - "is-extendable": "0.1.1", - "set-value": "0.4.3" + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" }, "dependencies": { "extend-shallow": { @@ -13164,7 +13172,7 @@ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "dev": true, "requires": { - "is-extendable": "0.1.1" + "is-extendable": "^0.1.0" } }, "set-value": { @@ -13173,10 +13181,10 @@ "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", "dev": true, "requires": { - "extend-shallow": "2.0.1", - "is-extendable": "0.1.1", - "is-plain-object": "2.0.4", - "to-object-path": "0.3.0" + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" } } } @@ -13187,7 +13195,7 @@ "integrity": "sha1-0F8v5AMlYIcfMOk8vnNe6iAVFPM=", "dev": true, "requires": { - "unique-slug": "2.0.0" + "unique-slug": "^2.0.0" } }, "unique-slug": { @@ -13196,7 +13204,7 @@ "integrity": "sha1-22Z258fMBimHj/GWCXx4hVrp9Ks=", "dev": true, "requires": { - "imurmurhash": "0.1.4" + "imurmurhash": "^0.1.4" } }, "unique-stream": { @@ -13205,8 +13213,8 @@ "integrity": "sha1-WqADz76Uxf+GbE59ZouxxNuts2k=", "dev": true, "requires": { - "json-stable-stringify": "1.0.1", - "through2-filter": "2.0.0" + "json-stable-stringify": "^1.0.0", + "through2-filter": "^2.0.0" } }, "universalify": { @@ -13227,8 +13235,8 @@ "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", "dev": true, "requires": { - "has-value": "0.3.1", - "isobject": "3.0.1" + "has-value": "^0.3.1", + "isobject": "^3.0.0" }, "dependencies": { "has-value": { @@ -13237,9 +13245,9 @@ "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", "dev": true, "requires": { - "get-value": "2.0.6", - "has-values": "0.1.4", - "isobject": "2.1.0" + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" }, "dependencies": { "isobject": { @@ -13279,7 +13287,7 @@ "integrity": "sha1-+QuFhQf4HepNz7s8TD2/orVX+qo=", "dev": true, "requires": { - "punycode": "2.1.1" + "punycode": "^2.1.0" } }, "urix": { @@ -13318,9 +13326,9 @@ "integrity": "sha512-vugEeXjyYFBCUOpX+ZuaunbK3QXMKaQ3zUnRfIpRBlGkY7QizCnzyyn2ASfcxsvyU3ef+CJppVywnl3Kgf13Gg==", "dev": true, "requires": { - "loader-utils": "1.1.0", - "mime": "2.3.1", - "schema-utils": "1.0.0" + "loader-utils": "^1.1.0", + "mime": "^2.0.3", + "schema-utils": "^1.0.0" }, "dependencies": { "mime": { @@ -13335,9 +13343,9 @@ "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", "dev": true, "requires": { - "ajv": "6.4.0", - "ajv-errors": "1.0.0", - "ajv-keywords": "3.2.0" + "ajv": "^6.1.0", + "ajv-errors": "^1.0.0", + "ajv-keywords": "^3.1.0" } } } @@ -13348,8 +13356,8 @@ "integrity": "sha512-rh+KuAW36YKo0vClhQzLLveoj8FwPJNu65xLb7Mrt+eZht0IPT0IXgSv8gcMegZ6NvjJUALf6Mf25POlMwD1Fw==", "dev": true, "requires": { - "querystringify": "2.0.0", - "requires-port": "1.0.0" + "querystringify": "^2.0.0", + "requires-port": "^1.0.0" } }, "urlgrey": { @@ -13364,7 +13372,7 @@ "integrity": "sha512-6UJEQM/L+mzC3ZJNM56Q4DFGLX/evKGRg15UJHGB9X5j5Z3AFbgZvjUh2yq/UJUY4U5dh7Fal++XbNg1uzpRAw==", "dev": true, "requires": { - "kind-of": "6.0.2" + "kind-of": "^6.0.2" } }, "user-home": { @@ -13373,7 +13381,7 @@ "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", "dev": true, "requires": { - "os-homedir": "1.0.2" + "os-homedir": "^1.0.0" } }, "useragent": { @@ -13382,8 +13390,8 @@ "integrity": "sha1-z1k+9PLRdYdei7ZY6pLhik/QbY4=", "dev": true, "requires": { - "lru-cache": "2.2.4", - "tmp": "0.0.33" + "lru-cache": "2.2.x", + "tmp": "0.0.x" }, "dependencies": { "lru-cache": { @@ -13423,8 +13431,8 @@ "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", "dev": true, "requires": { - "define-properties": "1.1.2", - "object.getownpropertydescriptors": "2.0.3" + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" } }, "utila": { @@ -13458,7 +13466,7 @@ "integrity": "sha512-0m69VIK2dudEf2Ub0xwLQhZkDZu85OmiOpTw+UGDt56ibviYICHziM/3aE+oVg7IjGPp0c83w3eSVqa+lYZ9UQ==", "dev": true, "requires": { - "homedir-polyfill": "1.0.1" + "homedir-polyfill": "^1.0.1" } }, "validate-npm-package-license": { @@ -13467,8 +13475,8 @@ "integrity": "sha512-63ZOUnL4SIXj4L0NixR3L1lcjO38crAbgrTpl28t8jjrfuiOBL5Iygm+60qPs/KsZGzPNg6Smnc/oY16QTjF0g==", "dev": true, "requires": { - "spdx-correct": "3.0.0", - "spdx-expression-parse": "3.0.0" + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" } }, "validate-npm-package-name": { @@ -13477,7 +13485,7 @@ "integrity": "sha1-X6kS2B630MdK/BQN5zF/DKffQ34=", "dev": true, "requires": { - "builtins": "1.0.3" + "builtins": "^1.0.3" } }, "value-or-function": { @@ -13498,9 +13506,9 @@ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "dev": true, "requires": { - "assert-plus": "1.0.0", + "assert-plus": "^1.0.0", "core-util-is": "1.0.2", - "extsprintf": "1.3.0" + "extsprintf": "^1.2.0" } }, "vinyl": { @@ -13509,12 +13517,12 @@ "integrity": "sha1-Ah+cLPlR1rk5lDyJ617lrdT9kkw=", "dev": true, "requires": { - "clone": "2.1.1", - "clone-buffer": "1.0.0", - "clone-stats": "1.0.0", - "cloneable-readable": "1.1.2", - "remove-trailing-separator": "1.1.0", - "replace-ext": "1.0.0" + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" } }, "vinyl-fs": { @@ -13523,23 +13531,23 @@ "integrity": "sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng==", "dev": true, "requires": { - "fs-mkdirp-stream": "1.0.0", - "glob-stream": "6.1.0", - "graceful-fs": "4.1.11", - "is-valid-glob": "1.0.0", - "lazystream": "1.0.0", - "lead": "1.0.0", - "object.assign": "4.1.0", - "pumpify": "1.5.1", - "readable-stream": "2.3.6", - "remove-bom-buffer": "3.0.0", - "remove-bom-stream": "1.2.0", - "resolve-options": "1.1.0", - "through2": "2.0.3", - "to-through": "2.0.0", - "value-or-function": "3.0.0", - "vinyl": "2.1.0", - "vinyl-sourcemap": "1.1.0" + "fs-mkdirp-stream": "^1.0.0", + "glob-stream": "^6.1.0", + "graceful-fs": "^4.0.0", + "is-valid-glob": "^1.0.0", + "lazystream": "^1.0.0", + "lead": "^1.0.0", + "object.assign": "^4.0.4", + "pumpify": "^1.3.5", + "readable-stream": "^2.3.3", + "remove-bom-buffer": "^3.0.0", + "remove-bom-stream": "^1.2.0", + "resolve-options": "^1.1.0", + "through2": "^2.0.0", + "to-through": "^2.0.0", + "value-or-function": "^3.0.0", + "vinyl": "^2.0.0", + "vinyl-sourcemap": "^1.1.0" }, "dependencies": { "is-valid-glob": { @@ -13556,13 +13564,13 @@ "integrity": "sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY=", "dev": true, "requires": { - "append-buffer": "1.0.2", - "convert-source-map": "1.5.1", - "graceful-fs": "4.1.11", - "normalize-path": "2.1.1", - "now-and-later": "2.0.0", - "remove-bom-buffer": "3.0.0", - "vinyl": "2.1.0" + "append-buffer": "^1.0.2", + "convert-source-map": "^1.5.0", + "graceful-fs": "^4.1.6", + "normalize-path": "^2.1.1", + "now-and-later": "^2.0.0", + "remove-bom-buffer": "^3.0.0", + "vinyl": "^2.0.0" } }, "vm-browserify": { @@ -13586,9 +13594,9 @@ "integrity": "sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA==", "dev": true, "requires": { - "chokidar": "2.0.3", - "graceful-fs": "4.1.11", - "neo-async": "2.5.2" + "chokidar": "^2.0.2", + "graceful-fs": "^4.1.2", + "neo-async": "^2.5.0" } }, "wbuf": { @@ -13597,7 +13605,7 @@ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "dev": true, "requires": { - "minimalistic-assert": "1.0.1" + "minimalistic-assert": "^1.0.0" } }, "web-animations-js": { @@ -13615,7 +13623,7 @@ "@webassemblyjs/validation": "1.4.3", "@webassemblyjs/wasm-parser": "1.4.3", "@webassemblyjs/wast-parser": "1.4.3", - "long": "3.2.0" + "long": "^3.2.0" } }, "webdriver-js-extender": { @@ -13624,8 +13632,8 @@ "integrity": "sha512-fbyKiVu3azzIc5d4+26YfuPQcFTlgFQV5yQ/0OQj4Ybkl4g1YQuIPskf5v5wqwRJhHJnPHthB6tqCjWHOKLWag==", "dev": true, "requires": { - "@types/selenium-webdriver": "3.0.10", - "selenium-webdriver": "3.6.0" + "@types/selenium-webdriver": "^3.0.0", + "selenium-webdriver": "^3.0.1" } }, "webpack": { @@ -13637,26 +13645,26 @@ "@webassemblyjs/ast": "1.4.3", "@webassemblyjs/wasm-edit": "1.4.3", "@webassemblyjs/wasm-parser": "1.4.3", - "acorn": "5.5.3", - "acorn-dynamic-import": "3.0.0", - "ajv": "6.4.0", - "ajv-keywords": "3.2.0", - "chrome-trace-event": "0.1.3", - "enhanced-resolve": "4.1.0", - "eslint-scope": "3.7.3", - "json-parse-better-errors": "1.0.2", - "loader-runner": "2.3.0", - "loader-utils": "1.1.0", - "memory-fs": "0.4.1", - "micromatch": "3.1.10", - "mkdirp": "0.5.1", - "neo-async": "2.5.2", - "node-libs-browser": "2.1.0", - "schema-utils": "0.4.7", - "tapable": "1.0.0", - "uglifyjs-webpack-plugin": "1.3.0", - "watchpack": "1.6.0", - "webpack-sources": "1.1.0" + "acorn": "^5.0.0", + "acorn-dynamic-import": "^3.0.0", + "ajv": "^6.1.0", + "ajv-keywords": "^3.1.0", + "chrome-trace-event": "^0.1.1", + "enhanced-resolve": "^4.0.0", + "eslint-scope": "^3.7.1", + "json-parse-better-errors": "^1.0.2", + "loader-runner": "^2.3.0", + "loader-utils": "^1.1.0", + "memory-fs": "~0.4.1", + "micromatch": "^3.1.8", + "mkdirp": "~0.5.0", + "neo-async": "^2.5.0", + "node-libs-browser": "^2.0.0", + "schema-utils": "^0.4.4", + "tapable": "^1.0.0", + "uglifyjs-webpack-plugin": "^1.2.4", + "watchpack": "^1.5.0", + "webpack-sources": "^1.0.1" } }, "webpack-core": { @@ -13665,8 +13673,8 @@ "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", "dev": true, "requires": { - "source-list-map": "0.1.8", - "source-map": "0.4.4" + "source-list-map": "~0.1.7", + "source-map": "~0.4.1" }, "dependencies": { "source-list-map": { @@ -13681,7 +13689,7 @@ "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", "dev": true, "requires": { - "amdefine": "1.0.1" + "amdefine": ">=0.0.4" } } } @@ -13692,13 +13700,13 @@ "integrity": "sha512-I6Mmy/QjWU/kXwCSFGaiOoL5YEQIVmbb0o45xMoCyQAg/mClqZVTcsX327sPfekDyJWpCxb+04whNyLOIxpJdQ==", "dev": true, "requires": { - "loud-rejection": "1.6.0", - "memory-fs": "0.4.1", - "mime": "2.3.1", - "path-is-absolute": "1.0.1", - "range-parser": "1.2.0", - "url-join": "4.0.0", - "webpack-log": "1.2.0" + "loud-rejection": "^1.6.0", + "memory-fs": "~0.4.1", + "mime": "^2.1.0", + "path-is-absolute": "^1.0.0", + "range-parser": "^1.0.3", + "url-join": "^4.0.0", + "webpack-log": "^1.0.1" }, "dependencies": { "mime": { @@ -13716,32 +13724,32 @@ "dev": true, "requires": { "ansi-html": "0.0.7", - "array-includes": "3.0.3", - "bonjour": "3.5.0", - "chokidar": "2.0.3", - "compression": "1.7.3", - "connect-history-api-fallback": "1.5.0", - "debug": "3.1.0", - "del": "3.0.0", - "express": "4.16.3", - "html-entities": "1.2.1", - "http-proxy-middleware": "0.18.0", - "import-local": "1.0.0", + "array-includes": "^3.0.3", + "bonjour": "^3.5.0", + "chokidar": "^2.0.0", + "compression": "^1.5.2", + "connect-history-api-fallback": "^1.3.0", + "debug": "^3.1.0", + "del": "^3.0.0", + "express": "^4.16.2", + "html-entities": "^1.2.0", + "http-proxy-middleware": "~0.18.0", + "import-local": "^1.0.0", "internal-ip": "1.2.0", - "ip": "1.1.5", - "killable": "1.0.0", - "loglevel": "1.6.1", - "opn": "5.3.0", - "portfinder": "1.0.16", - "selfsigned": "1.10.3", - "serve-index": "1.9.1", + "ip": "^1.1.5", + "killable": "^1.0.0", + "loglevel": "^1.4.1", + "opn": "^5.1.0", + "portfinder": "^1.0.9", + "selfsigned": "^1.9.1", + "serve-index": "^1.7.2", "sockjs": "0.3.19", "sockjs-client": "1.1.5", - "spdy": "3.4.7", - "strip-ansi": "3.0.1", - "supports-color": "5.4.0", + "spdy": "^3.4.1", + "strip-ansi": "^3.0.0", + "supports-color": "^5.1.0", "webpack-dev-middleware": "3.1.3", - "webpack-log": "1.2.0", + "webpack-log": "^1.1.2", "yargs": "11.0.0" }, "dependencies": { @@ -13763,9 +13771,9 @@ "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", "dev": true, "requires": { - "string-width": "2.1.1", - "strip-ansi": "4.0.0", - "wrap-ansi": "2.1.0" + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" }, "dependencies": { "strip-ansi": { @@ -13774,7 +13782,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -13800,9 +13808,9 @@ "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", "dev": true, "requires": { - "execa": "0.7.0", - "lcid": "1.0.0", - "mem": "1.1.0" + "execa": "^0.7.0", + "lcid": "^1.0.0", + "mem": "^1.1.0" } }, "string-width": { @@ -13811,8 +13819,8 @@ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", "dev": true, "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, "dependencies": { "strip-ansi": { @@ -13821,7 +13829,7 @@ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", "dev": true, "requires": { - "ansi-regex": "3.0.0" + "ansi-regex": "^3.0.0" } } } @@ -13844,18 +13852,18 @@ "integrity": "sha512-Rjp+lMYQOWtgqojx1dEWorjCofi1YN7AoFvYV7b1gx/7dAAeuI4kN5SZiEvr0ZmsZTOpDRcCqrpI10L31tFkBw==", "dev": true, "requires": { - "cliui": "4.1.0", - "decamelize": "1.2.0", - "find-up": "2.1.0", - "get-caller-file": "1.0.2", - "os-locale": "2.1.0", - "require-directory": "2.1.1", - "require-main-filename": "1.0.1", - "set-blocking": "2.0.0", - "string-width": "2.1.1", - "which-module": "2.0.0", - "y18n": "3.2.1", - "yargs-parser": "9.0.2" + "cliui": "^4.0.0", + "decamelize": "^1.1.1", + "find-up": "^2.1.0", + "get-caller-file": "^1.0.1", + "os-locale": "^2.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1", + "yargs-parser": "^9.0.2" } }, "yargs-parser": { @@ -13864,7 +13872,7 @@ "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=", "dev": true, "requires": { - "camelcase": "4.1.0" + "camelcase": "^4.1.0" } } } @@ -13875,10 +13883,10 @@ "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==", "dev": true, "requires": { - "chalk": "2.2.2", - "log-symbols": "2.2.0", - "loglevelnext": "1.0.5", - "uuid": "3.2.1" + "chalk": "^2.1.0", + "log-symbols": "^2.1.0", + "loglevelnext": "^1.0.1", + "uuid": "^3.1.0" } }, "webpack-merge": { @@ -13887,7 +13895,7 @@ "integrity": "sha512-TmSe1HZKeOPey3oy1Ov2iS3guIZjWvMT2BBJDzzT5jScHTjVC3mpjJofgueEzaEd6ibhxRDD6MIblDr8tzh8iQ==", "dev": true, "requires": { - "lodash": "4.17.10" + "lodash": "^4.17.5" } }, "webpack-sources": { @@ -13896,8 +13904,8 @@ "integrity": "sha512-aqYp18kPphgoO5c/+NaUvEeACtZjMESmDChuD3NBciVpah3XpMEU9VAAtIaB1BsfJWWTSdv8Vv1m3T0aRk2dUw==", "dev": true, "requires": { - "source-list-map": "2.0.0", - "source-map": "0.6.1" + "source-list-map": "^2.0.0", + "source-map": "~0.6.1" }, "dependencies": { "source-map": { @@ -13914,7 +13922,7 @@ "integrity": "sha1-xcTj1pD50vZKlVDgeodn+Xlqpdg=", "dev": true, "requires": { - "webpack-core": "0.6.9" + "webpack-core": "^0.6.8" } }, "websocket-driver": { @@ -13923,8 +13931,8 @@ "integrity": "sha1-DK+dLXVdk67gSdS90NP+LMoqJOs=", "dev": true, "requires": { - "http-parser-js": "0.4.13", - "websocket-extensions": "0.1.3" + "http-parser-js": ">=0.4.0", + "websocket-extensions": ">=0.1.1" } }, "websocket-extensions": { @@ -13945,7 +13953,7 @@ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, "requires": { - "isexe": "2.0.0" + "isexe": "^2.0.0" } }, "which-module": { @@ -13960,7 +13968,7 @@ "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "requires": { - "string-width": "1.0.2" + "string-width": "^1.0.2 || 2" } }, "window-size": { @@ -13989,7 +13997,7 @@ "integrity": "sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ==", "dev": true, "requires": { - "errno": "0.1.7" + "errno": "~0.1.7" } }, "wrap-ansi": { @@ -13998,8 +14006,8 @@ "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", "dev": true, "requires": { - "string-width": "1.0.2", - "strip-ansi": "3.0.1" + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" } }, "wrappy": { @@ -14014,7 +14022,7 @@ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", "dev": true, "requires": { - "mkdirp": "0.5.1" + "mkdirp": "^0.5.1" } }, "ws": { @@ -14023,9 +14031,9 @@ "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", "dev": true, "requires": { - "async-limiter": "1.0.0", - "safe-buffer": "5.1.2", - "ultron": "1.1.1" + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" } }, "xhr2": { @@ -14039,8 +14047,8 @@ "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", "dev": true, "requires": { - "sax": "1.2.4", - "xmlbuilder": "9.0.7" + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" }, "dependencies": { "sax": { @@ -14087,7 +14095,7 @@ "integrity": "sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==", "dev": true, "requires": { - "cuint": "0.2.2" + "cuint": "^0.2.2" } }, "y18n": { @@ -14109,9 +14117,9 @@ "dev": true, "optional": true, "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", + "camelcase": "^1.0.2", + "cliui": "^2.1.0", + "decamelize": "^1.0.0", "window-size": "0.1.0" } }, @@ -14121,7 +14129,7 @@ "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=", "dev": true, "requires": { - "camelcase": "3.0.0" + "camelcase": "^3.0.0" }, "dependencies": { "camelcase": { @@ -14138,7 +14146,7 @@ "integrity": "sha1-7CblzIfVYBud+EMtvdPNLlFzoHE=", "dev": true, "requires": { - "buffer-crc32": "0.2.13" + "buffer-crc32": "~0.2.3" } }, "yeast": { diff --git a/package.json b/package.json index d832badbb0..5a1dd053fa 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@angular/forms": "^6.1.1", "@angular/http": "^6.1.1", "@angular/material": "^6.1.0", + "@angular/material-moment-adapter": "^6.4.7", "@angular/platform-browser": "^6.1.1", "@angular/platform-browser-dynamic": "^6.1.1", "@angular/platform-server": "^6.1.1", @@ -55,7 +56,7 @@ "@ngrx/router-store": "^6.0.1", "@ngrx/store": "^6.0.1", "@ngrx/store-devtools": "^6.0.1", - "@swimlane/ngx-charts": "^8.1.0", + "@swimlane/ngx-charts": "^9.0.0", "angular2-virtual-scroll": "^0.3.1", "core-js": "^2.5.7", "hammerjs": "^2.0.8", From 7476f040270fad1e73b4bf63b9df37513e18b4fe Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Mon, 10 Sep 2018 16:44:33 +0100 Subject: [PATCH 012/114] Allow rolling time windows on metrics charts --- .../date-time/date-time.component.ts | 7 +- .../metrics-chart.component.html | 13 +- .../metrics-chart/metrics-chart.component.ts | 139 +++++++++++++++--- .../metrics.component.manager.ts | 6 +- .../start-end-date.component.ts | 22 +-- .../app/store/actions/metrics.actions.ts | 3 +- .../app/store/effects/metrics.effects.ts | 10 +- .../app/store/types/base-metric.types.ts | 10 +- 8 files changed, 163 insertions(+), 47 deletions(-) diff --git a/src/frontend/app/shared/components/date-time/date-time.component.ts b/src/frontend/app/shared/components/date-time/date-time.component.ts index f91cca9af4..431687558f 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.ts +++ b/src/frontend/app/shared/components/date-time/date-time.component.ts @@ -2,7 +2,7 @@ import { SetupModule } from './../../../features/setup/setup.module'; import { Component, OnInit, Output, OnDestroy, Input, EventEmitter } from '@angular/core'; import { FormControl } from '@angular/forms'; import * as moment from 'moment'; -import { tap, map, filter, shareReplay } from 'rxjs/operators'; +import { tap, map, filter, shareReplay, debounceTime } from 'rxjs/operators'; import { combineLatest, Subscription, Observable } from 'rxjs'; @Component({ @@ -30,14 +30,14 @@ export class DateTimeComponent implements OnDestroy { } set dateTime(dateTime: moment.Moment) { - if (!this.dateTimeValue || !dateTime.isSame(this.dateTimeValue)) { + if (dateTime && dateTime.isValid() && (!this.dateTimeValue || !dateTime.isSame(this.dateTimeValue))) { this.dateTimeValue = dateTime; this.dateTimeChange.emit(this.dateTimeValue); } } private isDifferentDate(oldDate: moment.Moment, newDate: moment.Moment) { - return !oldDate || !oldDate.isSame(newDate); + return !oldDate || !newDate || !newDate.isValid() || !oldDate.isSame(newDate); } private setupInputSub() { @@ -46,6 +46,7 @@ export class DateTimeComponent implements OnDestroy { this.timeObservable, this.dateObservable ).pipe( + debounceTime(250), filter(([time, date]) => !!(time && date)), map(([time, date]: [string, moment.Moment]) => { const [hour, minute] = time.split(':'); diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 98ead19f09..86a926224c 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,6 +1,15 @@
-

{{title}}

- +
+

{{title}}

+ + + + {{time.label}} + + + +
+
diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 00bb62cd1f..a3747cb888 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -2,8 +2,8 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import * as moment from 'moment'; import { Subscription } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; -import { FetchApplicationMetricsAction, MetricQueryType, MetricsAction, MetricQueryConfig } from '../../../store/actions/metrics.actions'; +import { delay, map } from 'rxjs/operators'; +import { FetchApplicationMetricsAction, MetricQueryConfig, MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { EntityMonitor } from '../../monitors/entity-monitor'; @@ -12,8 +12,6 @@ import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.se import { MetricsChartTypes } from './metrics-chart.types'; import { MetricsChartManager } from './metrics.component.manager'; - - export interface MetricsConfig { metricsAction: MetricsAction; getSeriesName: (T) => string; @@ -28,6 +26,16 @@ export interface MetricsChartConfig { yAxisLabel?: string; } +enum RangeType { + ROLLING_WINDOW = 'ROLLING_WINDOW', + START_END = 'START_END' +} + +interface ITimeRange { + value?: string; + label: string; + type: RangeType; +} @Component({ selector: 'app-metrics-chart', templateUrl: './metrics-chart.component.html', @@ -52,13 +60,42 @@ export class MetricsChartComponent implements OnInit, OnDestroy { public results$; private readonly startIndex = 0; + private readonly endIndex = 1; + public metricsMonitor: EntityMonitor; + public rangeTypes = RangeType; + + public times: ITimeRange[] = [ + { + value: '5m', + label: 'The past 5 minutes', + type: RangeType.ROLLING_WINDOW + }, + { + value: '1h', + label: 'The past hour', + type: RangeType.ROLLING_WINDOW + }, + { + value: '1w', + label: 'The past week', + type: RangeType.ROLLING_WINDOW + }, + { + label: 'Over a specific period', + type: RangeType.START_END + } + ]; + + public selectedTimeRangeValue: ITimeRange; + + private commitDate(date: moment.Moment, type: 'start' | 'end') { const index = type === 'start' ? this.startIndex : this.endIndex; const oldDate = this.startEnd[index]; - if (oldDate && date.isSame(oldDate)) { + if (!date.isValid() || date.isSame(oldDate)) { return; } this.startEnd[index] = date; @@ -73,7 +110,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { start: startUnix, end: endUnix, - step: (endUnix - startUnix) / 100 + step: Math.max((endUnix - startUnix) / 100, 0) }), MetricQueryType.RANGE_QUERY ); @@ -82,6 +119,33 @@ export class MetricsChartComponent implements OnInit, OnDestroy { } } + get selectedTimeRange() { + return this.selectedTimeRangeValue; + } + + set selectedTimeRange(timeRange: ITimeRange) { + this.selectedTimeRangeValue = timeRange; + if (this.selectedTimeRangeValue.type === RangeType.ROLLING_WINDOW) { + this.commitWindow(this.selectedTimeRangeValue); + } + } + + private commitWindow(window: ITimeRange) { + if (!window || window.ty) { + return; + } + const oldAction = this.metricsConfig.metricsAction; + this.metricsConfig.metricsAction = new FetchApplicationMetricsAction( + oldAction.guid, + oldAction.cfGuid, + new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { + window: window.value + }) + ); + this.commit = this.getCommitFn(this.metricsConfig.metricsAction); + this.commit(); + } + set start(start: moment.Moment) { this.commitDate(start, 'start'); } @@ -120,7 +184,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { this.metricsConfig.metricsAction.query.params.end as number, this.metricsConfig.metricsAction.query.params.step as number, this.metricsConfig, - ) + ); } } return metricsArray; @@ -132,26 +196,48 @@ export class MetricsChartComponent implements OnInit, OnDestroy { metricSchemaKey, entityFactory(metricSchemaKey) ); + + this.results$ = this.metricsMonitor.entity$.pipe( - filter(metrics => !!metrics), + delay(1), map(metrics => { - const metricsArray = this.mapMetricsToChartData(metrics, this.metricsConfig); - if (!metricsArray.length) { + if (metrics) { + if (metrics.queryType === MetricQueryType.RANGE_QUERY) { + const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); + const end = moment.unix(parseInt(metrics.query.params.end as string, 10)); + const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); + if (isDifferent) { + this.start = start; + this.end = end; + } + this.selectedTimeRange = this.times.find(time => time.type === RangeType.START_END); + } else { + const newWindow = metrics.query.params.window ? + this.times.find(time => time.value === metrics.query.params.window) : + this.times[0]; + if (this.selectedTimeRange !== newWindow) { + this.selectedTimeRange = newWindow; + } + } + + const metricsArray = this.mapMetricsToChartData(metrics, this.metricsConfig); + if (!metricsArray.length) { + return null; + } + return this.postFetchMiddleware(metricsArray); + } else { + this.selectedTimeRange = this.times[0]; return null; } - return this.postFetchMiddleware(metricsArray); }) ); - const now = moment(moment.now()); - this.start = moment(now).subtract(1, 'weeks'); - this.end = now; } private setup(action: MetricsAction) { if (this.pollSub) { this.pollSub.unsubscribe(); } - this.store.dispatch(action); + this.pollSub = this.metricsMonitor .poll( 30000, @@ -167,21 +253,26 @@ export class MetricsChartComponent implements OnInit, OnDestroy { } private mapMetricsToChartData(metrics: IMetrics, metricsConfig: MetricsConfig) { - switch (metrics.resultType) { - case MetricResultTypes.MATRIX: - return MetricsChartManager.mapMatrix(metrics, metricsConfig); - case MetricResultTypes.VECTOR: - return MetricsChartManager.mapVector(metrics, metricsConfig); - case MetricResultTypes.SCALAR: - case MetricResultTypes.STRING: - default: - throw new Error(`Could not find chart data mapper for metrics type ${metrics.resultType}`); + if (metrics.data) { + switch (metrics.data.resultType) { + case MetricResultTypes.MATRIX: + return MetricsChartManager.mapMatrix(metrics.data, metricsConfig); + case MetricResultTypes.VECTOR: + return MetricsChartManager.mapVector(metrics.data, metricsConfig); + case MetricResultTypes.SCALAR: + case MetricResultTypes.STRING: + default: + throw new Error(`Could not find chart data mapper for metrics type ${metrics.data.resultType}`); + } + } else { + return []; } } private getCommitFn(action: MetricsAction) { return () => { this.setup(action); + this.store.dispatch(action); }; } } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts b/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts index 2e1035fe47..540f994e0f 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts @@ -1,9 +1,9 @@ import { MetricsConfig } from './metrics-chart.component'; -import { IMetrics, ChartSeries } from '../../../store/types/base-metric.types'; +import { IMetrics, ChartSeries, IMetricsData } from '../../../store/types/base-metric.types'; import { MetricsChartHelpers } from './metrics.component.helpers'; export class MetricsChartManager { - static mapMatrix(metrics: IMetrics, metricsConfig: MetricsConfig): ChartSeries[] { + static mapMatrix(metrics: IMetricsData, metricsConfig: MetricsConfig): ChartSeries[] { return metrics.result.map>( result => ({ name: metricsConfig.getSeriesName(result), @@ -14,7 +14,7 @@ export class MetricsChartManager { }) ); } - static mapVector(metrics: IMetrics, metricsConfig: MetricsConfig): ChartSeries[] { + static mapVector(metrics: IMetricsData, metricsConfig: MetricsConfig): ChartSeries[] { return metrics.result.map>( result => ({ name: metricsConfig.getSeriesName(result), diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts index e905741deb..6e2581beec 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -16,15 +16,17 @@ export class StartEndDateComponent { private endValue: moment.Moment; private isDifferentDate(oldDate: moment.Moment, newDate: moment.Moment) { - return !oldDate || !oldDate.isSame(newDate); + return !oldDate || !newDate || !oldDate.isSame(newDate); } @Input() set start(start: moment.Moment) { - if (this.isDifferentDate(this.startValue, start)) { - const clone = moment(start); - this.startValue = clone; - this.startChange.emit(clone); + if (start && start.isValid()) { + if (this.isDifferentDate(this.startValue, start)) { + const clone = moment(start); + this.startValue = clone; + this.startChange.emit(clone); + } } } @@ -34,10 +36,12 @@ export class StartEndDateComponent { @Input() set end(end: moment.Moment) { - if (this.isDifferentDate(this.endValue, end)) { - const clone = moment(end); - this.endValue = clone; - this.endChange.emit(clone); + if (end && end.isValid()) { + if (this.isDifferentDate(this.endValue, end)) { + const clone = moment(end); + this.endValue = clone; + this.endChange.emit(clone); + } } } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index c3b0234084..d5bde7ca75 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -28,10 +28,11 @@ export class MetricQueryConfig { window = '', ...params } = this.params; + const windowString = window ? `{}[${window}]` : ''; const paramString = Object.keys(params).reduce((accum, key) => { return accum + `&${key}=${params[key]}`; }, ''); - return (window + paramString) || ''; + return windowString + paramString || ''; } } diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index eafbde941c..f451bef265 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -1,5 +1,5 @@ -import { catchError, mergeMap, map } from 'rxjs/operators'; +import { catchError, mergeMap, map, switchMap } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; @@ -22,7 +22,7 @@ export class MetricsEffect { ) { } @Effect() metrics$ = this.actions$.ofType(METRICS_START).pipe( - mergeMap(action => { + switchMap(action => { const fullUrl = this.buildFullUrl(action); const apiAction = { guid: action.guid, @@ -35,7 +35,11 @@ export class MetricsEffect { map(metrics => { const metric = metrics[action.cfGuid]; const metricObject = metric ? { - [action.metricId]: metric.data + [action.metricId]: { + query: action.query, + queryType: action.queryType, + data: metric.data + } } : {}; return new WrapperRequestActionSuccess( { diff --git a/src/frontend/app/store/types/base-metric.types.ts b/src/frontend/app/store/types/base-metric.types.ts index baa9e59e05..d4bb0d2c25 100644 --- a/src/frontend/app/store/types/base-metric.types.ts +++ b/src/frontend/app/store/types/base-metric.types.ts @@ -1,3 +1,5 @@ +import { MetricQueryConfig, MetricQueryType } from "../actions/metrics.actions"; + export enum MetricResultTypes { MATRIX = 'matrix', VECTOR = 'vector', @@ -9,11 +11,15 @@ export interface IMetricsResponse { status: string; data: IMetrics; } - -export interface IMetrics { +export interface IMetricsData { resultType: string; result: [T]; } +export interface IMetrics { + query: MetricQueryConfig; + queryType: MetricQueryType; + data: IMetricsData; +} interface IVectorResult { metric: T; From 5591dd06405a0c504b81ae906633bba052cfd22a Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 11 Sep 2018 15:57:39 +0100 Subject: [PATCH 013/114] Fixed up time selection --- .../metrics-tab/metrics-tab.component.html | 6 +- .../metrics-tab/metrics-tab.component.scss | 2 +- .../metrics-chart.component.html | 4 +- .../metrics-chart.component.scss | 7 +- .../metrics-chart/metrics-chart.component.ts | 86 +++++++++++-------- .../start-end-date.component.html | 3 + .../start-end-date.component.ts | 18 ++++ .../entity-monitor.factory.service.ts | 6 +- .../app/shared/monitors/entity-monitor.ts | 14 ++- .../app/store/effects/metrics.effects.ts | 4 +- 10 files changed, 100 insertions(+), 50 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html index ca70406595..fcba09f8cc 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html @@ -1,15 +1,15 @@ - - - \ No newline at end of file diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.scss b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.scss index 5311d5661d..58eef9915b 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.scss +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.scss @@ -1,4 +1,4 @@ .metric-chart-wrapper { - height: 360px; margin-bottom: 20px; + height: 500px; } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 86a926224c..019ec66619 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -2,14 +2,14 @@

{{title}}

- + {{time.label}} +
-
diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index 53925558a0..21d7ba9772 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -1,6 +1,10 @@ .metrics-chart { height: 100%; width: 100%; + &__header { + align-items: center; + display: flex; + } &__outer { display: flex; flex-direction: column; @@ -8,8 +12,7 @@ width: 100%; } &__title { - font-size: 14px; - margin: 0 0 10px; + margin: 0 10px 10px; text-align: left; } &__loading { diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index a3747cb888..5dd63c2f04 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -2,7 +2,7 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import * as moment from 'moment'; import { Subscription } from 'rxjs'; -import { delay, map } from 'rxjs/operators'; +import { delay, map, tap, first, filter } from 'rxjs/operators'; import { FetchApplicationMetricsAction, MetricQueryConfig, MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; @@ -55,6 +55,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy { private pollSub: Subscription; + private initSub: Subscription; + private commit: Function = null; public results$; @@ -91,7 +93,6 @@ export class MetricsChartComponent implements OnInit, OnDestroy { public selectedTimeRangeValue: ITimeRange; - private commitDate(date: moment.Moment, type: 'start' | 'end') { const index = type === 'start' ? this.startIndex : this.endIndex; const oldDate = this.startEnd[index]; @@ -110,7 +111,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { start: startUnix, end: endUnix, - step: Math.max((endUnix - startUnix) / 100, 0) + step: Math.max((endUnix - startUnix) / 300, 0) }), MetricQueryType.RANGE_QUERY ); @@ -131,7 +132,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { } private commitWindow(window: ITimeRange) { - if (!window || window.ty) { + if (!window) { return; } const oldAction = this.metricsConfig.metricsAction; @@ -190,17 +191,10 @@ export class MetricsChartComponent implements OnInit, OnDestroy { return metricsArray; } - ngOnInit() { - this.metricsMonitor = this.entityMonitorFactory.create( - this.metricsConfig.metricsAction.metricId, - metricSchemaKey, - entityFactory(metricSchemaKey) - ); - - - this.results$ = this.metricsMonitor.entity$.pipe( - delay(1), - map(metrics => { + private getInitSub(entityMonitor: EntityMonitor) { + return entityMonitor.entity$.pipe( + first(), + tap(metrics => { if (metrics) { if (metrics.queryType === MetricQueryType.RANGE_QUERY) { const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); @@ -214,21 +208,39 @@ export class MetricsChartComponent implements OnInit, OnDestroy { } else { const newWindow = metrics.query.params.window ? this.times.find(time => time.value === metrics.query.params.window) : - this.times[0]; + this.getDefaultTimeRange(); if (this.selectedTimeRange !== newWindow) { this.selectedTimeRange = newWindow; } } - - const metricsArray = this.mapMetricsToChartData(metrics, this.metricsConfig); - if (!metricsArray.length) { - return null; - } - return this.postFetchMiddleware(metricsArray); } else { - this.selectedTimeRange = this.times[0]; + this.selectedTimeRange = this.getDefaultTimeRange(); + } + }) + ).subscribe(); + } + + private getDefaultTimeRange() { + return this.times.find(time => time.value === '1h') || this.times[0]; + } + + ngOnInit() { + this.metricsMonitor = this.entityMonitorFactory.create( + this.metricsConfig.metricsAction.metricId, + metricSchemaKey, + entityFactory(metricSchemaKey), + false + ); + + this.initSub = this.getInitSub(this.metricsMonitor); + + this.results$ = this.metricsMonitor.entity$.pipe( + map(metrics => { + const metricsArray = this.mapMetricsToChartData(metrics, this.metricsConfig); + if (!metricsArray.length) { return null; } + return this.postFetchMiddleware(metricsArray); }) ); } @@ -237,23 +249,29 @@ export class MetricsChartComponent implements OnInit, OnDestroy { if (this.pollSub) { this.pollSub.unsubscribe(); } - - this.pollSub = this.metricsMonitor - .poll( - 30000, - () => { - this.store.dispatch(action); - }, - request => ({ busy: request.fetching, error: request.error, message: request.message }) - ).subscribe(); + if (action.queryType === MetricQueryType.QUERY) { + this.pollSub = this.metricsMonitor + .poll( + 1000, + () => { + this.store.dispatch(action); + }, + request => ({ busy: request.fetching, error: request.error, message: request.message }) + ).subscribe(); + } } ngOnDestroy() { - this.pollSub.unsubscribe(); + if (this.pollSub) { + this.pollSub.unsubscribe(); + } + if (this.initSub) { + this.initSub.unsubscribe(); + } } private mapMetricsToChartData(metrics: IMetrics, metricsConfig: MetricsConfig) { - if (metrics.data) { + if (metrics && metrics.data) { switch (metrics.data.resultType) { case MetricResultTypes.MATRIX: return MetricsChartManager.mapMatrix(metrics.data, metricsConfig); diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.html b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html index c92cc119b7..ced9750732 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.html +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html @@ -1,4 +1,7 @@
+
+ Start date must be before end date. +
Start
diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts index 6e2581beec..b410c65bc2 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -15,13 +15,27 @@ export class StartEndDateComponent { private startValue: moment.Moment; private endValue: moment.Moment; + public invalid = false; + private isDifferentDate(oldDate: moment.Moment, newDate: moment.Moment) { return !oldDate || !newDate || !oldDate.isSame(newDate); } + private isStartEndValid(start: moment.Moment, end: moment.Moment) { + if (!end || !start) { + return true; + } + return start.isBefore(end); + } + @Input() set start(start: moment.Moment) { + this.invalid = false; if (start && start.isValid()) { + if (!this.isStartEndValid(start, this.end)) { + this.invalid = true; + return; + } if (this.isDifferentDate(this.startValue, start)) { const clone = moment(start); this.startValue = clone; @@ -36,7 +50,11 @@ export class StartEndDateComponent { @Input() set end(end: moment.Moment) { + this.invalid = false; if (end && end.isValid()) { + if (!this.isStartEndValid(this.start, end)) { + this.invalid = true; + } if (this.isDifferentDate(this.endValue, end)) { const clone = moment(end); this.endValue = clone; diff --git a/src/frontend/app/shared/monitors/entity-monitor.factory.service.ts b/src/frontend/app/shared/monitors/entity-monitor.factory.service.ts index 687e98ae7f..e1f5fa6532 100644 --- a/src/frontend/app/shared/monitors/entity-monitor.factory.service.ts +++ b/src/frontend/app/shared/monitors/entity-monitor.factory.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { EntityMonitor } from './entity-monitor'; import { AppState } from '../../store/app-state'; import { Store } from '@ngrx/store'; -import { schema as normalizrSchema} from 'normalizr'; +import { schema as normalizrSchema } from 'normalizr'; @Injectable() export class EntityMonitorFactory { @@ -17,6 +17,7 @@ export class EntityMonitorFactory { id: string, entityKey: string, schema: normalizrSchema.Entity, + startWithNull = true ): EntityMonitor { const cacheKey = id + entityKey; if (this.monitorCache[cacheKey]) { @@ -26,7 +27,8 @@ export class EntityMonitorFactory { this.store, id, entityKey, - schema + schema, + startWithNull ); this.monitorCache[cacheKey] = monitor; return monitor; diff --git a/src/frontend/app/shared/monitors/entity-monitor.ts b/src/frontend/app/shared/monitors/entity-monitor.ts index f70ebe512a..3fab22f202 100644 --- a/src/frontend/app/shared/monitors/entity-monitor.ts +++ b/src/frontend/app/shared/monitors/entity-monitor.ts @@ -21,6 +21,7 @@ export class EntityMonitor { public id: string, public entityKey: string, public schema: normalizrSchema.Entity, + startWithNull = true ) { const defaultRequestState = getDefaultRequestState(); this.entityRequest$ = store.select(selectRequestInfo(entityKey, id)).pipe( @@ -39,13 +40,19 @@ export class EntityMonitor { distinctUntilChanged() ); - this.apiRequestData$ = this.store.select(getAPIRequestDataState).pipe(publishReplay(1), refCount(), ); - this.entity$ = this.getEntityObservable( + this.apiRequestData$ = this.store.select(getAPIRequestDataState).pipe(publishReplay(1), refCount()); + + const entity$ = this.getEntityObservable( schema, store.select(selectEntity(entityKey, id)), this.entityRequest$, store.select(getAPIRequestDataState), ); + if (startWithNull) { + this.entity$ = entity$.pipe(startWith(null)); + } else { + this.entity$ = entity$; + } } private updatingSectionObservableCache: { [key: string]: Observable @@ -107,8 +114,7 @@ export class EntityMonitor { ]) => { return entity ? denormalize(entity, schema, entities) : null; }), - distinctUntilChanged(), - startWith(null) + distinctUntilChanged() ); } diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index f451bef265..ad0ffcafa7 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -1,5 +1,5 @@ -import { catchError, mergeMap, map, switchMap } from 'rxjs/operators'; +import { catchError, mergeMap, map } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; @@ -22,7 +22,7 @@ export class MetricsEffect { ) { } @Effect() metrics$ = this.actions$.ofType(METRICS_START).pipe( - switchMap(action => { + mergeMap(action => { const fullUrl = this.buildFullUrl(action); const apiAction = { guid: action.guid, From 5b76c776cc7363ffd791543895a09230dadefe9b Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 13 Sep 2018 16:56:36 +0100 Subject: [PATCH 014/114] Improve UX of start end selector on metrics chart --- .../date-time/date-time.component.html | 4 +- .../date-time/date-time.component.scss | 5 ++ .../date-time/date-time.component.ts | 1 + .../metrics-chart.component.html | 28 +++++-- .../metrics-chart.component.scss | 39 ++++++++++ .../metrics-chart.component.theme.scss | 12 +++ .../metrics-chart/metrics-chart.component.ts | 78 +++++++++++-------- .../start-end-date.component.html | 10 +-- .../start-end-date.component.scss | 16 ++-- .../start-end-date.component.theme.scss | 7 ++ .../start-end-date.component.ts | 24 ++++-- src/frontend/app/shared/shared.module.ts | 2 + src/frontend/sass/_all-theme.scss | 4 + 13 files changed, 171 insertions(+), 59 deletions(-) create mode 100644 src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss create mode 100644 src/frontend/app/shared/components/start-end-date/start-end-date.component.theme.scss diff --git a/src/frontend/app/shared/components/date-time/date-time.component.html b/src/frontend/app/shared/components/date-time/date-time.component.html index b6669ac71b..283b03197e 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.html +++ b/src/frontend/app/shared/components/date-time/date-time.component.html @@ -1,10 +1,10 @@
- + - + access_time diff --git a/src/frontend/app/shared/components/date-time/date-time.component.scss b/src/frontend/app/shared/components/date-time/date-time.component.scss index 742417a1c0..d597e5cb53 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.scss +++ b/src/frontend/app/shared/components/date-time/date-time.component.scss @@ -1,3 +1,8 @@ .date-time-form { display: flex; } + +.form-field { + display: flex; + height: 66px; +} \ No newline at end of file diff --git a/src/frontend/app/shared/components/date-time/date-time.component.ts b/src/frontend/app/shared/components/date-time/date-time.component.ts index 431687558f..44f22a309e 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.ts +++ b/src/frontend/app/shared/components/date-time/date-time.component.ts @@ -114,6 +114,7 @@ export class DateTimeComponent implements OnDestroy { ); this.setupInputSub(); this.setupChangeSub(); + this.time.setValue('00:00'); } ngOnDestroy() { diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 019ec66619..b832cd1aae 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,4 +1,12 @@
+
+
+ + + +
+

{{title}}

@@ -8,13 +16,23 @@

{{title}}

- +
+
+
{{ committedStartEnd[0] | amDateFormat:'LL' }}
+
to
+
{{ committedStartEnd[1] | amDateFormat:'LL' }}
+
+ Edit +
- + -
- +
+ {{ chartConfig.chartType }} chart type not found
diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index 21d7ba9772..9f1fe13a80 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -20,4 +20,43 @@ min-width: 100px; width: 10%; } + &__overlay { + background-color: rgba(0, 0, 0, .6); + bottom: 0; + display: none; + left: 0; + position: absolute; + right: 0; + top: 0; + z-index: 1; + &-show { + display: block; + } + &-inner { + padding: 60px; + } + &-set { + margin-right: 10px; + } + } + &__selected-range { + align-items: center; + display: flex; + } + &__selected-range-dates { + display: flex; + font-size: 14px; + opacity: .6; + padding-left: 10px; + } + &__selected-range-date { + font-weight: bold; + } + &__selected-range-to { + padding: 0 10px; + } + &__selected-range-edit { + cursor: pointer; + margin-left: 10px; + } } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss new file mode 100644 index 0000000000..4631e0e850 --- /dev/null +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss @@ -0,0 +1,12 @@ +@import '~@angular/material/theming'; +@mixin metrics-chart-theme($theme, $app-theme) { + $primary: map-get($theme, primary); + .metrics-chart { + &__overlay { + &-inner { + background-color: mat-color($primary, 500); + color: mat-contrast($primary, 500); + } + } + } +} diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 5dd63c2f04..76989f7b26 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -2,7 +2,7 @@ import { Component, Input, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import * as moment from 'moment'; import { Subscription } from 'rxjs'; -import { delay, map, tap, first, filter } from 'rxjs/operators'; +import { delay, map, tap, first, filter, debounceTime, takeWhile } from 'rxjs/operators'; import { FetchApplicationMetricsAction, MetricQueryConfig, MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; @@ -53,6 +53,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy { private startEnd: [moment.Moment, moment.Moment] = [null, null]; + private committedStartEnd: [moment.Moment, moment.Moment] = [null, null]; + private pollSub: Subscription; private initSub: Subscription; @@ -69,6 +71,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy { public rangeTypes = RangeType; + public dateValid = false; + public times: ITimeRange[] = [ { value: '5m', @@ -92,6 +96,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { ]; public selectedTimeRangeValue: ITimeRange; + public showOverlay = false; private commitDate(date: moment.Moment, type: 'start' | 'end') { const index = type === 'start' ? this.startIndex : this.endIndex; @@ -115,8 +120,14 @@ export class MetricsChartComponent implements OnInit, OnDestroy { }), MetricQueryType.RANGE_QUERY ); - this.commit = this.getCommitFn(this.metricsConfig.metricsAction); - this.commit(); + + this.commit = () => { + this.committedStartEnd = [ + this.startEnd[0], + this.startEnd[1] + ]; + this.commitAction(this.metricsConfig.metricsAction); + }; } } @@ -125,9 +136,12 @@ export class MetricsChartComponent implements OnInit, OnDestroy { } set selectedTimeRange(timeRange: ITimeRange) { + this.commit = null; this.selectedTimeRangeValue = timeRange; if (this.selectedTimeRangeValue.type === RangeType.ROLLING_WINDOW) { this.commitWindow(this.selectedTimeRangeValue); + } else if (this.selectedTimeRangeValue.type === RangeType.START_END) { + this.showOverlay = true; } } @@ -143,8 +157,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { window: window.value }) ); - this.commit = this.getCommitFn(this.metricsConfig.metricsAction); - this.commit(); + this.commitAction(this.metricsConfig.metricsAction); } set start(start: moment.Moment) { @@ -193,30 +206,33 @@ export class MetricsChartComponent implements OnInit, OnDestroy { private getInitSub(entityMonitor: EntityMonitor) { return entityMonitor.entity$.pipe( - first(), + debounceTime(1), tap(metrics => { - if (metrics) { - if (metrics.queryType === MetricQueryType.RANGE_QUERY) { - const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); - const end = moment.unix(parseInt(metrics.query.params.end as string, 10)); - const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); - if (isDifferent) { - this.start = start; - this.end = end; + if (!this.selectedTimeRange) { + if (metrics) { + if (metrics.queryType === MetricQueryType.RANGE_QUERY) { + const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); + const end = moment.unix(parseInt(metrics.query.params.end as string, 10)); + const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); + if (isDifferent) { + this.start = start; + this.end = end; + } + this.selectedTimeRange = this.times.find(time => time.type === RangeType.START_END); + } else { + const newWindow = metrics.query.params.window ? + this.times.find(time => time.value === metrics.query.params.window) : + this.getDefaultTimeRange(); + if (this.selectedTimeRange !== newWindow) { + this.selectedTimeRange = newWindow; + } } - this.selectedTimeRange = this.times.find(time => time.type === RangeType.START_END); } else { - const newWindow = metrics.query.params.window ? - this.times.find(time => time.value === metrics.query.params.window) : - this.getDefaultTimeRange(); - if (this.selectedTimeRange !== newWindow) { - this.selectedTimeRange = newWindow; - } + this.selectedTimeRange = this.getDefaultTimeRange(); } - } else { - this.selectedTimeRange = this.getDefaultTimeRange(); } - }) + }), + takeWhile(metrics => !metrics) ).subscribe(); } @@ -228,8 +244,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { this.metricsMonitor = this.entityMonitorFactory.create( this.metricsConfig.metricsAction.metricId, metricSchemaKey, - entityFactory(metricSchemaKey), - false + entityFactory(metricSchemaKey) ); this.initSub = this.getInitSub(this.metricsMonitor); @@ -252,7 +267,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { if (action.queryType === MetricQueryType.QUERY) { this.pollSub = this.metricsMonitor .poll( - 1000, + 10000, () => { this.store.dispatch(action); }, @@ -287,10 +302,9 @@ export class MetricsChartComponent implements OnInit, OnDestroy { } } - private getCommitFn(action: MetricsAction) { - return () => { - this.setup(action); - this.store.dispatch(action); - }; + private commitAction(action: MetricsAction) { + this.setup(action); + this.store.dispatch(action); + this.commit = null; } } diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.html b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html index ced9750732..e1df11c40b 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.html +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.html @@ -1,16 +1,14 @@
-
- Start date must be before end date. -
-
Start
- arrow_forward + to
-
End
+
+
+ Start date must be before end date.
\ No newline at end of file diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss b/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss index e22db1ce7b..86c9aaf7a6 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss @@ -1,19 +1,17 @@ .start-end-date { - align-items: flex-end; + align-items: center; display: flex; max-width: 800px; &__selector { flex: auto; padding: 0 5px; } - &__header { - font-size: 14px; - font-weight: bold; - opacity: .6; - } &__arrow { - // Setting padding bottom to match the input fields - padding: 0 20px 1.25em; - color: rgba(0, 0, 0, 0.54); + padding: 0 10px; } } + +.invalid-message { + font-size: 12px; + margin-top: -13px; +} diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.theme.scss b/src/frontend/app/shared/components/start-end-date/start-end-date.component.theme.scss new file mode 100644 index 0000000000..c0e2e8d34e --- /dev/null +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.theme.scss @@ -0,0 +1,7 @@ +@import '~@angular/material/theming'; +@mixin start-end-theme($theme, $app-theme) { + $status: map-get($app-theme, status); + .invalid-message { + color: map-get($status, danger); + } +} diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts index b410c65bc2..4ad811cb9f 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -12,10 +12,23 @@ export class StartEndDateComponent { @Output() public startChange = new EventEmitter(); + @Output() + public isValid = new EventEmitter(); + + get valid() { + return this.validValue; + } + + set valid(valid: boolean) { + this.validValue = valid; + this.isValid.emit(this.validValue); + } + + public validValue = true; + private startValue: moment.Moment; private endValue: moment.Moment; - public invalid = false; private isDifferentDate(oldDate: moment.Moment, newDate: moment.Moment) { return !oldDate || !newDate || !oldDate.isSame(newDate); @@ -30,10 +43,10 @@ export class StartEndDateComponent { @Input() set start(start: moment.Moment) { - this.invalid = false; + this.valid = true; if (start && start.isValid()) { if (!this.isStartEndValid(start, this.end)) { - this.invalid = true; + this.valid = false; return; } if (this.isDifferentDate(this.startValue, start)) { @@ -50,10 +63,11 @@ export class StartEndDateComponent { @Input() set end(end: moment.Moment) { - this.invalid = false; + this.valid = true; if (end && end.isValid()) { if (!this.isStartEndValid(this.start, end)) { - this.invalid = true; + this.valid = false; + return; } if (this.isDifferentDate(this.endValue, end)) { const clone = moment(end); diff --git a/src/frontend/app/shared/shared.module.ts b/src/frontend/app/shared/shared.module.ts index 6bb54869af..6fbeef0d55 100644 --- a/src/frontend/app/shared/shared.module.ts +++ b/src/frontend/app/shared/shared.module.ts @@ -135,6 +135,7 @@ import { CapitalizeFirstPipe } from './pipes/capitalizeFirstLetter.pipe'; import { RoutingIndicatorComponent } from './components/routing-indicator/routing-indicator.component'; import { DateTimeComponent } from './components/date-time/date-time.component'; import { StartEndDateComponent } from './components/start-end-date/start-end-date.component'; +import { MomentModule } from 'ngx-moment'; @NgModule({ imports: [ @@ -146,6 +147,7 @@ import { StartEndDateComponent } from './components/start-end-date/start-end-dat CfAuthModule, CdkTableModule, NgxChartsModule, + MomentModule, ], declarations: [ LoadingPageComponent, diff --git a/src/frontend/sass/_all-theme.scss b/src/frontend/sass/_all-theme.scss index 791937dfda..c6748e9e63 100644 --- a/src/frontend/sass/_all-theme.scss +++ b/src/frontend/sass/_all-theme.scss @@ -33,6 +33,8 @@ @import '../app/shared/components/app-action-monitor-icon/app-action-monitor-icon.component.theme'; @import '../app/shared/components/upload-progress-indicator/upload-progress-indicator.component.theme'; @import '../app/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.theme'; +@import '../app/shared/components/start-end-date/start-end-date.component.theme'; +@import '../app/shared/components/metrics-chart/metrics-chart.component.theme.scss'; @import './components/mat-tabs.theme'; @import './components/text-status.theme'; @import './components/hyperlinks.theme'; @@ -105,6 +107,8 @@ $side-nav-light-active: #484848; @include page-404($theme, $app-theme); @include about-page-theme($theme, $app-theme); @include meta-card-component($theme, $app-theme); + @include start-end-theme($theme, $app-theme); + @include metrics-chart-theme($theme, $app-theme); } @function app-generate-nav-theme($theme, $nav-theme: null) { From 16efae5fc74295605ba5f3316839c19a058d704b Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 14 Sep 2018 14:59:13 +0100 Subject: [PATCH 015/114] Improve overlay for date-time selection --- package-lock.json | 17 ++++++--- package.json | 3 +- .../metrics-chart.component.html | 5 +-- .../metrics-chart.component.scss | 36 ++++++++++++++++--- .../metrics-chart.component.theme.scss | 4 +-- .../metrics-chart/metrics-chart.component.ts | 25 ++++++++++--- 6 files changed, 72 insertions(+), 18 deletions(-) diff --git a/package-lock.json b/package-lock.json index c7510351db..1b299a5c56 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8815,9 +8815,9 @@ "dev": true }, "moment": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.1.tgz", - "integrity": "sha512-shJkRTSebXvsVqk56I+lkb2latjBs8I+pc2TzWc545y2iFnSjm7Wg0QMh+ZWcdSLQyGEau5jI8ocnmkyTgr9YQ==" + "version": "2.22.2", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.22.2.tgz", + "integrity": "sha1-PCV/mDn8DpP/UxSWMiOeuQeD/2Y=" }, "move-concurrently": { "version": "1.0.1", @@ -8933,6 +8933,14 @@ "resolved": "https://registry.npmjs.org/ngrx-store-logger/-/ngrx-store-logger-0.2.1.tgz", "integrity": "sha1-IW6ZyVJ888UIYu4rJ/6T++jChnE=" }, + "ngx-moment": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/ngx-moment/-/ngx-moment-3.1.0.tgz", + "integrity": "sha512-liX6iTfOY0XyI3rUuWNgGpgxoeD+DFaAl7UJ/ejl9Ama5cXzw8L1Eft6UQLUo1d80kjNMc6AL+L19CPMvUQ/BA==", + "requires": { + "tslib": "^1.9.0" + } + }, "no-case": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz", @@ -13070,7 +13078,8 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true + "dev": true, + "optional": true }, "uglifyjs-webpack-plugin": { "version": "1.3.0", diff --git a/package.json b/package.json index 5a1dd053fa..34f62e0062 100644 --- a/package.json +++ b/package.json @@ -61,8 +61,9 @@ "core-js": "^2.5.7", "hammerjs": "^2.0.8", "js-yaml": "^3.11.0", - "moment": "^2.22.1", + "moment": "^2.22.2", "ngrx-store-logger": "^0.2.1", + "ngx-moment": "^3.1.0", "normalizr": "^3.2.3", "reselect": "^3.0.1", "rxjs": "^6.2.0", diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index b832cd1aae..e7d56a5f7d 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,11 +1,12 @@
-
+
- +
+

{{title}}

diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index 9f1fe13a80..e7148b70a6 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -1,4 +1,6 @@ .metrics-chart { + $animation-function: cubic-bezier(0, 0, .2, 1); + $animation-time: 250ms; height: 100%; width: 100%; &__header { @@ -21,23 +23,49 @@ width: 10%; } &__overlay { - background-color: rgba(0, 0, 0, .6); + background-color: rgba(0, 0, 0, 0); bottom: 0; - display: none; left: 0; + overflow: hidden; position: absolute; right: 0; top: 0; + transition: visibility 0s linear $animation-time, background-color $animation-time $animation-function; + visibility: hidden; z-index: 1; + + &-click { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + } &-show { - display: block; + background-color: rgba(0, 0, 0, .6); + transition-delay:0s; + visibility: visible; + .metrics-chart__overlay-inner { + transform: translateY(0); + } } + &-inner { - padding: 60px; + box-shadow: -4px 3px 41px -1px rgba(0, 0, 0, .36); + padding: 50px; + position: relative; + transform: translateY(-100%); + transition: transform $animation-time $animation-function; + z-index: 2; } + &-set { margin-right: 10px; } + + &-overflow { + overflow: hidden; + } } &__selected-range { align-items: center; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss index 4631e0e850..589c94951a 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss @@ -4,8 +4,8 @@ .metrics-chart { &__overlay { &-inner { - background-color: mat-color($primary, 500); - color: mat-contrast($primary, 500); + // background-color: mat-color($primary, 500); + background-color: mat-contrast($primary, 500); } } } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 76989f7b26..ff84d07952 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,8 +1,9 @@ -import { Component, Input, OnDestroy, OnInit } from '@angular/core'; +import { animate, style, transition, trigger } from '@angular/animations'; +import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Store } from '@ngrx/store'; import * as moment from 'moment'; import { Subscription } from 'rxjs'; -import { delay, map, tap, first, filter, debounceTime, takeWhile } from 'rxjs/operators'; +import { debounceTime, map, takeWhile, tap } from 'rxjs/operators'; import { FetchApplicationMetricsAction, MetricQueryConfig, MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; @@ -36,6 +37,7 @@ interface ITimeRange { label: string; type: RangeType; } + @Component({ selector: 'app-metrics-chart', templateUrl: './metrics-chart.component.html', @@ -49,17 +51,20 @@ export class MetricsChartComponent implements OnInit, OnDestroy { @Input() public title: string; + @ViewChild('overlayInner') + overlayInner: ElementRef; + public chartTypes = MetricsChartTypes; private startEnd: [moment.Moment, moment.Moment] = [null, null]; - private committedStartEnd: [moment.Moment, moment.Moment] = [null, null]; + public committedStartEnd: [moment.Moment, moment.Moment] = [null, null]; private pollSub: Subscription; private initSub: Subscription; - private commit: Function = null; + public commit: Function = null; public results$; @@ -96,7 +101,16 @@ export class MetricsChartComponent implements OnInit, OnDestroy { ]; public selectedTimeRangeValue: ITimeRange; - public showOverlay = false; + + set showOverlay(show: boolean) { + this.showOverlayValue = show; + } + + get showOverlay() { + return this.showOverlayValue; + } + + public showOverlayValue = false; private commitDate(date: moment.Moment, type: 'start' | 'end') { const index = type === 'start' ? this.startIndex : this.endIndex; @@ -137,6 +151,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { set selectedTimeRange(timeRange: ITimeRange) { this.commit = null; + this.committedStartEnd = [null, null]; this.selectedTimeRangeValue = timeRange; if (this.selectedTimeRangeValue.type === RangeType.ROLLING_WINDOW) { this.commitWindow(this.selectedTimeRangeValue); From b60d5b79b21dbea741e477aab7c19506967bf798 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 14 Sep 2018 15:54:08 +0100 Subject: [PATCH 016/114] Improve loading indicator on mertics chart --- .../metrics-chart.component.html | 29 ++++++++++--------- .../metrics-chart.component.scss | 4 ++- .../metrics-chart/metrics-chart.component.ts | 9 +++--- .../start-end-date.component.scss | 4 +-- .../app/store/effects/metrics.effects.ts | 4 +-- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index e7d56a5f7d..073a17d679 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,10 +1,13 @@
-
+
+

Choose time window

- - +
+ + +
@@ -18,22 +21,22 @@

{{title}}

-
-
{{ committedStartEnd[0] | amDateFormat:'LL' }}
+
+
{{ committedStartEnd[0] | amDateFormat:'Do MMM YYYY, HH:mm' }} +
to
-
{{ committedStartEnd[1] | amDateFormat:'LL' }}
+
{{ committedStartEnd[1] | amDateFormat:'Do MMM YYYY, HH:mm' }} +
+ No dates selected Edit
- + -
- +
+ {{ chartConfig.chartType }} chart type not found
diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index e7148b70a6..0a7ec9036d 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -33,7 +33,9 @@ transition: visibility 0s linear $animation-time, background-color $animation-time $animation-function; visibility: hidden; z-index: 1; - + &-buttons { + margin-top: 10px; + } &-click { bottom: 0; left: 0; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index ff84d07952..ee22c0a22b 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -51,8 +51,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy { @Input() public title: string; - @ViewChild('overlayInner') - overlayInner: ElementRef; + @ViewChild('noDatesSelected') + public noDatesSelected: ElementRef; public chartTypes = MetricsChartTypes; @@ -124,7 +124,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { const startUnix = start.unix(); const endUnix = end.unix(); const oldAction = this.metricsConfig.metricsAction; - this.metricsConfig.metricsAction = new FetchApplicationMetricsAction( + const action = new FetchApplicationMetricsAction( oldAction.guid, oldAction.cfGuid, new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { @@ -140,7 +140,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { this.startEnd[0], this.startEnd[1] ]; - this.commitAction(this.metricsConfig.metricsAction); + this.commitAction(action); }; } } @@ -151,7 +151,6 @@ export class MetricsChartComponent implements OnInit, OnDestroy { set selectedTimeRange(timeRange: ITimeRange) { this.commit = null; - this.committedStartEnd = [null, null]; this.selectedTimeRangeValue = timeRange; if (this.selectedTimeRangeValue.type === RangeType.ROLLING_WINDOW) { this.commitWindow(this.selectedTimeRangeValue); diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss b/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss index 86c9aaf7a6..b899d74b26 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss @@ -7,11 +7,11 @@ padding: 0 5px; } &__arrow { - padding: 0 10px; + padding: 0 5px; } } .invalid-message { font-size: 12px; - margin-top: -13px; + margin-top: -15px; } diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index ad0ffcafa7..d6a11eee83 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -25,7 +25,7 @@ export class MetricsEffect { mergeMap(action => { const fullUrl = this.buildFullUrl(action); const apiAction = { - guid: action.guid, + guid: action.metricId, entityKey: metricSchemaKey } as IRequestAction; this.store.dispatch(new StartRequestAction(apiAction)); @@ -46,7 +46,7 @@ export class MetricsEffect { entities: { [metricSchemaKey]: metricObject }, - result: [action.guid] + result: [action.metricId] }, apiAction ); From ecd631b8cd2bc1263708687a9fd006387b7881c6 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Mon, 17 Sep 2018 15:12:25 +0100 Subject: [PATCH 017/114] Fix chart data padding --- .../metrics-chart/metrics-chart.component.ts | 23 +++++---- .../metrics.component.manager.ts | 48 ++++++++++--------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index ee22c0a22b..bde99eecaa 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,4 +1,3 @@ -import { animate, style, transition, trigger } from '@angular/animations'; import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; import { Store } from '@ngrx/store'; import * as moment from 'moment'; @@ -44,6 +43,7 @@ interface ITimeRange { styleUrls: ['./metrics-chart.component.scss'] }) export class MetricsChartComponent implements OnInit, OnDestroy { + private committedAction: MetricsAction; @Input() public metricsConfig: MetricsConfig; @Input() @@ -130,7 +130,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { start: startUnix, end: endUnix, - step: Math.max((endUnix - startUnix) / 300, 0) + step: Math.max((endUnix - startUnix) / 200, 0) }), MetricQueryType.RANGE_QUERY ); @@ -163,15 +163,17 @@ export class MetricsChartComponent implements OnInit, OnDestroy { if (!window) { return; } + this.committedStartEnd = [null, null]; + this.startEnd = [null, null]; const oldAction = this.metricsConfig.metricsAction; - this.metricsConfig.metricsAction = new FetchApplicationMetricsAction( + const action = new FetchApplicationMetricsAction( oldAction.guid, oldAction.cfGuid, new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { window: window.value }) ); - this.commitAction(this.metricsConfig.metricsAction); + this.commitAction(action); } set start(start: moment.Moment) { @@ -202,15 +204,15 @@ export class MetricsChartComponent implements OnInit, OnDestroy { ]; newMetricsArray.sort(this.metricsConfig.sort); if ( - this.metricsConfig.metricsAction.query.params && - this.metricsConfig.metricsAction.query.params.start && - this.metricsConfig.metricsAction.query.params.end + this.committedAction.query.params && + this.committedAction.query.params.start && + this.committedAction.query.params.end ) { return MetricsChartManager.fillOutTimeOrderedChartSeries( newMetricsArray, - this.metricsConfig.metricsAction.query.params.start as number, - this.metricsConfig.metricsAction.query.params.end as number, - this.metricsConfig.metricsAction.query.params.step as number, + this.committedAction.query.params.start as number, + this.committedAction.query.params.end as number, + this.committedAction.query.params.step as number, this.metricsConfig, ); } @@ -317,6 +319,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { } private commitAction(action: MetricsAction) { + this.committedAction = action; this.setup(action); this.store.dispatch(action); this.commit = null; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts b/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts index 540f994e0f..c9e04b2e15 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics.component.manager.ts @@ -1,7 +1,10 @@ +import { ChartSeries, IMetricsData } from '../../../store/types/base-metric.types'; import { MetricsConfig } from './metrics-chart.component'; -import { IMetrics, ChartSeries, IMetricsData } from '../../../store/types/base-metric.types'; -import { MetricsChartHelpers } from './metrics.component.helpers'; +function dateLessThanUnix(date: Date, unix: number) { + const unixDate = date.getTime() / 1000; + return unixDate < unix; +} export class MetricsChartManager { static mapMatrix(metrics: IMetricsData, metricsConfig: MetricsConfig): ChartSeries[] { return metrics.result.map>( @@ -25,37 +28,38 @@ export class MetricsChartManager { }) ); } - static fillOutTimeOrderedChartSeries(timeOrdered: ChartSeries[], start: number, end: number, step: number, metricConfig: MetricsConfig) { + static fillOutTimeOrderedChartSeries( + timeOrdered: ChartSeries[], + start: number, + end: number, + step: number, + metricConfig: MetricsConfig + ): ChartSeries[] { if (!timeOrdered || !timeOrdered.length) { return timeOrdered; } return timeOrdered.reduce((allSeries, series) => { let pos = 0; - const data = series.series; + const newSeries = []; for (let t = start; t <= end; t += step) { - const current = data[pos]; - const name = metricConfig.mapSeriesItemName ? metricConfig.mapSeriesItemName(t) : t + ''; - if (!current) { - data.push({ + const current = series.series[pos]; + if (series.series.length > pos && dateLessThanUnix(series.series[pos].name as Date, t + step)) { + newSeries.push({ + name: current.name, + value: current.value + }); + pos++; + } else { + newSeries.push({ name: metricConfig.mapSeriesItemName ? metricConfig.mapSeriesItemName(t) : t + '', value: 0 }); - } else { - if (current.name < name) { - data.splice(pos, 0, { - name: metricConfig.mapSeriesItemName ? metricConfig.mapSeriesItemName(t) : t + '', - value: 0 - }); - } else { - data.splice(pos + 1, 0, { - name: metricConfig.mapSeriesItemName ? metricConfig.mapSeriesItemName(t) : t + '', - value: 0 - }); - pos += 2; - } } } - allSeries.push(series); + allSeries.push({ + name: series.name, + series: newSeries + }); return allSeries; }, []); } From 9e8027d2840fa5f61bb766407d0fe9639dd2da91 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Mon, 17 Sep 2018 16:09:29 +0100 Subject: [PATCH 018/114] Fixed overlay when navigating to page with chart --- .../components/metrics-chart/metrics-chart.component.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index bde99eecaa..d584812913 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -155,7 +155,9 @@ export class MetricsChartComponent implements OnInit, OnDestroy { if (this.selectedTimeRangeValue.type === RangeType.ROLLING_WINDOW) { this.commitWindow(this.selectedTimeRangeValue); } else if (this.selectedTimeRangeValue.type === RangeType.START_END) { - this.showOverlay = true; + if (!this.startEnd[0] || !this.startEnd[1]) { + this.showOverlay = true; + } } } @@ -233,6 +235,10 @@ export class MetricsChartComponent implements OnInit, OnDestroy { if (isDifferent) { this.start = start; this.end = end; + this.committedStartEnd = [ + start, + end + ]; } this.selectedTimeRange = this.times.find(time => time.type === RangeType.START_END); } else { @@ -257,6 +263,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { } ngOnInit() { + this.committedAction = this.metricsConfig.metricsAction; this.metricsMonitor = this.entityMonitorFactory.create( this.metricsConfig.metricsAction.metricId, metricSchemaKey, From f3379568e655c5e998b985d7a44aa292d5a082a1 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Mon, 17 Sep 2018 16:35:13 +0100 Subject: [PATCH 019/114] Updated package.lock --- package-lock.json | 1358 +++++++-------------------------------------- 1 file changed, 212 insertions(+), 1146 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1b299a5c56..b4ed2a1a04 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "stratos", - "version": "2.0.0", + "version": "2.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -491,6 +491,16 @@ "tslib": "^1.9.0" } }, + "@mrmlnc/readdir-enhanced": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", + "integrity": "sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g==", + "dev": true, + "requires": { + "call-me-maybe": "^1.0.1", + "glob-to-regexp": "^0.3.0" + } + }, "@ngrx/effects": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/@ngrx/effects/-/effects-6.0.1.tgz", @@ -523,6 +533,12 @@ "webpack-sources": "^1.1.0" } }, + "@nodelib/fs.stat": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-1.1.2.tgz", + "integrity": "sha512-yprFYuno9FtNsSHVlSWd+nRlmGoAbqbeCwOryP6sC/zoCjhpArcRMYp19EvpSUSizJAlsXEwJv+wcWS9XaXdMw==", + "dev": true + }, "@swimlane/ngx-charts": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/@swimlane/ngx-charts/-/ngx-charts-9.0.0.tgz", @@ -941,13 +957,6 @@ } } }, - "addressparser": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/addressparser/-/addressparser-1.0.1.tgz", - "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=", - "dev": true, - "optional": true - }, "adm-zip": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.11.tgz", @@ -1021,49 +1030,6 @@ "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", "dev": true }, - "amqplib": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/amqplib/-/amqplib-0.5.2.tgz", - "integrity": "sha512-l9mCs6LbydtHqRniRwYkKdqxVa6XMz3Vw1fh+2gJaaVgTM6Jk3o8RccAKWKtlhT1US5sWrFh+KKxsVUALURSIA==", - "dev": true, - "optional": true, - "requires": { - "bitsyntax": "~0.0.4", - "bluebird": "^3.4.6", - "buffer-more-ints": "0.0.2", - "readable-stream": "1.x >=1.1.9", - "safe-buffer": "^5.0.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true, - "optional": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true, - "optional": true - } - } - }, "angular2-virtual-scroll": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/angular2-virtual-scroll/-/angular2-virtual-scroll-0.3.2.tgz", @@ -1413,13 +1379,6 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, - "ast-types": { - "version": "0.11.5", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.11.5.tgz", - "integrity": "sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw==", - "dev": true, - "optional": true - }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -1518,28 +1477,6 @@ "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", "dev": true }, - "axios": { - "version": "0.15.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.15.3.tgz", - "integrity": "sha1-LJ1jiy4ZGgjqHWzJiOrda6W9wFM=", - "dev": true, - "optional": true, - "requires": { - "follow-redirects": "1.0.0" - }, - "dependencies": { - "follow-redirects": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.0.0.tgz", - "integrity": "sha1-jjQpjL0uF28lTv/sdaHHjMhJ/Tc=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.2.0" - } - } - } - }, "babel-code-frame": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", @@ -1808,57 +1745,6 @@ "integrity": "sha1-RqoXUftqL5PuXmibsQh9SxTGwgU=", "dev": true }, - "bitsyntax": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/bitsyntax/-/bitsyntax-0.0.4.tgz", - "integrity": "sha1-6xDMb4K4xJDj6FaY8H6D1G4MuoI=", - "dev": true, - "optional": true, - "requires": { - "buffer-more-ints": "0.0.2" - } - }, - "bl": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.1.2.tgz", - "integrity": "sha1-/cqHGplxOqANGeO7ukHER4emU5g=", - "dev": true, - "optional": true, - "requires": { - "readable-stream": "~2.0.5" - }, - "dependencies": { - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true, - "optional": true - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "~1.0.0", - "process-nextick-args": "~1.0.6", - "string_decoder": "~0.10.x", - "util-deprecate": "~1.0.1" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true, - "optional": true - } - } - }, "blob": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz", @@ -1950,15 +1836,6 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.x.x" - } - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -2105,6 +1982,22 @@ "isarray": "^1.0.0" } }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true + }, "buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -2117,6 +2010,12 @@ "integrity": "sha1-WWFrSYME1Var1GaWayLu2j7KX74=", "dev": true }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=", + "dev": true + }, "buffer-from": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", @@ -2129,43 +2028,12 @@ "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g==", "dev": true }, - "buffer-more-ints": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz", - "integrity": "sha1-JrOIXRD6E9t/wBquOquHAZngEkw=", - "dev": true - }, "buffer-xor": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "dev": true }, - "buildmail": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/buildmail/-/buildmail-4.0.1.tgz", - "integrity": "sha1-h393OLeHKYccmhBeO4N9K+EaenI=", - "dev": true, - "optional": true, - "requires": { - "addressparser": "1.0.1", - "libbase64": "0.1.0", - "libmime": "3.0.0", - "libqp": "1.1.0", - "nodemailer-fetch": "1.6.0", - "nodemailer-shared": "1.1.0", - "punycode": "1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true, - "optional": true - } - } - }, "builtin-modules": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", @@ -2228,6 +2096,12 @@ "unset-value": "^1.0.0" } }, + "call-me-maybe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", + "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=", + "dev": true + }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -2844,6 +2718,22 @@ "minimatch": "^3.0.4", "p-limit": "^1.0.0", "serialize-javascript": "^1.4.0" + }, + "dependencies": { + "globby": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", + "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "dir-glob": "^2.0.0", + "glob": "^7.1.2", + "ignore": "^3.3.5", + "pify": "^3.0.0", + "slash": "^1.0.0" + } + } } }, "core-js": { @@ -2929,16 +2819,6 @@ "which": "^1.2.9" } }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.x.x" - } - }, "crypto-browserify": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", @@ -3196,13 +3076,6 @@ "assert-plus": "^1.0.0" } }, - "data-uri-to-buffer": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz", - "integrity": "sha512-vKQ9DTQPN1FLYiiEEOQ6IBGFqvjCa5rSK3cWMy/Nespm5d/x3dGFT9UBZnkLxCwua/IXBi2TYnwTEpsOvhC4UQ==", - "dev": true, - "optional": true - }, "date-format": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/date-format/-/date-format-1.2.0.tgz", @@ -3351,27 +3224,6 @@ } } }, - "degenerator": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", - "integrity": "sha1-/PSQo37OJmRk2cxDGrmMWBnO0JU=", - "dev": true, - "optional": true, - "requires": { - "ast-types": "0.x.x", - "escodegen": "1.x.x", - "esprima": "3.x.x" - }, - "dependencies": { - "esprima": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true, - "optional": true - } - } - }, "del": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/del/-/del-3.0.0.tgz", @@ -3640,13 +3492,6 @@ "domelementtype": "1" } }, - "double-ended-queue": { - "version": "2.1.0-0", - "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", - "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=", - "dev": true, - "optional": true - }, "duplexify": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.0.tgz", @@ -3734,9 +3579,9 @@ } }, "engine.io": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.1.5.tgz", - "integrity": "sha512-D06ivJkYxyRrcEe0bTpNnBQNgP9d3xog+qZlLbui8EsMr/DouQpf5o9FzJnWYHEYE0YsFHllUv2R1dkgYZXHcA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-3.2.0.tgz", + "integrity": "sha512-mRbgmAtQ4GAlKwuPnnAvXXwdPhEx+jkc0OBCLrXuD/CRvwNK3AxRSnqK4FSqmAMRRHryVJP8TopOvmEaA64fKw==", "dev": true, "requires": { "accepts": "~1.3.4", @@ -3744,7 +3589,6 @@ "cookie": "0.3.1", "debug": "~3.1.0", "engine.io-parser": "~2.1.0", - "uws": "~9.14.0", "ws": "~3.3.1" }, "dependencies": { @@ -3760,9 +3604,9 @@ } }, "engine.io-client": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.1.6.tgz", - "integrity": "sha512-hnuHsFluXnsKOndS4Hv6SvUrgdYx1pk2NqfaDMW+GWdgfU3+/V25Cj7I8a0x92idSpa5PIhJRKxPvp9mnoLsfg==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-3.2.1.tgz", + "integrity": "sha512-y5AbkytWeM4jQr7m/koQLc5AxpRKC1hEVUb/s1FUAWEJq5AzJJ4NLvzuKPuxtDi5Mq755WuDvZ6Iv2rXj4PTzw==", "dev": true, "requires": { "component-emitter": "1.2.1", @@ -4571,6 +4415,20 @@ "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", "dev": true }, + "fast-glob": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.2.tgz", + "integrity": "sha512-TR6zxCKftDQnUAPvkrCWdBgDq/gbqx8A3ApnBrR5rMvpp6+KMJI0Igw7fkWPgeVK0uhRXTXdvO3O+YP0CaUX2g==", + "dev": true, + "requires": { + "@mrmlnc/readdir-enhanced": "^2.2.1", + "@nodelib/fs.stat": "^1.0.1", + "glob-parent": "^3.1.0", + "is-glob": "^4.0.0", + "merge2": "^1.2.1", + "micromatch": "^3.1.10" + } + }, "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", @@ -4628,13 +4486,6 @@ "schema-utils": "^0.4.5" } }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, "filename-regex": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", @@ -5629,46 +5480,6 @@ "rimraf": "2" } }, - "ftp": { - "version": "0.3.10", - "resolved": "https://registry.npmjs.org/ftp/-/ftp-0.3.10.tgz", - "integrity": "sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0=", - "dev": true, - "optional": true, - "requires": { - "readable-stream": "1.1.x", - "xregexp": "2.0.0" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true, - "optional": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true, - "optional": true - } - } - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", @@ -5734,21 +5545,6 @@ "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", "dev": true }, - "get-uri": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-2.0.2.tgz", - "integrity": "sha512-ZD325dMZOgerGqF/rF6vZXyFGTAay62svjQIT+X/oU2PtxYpFxvSkbsdi+oxIrsNxlZVd4y8wUDqkaExWTI/Cw==", - "dev": true, - "optional": true, - "requires": { - "data-uri-to-buffer": "1", - "debug": "2", - "extend": "3", - "file-uri-to-path": "1", - "ftp": "~0.3.10", - "readable-stream": "2" - } - }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -5853,6 +5649,12 @@ "unique-stream": "^2.0.2" } }, + "glob-to-regexp": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz", + "integrity": "sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=", + "dev": true + }, "glob-watcher": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.1.tgz", @@ -5910,13 +5712,14 @@ "dev": true }, "globby": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-7.1.1.tgz", - "integrity": "sha1-+yzP+UAfhgCUXfral0QMypcrhoA=", + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-8.0.1.tgz", + "integrity": "sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw==", "dev": true, "requires": { "array-union": "^1.0.1", "dir-glob": "^2.0.0", + "fast-glob": "^2.0.2", "glob": "^7.1.2", "ignore": "^3.3.5", "pify": "^3.0.0", @@ -6294,36 +6097,12 @@ "minimalistic-assert": "^1.0.1" } }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "optional": true, - "requires": { - "boom": "2.x.x", - "cryptiles": "2.x.x", - "hoek": "2.x.x", - "sntp": "1.x.x" - } - }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, - "hipchat-notifier": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/hipchat-notifier/-/hipchat-notifier-1.1.0.tgz", - "integrity": "sha1-ttJJdVQ3wZEII2d5nTupoPI7Ix4=", - "dev": true, - "optional": true, - "requires": { - "lodash": "^4.0.0", - "request": "^2.0.0" - } - }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", @@ -6335,12 +6114,6 @@ "minimalistic-crypto-utils": "^1.0.1" } }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true - }, "homedir-polyfill": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz", @@ -6517,31 +6290,10 @@ "requires-port": "^1.0.0" } }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "dev": true, - "requires": { - "agent-base": "4", - "debug": "3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "http-proxy-middleware": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", - "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", + "http-proxy-middleware": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-0.18.0.tgz", + "integrity": "sha512-Fs25KVMPAIIcgjMZkVHJoKg9VcXcC1C8yb9JUgeDvVXY0S/zgVIhMb+qVswDIgtJe2DfckMSY2d6TuTEutlk6Q==", "dev": true, "requires": { "http-proxy": "^1.16.2", @@ -6561,22 +6313,6 @@ "sshpk": "^1.7.0" } }, - "httpntlm": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.6.1.tgz", - "integrity": "sha1-rQFScUOi6Hc8+uapb1hla7UqNLI=", - "dev": true, - "requires": { - "httpreq": ">=0.4.22", - "underscore": "~1.7.0" - } - }, - "httpreq": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-0.4.24.tgz", - "integrity": "sha1-QzX/2CzZaWaKOUZckprGHWOTYn8=", - "dev": true - }, "https-browserify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", @@ -6697,13 +6433,6 @@ "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", "dev": true }, - "inflection": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", - "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=", - "dev": true, - "optional": true - }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -7182,10 +6911,13 @@ "dev": true }, "isbinaryfile": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.2.tgz", - "integrity": "sha1-Sj6XTsDLqQBNP8bN5yCeppNopiE=", - "dev": true + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-3.0.3.tgz", + "integrity": "sha512-8cJBL5tTd2OS0dM4jz07wQd5g0dCCqIhUxPIGtZfa5L6hWlvV5MHTITy/DBAsF+Oe2LS1X3krBUhNwaGUWpWxw==", + "dev": true, + "requires": { + "buffer-alloc": "^1.2.0" + } }, "isexe": { "version": "2.0.0", @@ -7649,14 +7381,14 @@ "dev": true }, "karma": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/karma/-/karma-2.0.2.tgz", - "integrity": "sha1-TS25QChQpmVR+nhLAWT7CCTtjEs=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/karma/-/karma-3.0.0.tgz", + "integrity": "sha512-ZTjyuDXVXhXsvJ1E4CnZzbCjSxD6sEdzEsFYogLuZM0yqvg/mgz+O+R1jb0J7uAQeuzdY8kJgx6hSNXLwFuHIQ==", "dev": true, "requires": { "bluebird": "^3.3.0", "body-parser": "^1.16.1", - "chokidar": "^1.4.1", + "chokidar": "^2.0.3", "colors": "^1.1.0", "combine-lists": "^1.0.0", "connect": "^3.6.0", @@ -7669,145 +7401,26 @@ "http-proxy": "^1.13.0", "isbinaryfile": "^3.0.0", "lodash": "^4.17.4", - "log4js": "^2.3.9", - "mime": "^1.3.4", + "log4js": "^3.0.0", + "mime": "^2.3.1", "minimatch": "^3.0.2", "optimist": "^0.6.1", "qjobs": "^1.1.4", "range-parser": "^1.2.0", "rimraf": "^2.6.0", "safe-buffer": "^5.0.1", - "socket.io": "2.0.4", + "socket.io": "2.1.1", "source-map": "^0.6.1", "tmp": "0.0.33", "useragent": "2.2.1" }, "dependencies": { - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "^1.0.1" - } - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "mime": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.3.1.tgz", + "integrity": "sha512-OEUllcVoydBHGN1z84yfQDimn58pZNNNXgZlHXSboxMlFvgI6MXSWpWKpFRra7H1HxpVhHTkrghfRW49k6yjeg==", "dev": true }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -8004,37 +7617,6 @@ "type-check": "~0.3.2" } }, - "libbase64": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-0.1.0.tgz", - "integrity": "sha1-YjUag5VjrF/1vSbxL2Dpgwu3UeY=", - "dev": true - }, - "libmime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-3.0.0.tgz", - "integrity": "sha1-UaGp50SOy9Ms2lRCFnW7IbwJPaY=", - "dev": true, - "requires": { - "iconv-lite": "0.4.15", - "libbase64": "0.1.0", - "libqp": "1.1.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.15", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.15.tgz", - "integrity": "sha1-/iZaIYrGpXz+hUkn6dBMGYJe3es=", - "dev": true - } - } - }, - "libqp": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-1.1.0.tgz", - "integrity": "sha1-9ebgatdLeU+1tbZpiL9yjvHe2+g=", - "dev": true - }, "license-webpack-plugin": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-1.4.0.tgz", @@ -8171,185 +7753,38 @@ } }, "log4js": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/log4js/-/log4js-2.7.0.tgz", - "integrity": "sha512-FyTwaPJfbfiK2AHc9ct/oFHNN4bJj0IQeqdO/LaDHhfjeBi8fnZU5rPcHOZhkYV0Aes31Ow+St1YTCluPtzs5g==", + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-3.0.5.tgz", + "integrity": "sha512-IX5c3G/7fuTtdr0JjOT2OIR12aTESVhsH6cEsijloYwKgcPRlO6DgOU72v0UFhWcoV1HN6+M3dwT89qVPLXm0w==", "dev": true, "requires": { - "amqplib": "^0.5.2", - "axios": "^0.15.3", - "circular-json": "^0.5.4", + "circular-json": "^0.5.5", "date-format": "^1.2.0", "debug": "^3.1.0", - "hipchat-notifier": "^1.1.0", - "loggly": "^1.1.0", - "mailgun-js": "^0.18.0", - "nodemailer": "^2.5.0", - "redis": "^2.7.1", - "semver": "^5.5.0", - "slack-node": "~0.2.0", + "rfdc": "^1.1.2", "streamroller": "0.7.0" }, "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "loggly": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/loggly/-/loggly-1.1.1.tgz", - "integrity": "sha1-Cg/B0/o6XsRP3HuJe+uipGlc6+4=", - "dev": true, - "optional": true, - "requires": { - "json-stringify-safe": "5.0.x", - "request": "2.75.x", - "timespan": "2.3.x" - }, - "dependencies": { - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true, - "optional": true - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true, - "optional": true - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - } - }, - "form-data": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.0.0.tgz", - "integrity": "sha1-bwrrrcxdoWwT4ezBETfYX5uIOyU=", - "dev": true, - "optional": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.5", - "mime-types": "^2.1.11" - } - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "optional": true, - "requires": { - "chalk": "^1.1.1", - "commander": "^2.9.0", - "is-my-json-valid": "^2.12.4", - "pinkie-promise": "^2.0.0" - } - }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "optional": true, - "requires": { - "assert-plus": "^0.2.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", - "dev": true, - "optional": true - }, - "qs": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.2.3.tgz", - "integrity": "sha1-HPyyXBCpsrSDBT/zn138kjOQjP4=", - "dev": true, - "optional": true + "circular-json": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.5.tgz", + "integrity": "sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==", + "dev": true }, - "request": { - "version": "2.75.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.75.0.tgz", - "integrity": "sha1-0rgmiihtoT6qXQGt9dGMyQ9lfZM=", + "debug": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, - "optional": true, "requires": { - "aws-sign2": "~0.6.0", - "aws4": "^1.2.1", - "bl": "~1.1.2", - "caseless": "~0.11.0", - "combined-stream": "~1.0.5", - "extend": "~3.0.0", - "forever-agent": "~0.6.1", - "form-data": "~2.0.0", - "har-validator": "~2.0.6", - "hawk": "~3.1.3", - "http-signature": "~1.1.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.7", - "node-uuid": "~1.4.7", - "oauth-sign": "~0.8.1", - "qs": "~6.2.0", - "stringstream": "~0.0.4", - "tough-cookie": "~2.3.0", - "tunnel-agent": "~0.4.1" + "ms": "^2.1.1" } }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true, - "optional": true - }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true, - "optional": true + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, @@ -8397,74 +7832,23 @@ "dev": true, "requires": { "currently-unhandled": "^0.4.1", - "signal-exit": "^3.0.0" - } - }, - "lower-case": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", - "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", - "dev": true - }, - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "dev": true, - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "mailcomposer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/mailcomposer/-/mailcomposer-4.0.1.tgz", - "integrity": "sha1-DhxEsqB890DuF9wUm6AJ8Zyt/rQ=", - "dev": true, - "optional": true, - "requires": { - "buildmail": "4.0.1", - "libmime": "3.0.0" - } - }, - "mailgun-js": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/mailgun-js/-/mailgun-js-0.18.0.tgz", - "integrity": "sha512-o0P6jjZlx5CQj12tvVgDTbgjTqVN0+5h6/6P1+3c6xmozVKBwniQ6Qt3MkCSF0+ueVTbobAfWyGpWRZMJu8t1g==", - "dev": true, - "optional": true, - "requires": { - "async": "~2.6.0", - "debug": "~3.1.0", - "form-data": "~2.3.0", - "inflection": "~1.12.0", - "is-stream": "^1.1.0", - "path-proxy": "~1.0.0", - "promisify-call": "^2.0.2", - "proxy-agent": "~3.0.0", - "tsscmp": "~1.0.0" - }, - "dependencies": { - "async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", - "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", - "dev": true, - "optional": true, - "requires": { - "lodash": "^4.17.10" - } - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - } + "signal-exit": "^3.0.0" + } + }, + "lower-case": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz", + "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", + "dev": true + }, + "lru-cache": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", + "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" } }, "make-dir": { @@ -8640,6 +8024,12 @@ "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=", "dev": true }, + "merge2": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.2.tgz", + "integrity": "sha512-bgM8twH86rWni21thii6WCMQMRMmwqqdW3sGWi9IipnVAszdLXRjwDwAnyrVXo6DuP3AjRMMttZKUB48QWIFGg==", + "dev": true + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -8906,13 +8296,6 @@ "integrity": "sha512-vdqTKI9GBIYcAEbFAcpKPErKINfPF5zIuz3/niBfq8WUZjpT2tytLlFVrBgWdOtqI4uaA/Rb6No0hux39XXDuw==", "dev": true }, - "netmask": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", - "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=", - "dev": true, - "optional": true - }, "next-tick": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", @@ -9089,91 +8472,6 @@ } } }, - "nodemailer": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-2.7.2.tgz", - "integrity": "sha1-8kLmSa7q45tsftdA73sGHEBNMPk=", - "dev": true, - "optional": true, - "requires": { - "libmime": "3.0.0", - "mailcomposer": "4.0.1", - "nodemailer-direct-transport": "3.3.2", - "nodemailer-shared": "1.1.0", - "nodemailer-smtp-pool": "2.8.2", - "nodemailer-smtp-transport": "2.7.2", - "socks": "1.1.9" - }, - "dependencies": { - "socks": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.9.tgz", - "integrity": "sha1-Yo1+TQSRJDVEWsC25Fk3bLPm1pE=", - "dev": true, - "optional": true, - "requires": { - "ip": "^1.1.2", - "smart-buffer": "^1.0.4" - } - } - } - }, - "nodemailer-direct-transport": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/nodemailer-direct-transport/-/nodemailer-direct-transport-3.3.2.tgz", - "integrity": "sha1-6W+vuQNYVglH5WkBfZfmBzilCoY=", - "dev": true, - "optional": true, - "requires": { - "nodemailer-shared": "1.1.0", - "smtp-connection": "2.12.0" - } - }, - "nodemailer-fetch": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/nodemailer-fetch/-/nodemailer-fetch-1.6.0.tgz", - "integrity": "sha1-ecSQihwPXzdbc/6IjamCj23JY6Q=", - "dev": true - }, - "nodemailer-shared": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/nodemailer-shared/-/nodemailer-shared-1.1.0.tgz", - "integrity": "sha1-z1mU4v0mjQD1zw+nZ6CBae2wfsA=", - "dev": true, - "requires": { - "nodemailer-fetch": "1.6.0" - } - }, - "nodemailer-smtp-pool": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/nodemailer-smtp-pool/-/nodemailer-smtp-pool-2.8.2.tgz", - "integrity": "sha1-LrlNbPhXgLG0clzoU7nL1ejajHI=", - "dev": true, - "optional": true, - "requires": { - "nodemailer-shared": "1.1.0", - "nodemailer-wellknown": "0.1.10", - "smtp-connection": "2.12.0" - } - }, - "nodemailer-smtp-transport": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/nodemailer-smtp-transport/-/nodemailer-smtp-transport-2.7.2.tgz", - "integrity": "sha1-A9ccdjFPFKx9vHvwM6am0W1n+3c=", - "dev": true, - "optional": true, - "requires": { - "nodemailer-shared": "1.1.0", - "nodemailer-wellknown": "0.1.10", - "smtp-connection": "2.12.0" - } - }, - "nodemailer-wellknown": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/nodemailer-wellknown/-/nodemailer-wellknown-0.1.10.tgz", - "integrity": "sha1-WG24EB2zDLRDjrVGc3pBqtDPE9U=", - "dev": true - }, "nopt": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", @@ -9620,49 +8918,6 @@ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", "dev": true }, - "pac-proxy-agent": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-2.0.2.tgz", - "integrity": "sha512-cDNAN1Ehjbf5EHkNY5qnRhGPUCp6SnpyVof5fRzN800QV1Y2OkzbH9rmjZkbBRa8igof903yOnjIl6z0SlAhxA==", - "dev": true, - "optional": true, - "requires": { - "agent-base": "^4.2.0", - "debug": "^3.1.0", - "get-uri": "^2.0.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "pac-resolver": "^3.0.0", - "raw-body": "^2.2.0", - "socks-proxy-agent": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "pac-resolver": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-3.0.0.tgz", - "integrity": "sha512-tcc38bsjuE3XZ5+4vP96OfhOugrX+JcnpUbhfuc4LuXBLQhoTthOstZeoQJBDnQUDYzYmdImKsbz0xSl1/9qeA==", - "dev": true, - "optional": true, - "requires": { - "co": "^4.6.0", - "degenerator": "^1.0.4", - "ip": "^1.1.5", - "netmask": "^1.0.6", - "thunkify": "^2.1.2" - } - }, "pako": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", @@ -9845,25 +9100,6 @@ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", "dev": true }, - "path-proxy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/path-proxy/-/path-proxy-1.0.0.tgz", - "integrity": "sha1-GOijaFn8nS8aU7SN7hOFQ8Ag3l4=", - "dev": true, - "optional": true, - "requires": { - "inflection": "~1.3.0" - }, - "dependencies": { - "inflection": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.3.8.tgz", - "integrity": "sha1-y9Fg2p91sUw8xjV41POWeEvzAU4=", - "dev": true, - "optional": true - } - } - }, "path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", @@ -10163,16 +9399,6 @@ "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "dev": true }, - "promisify-call": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/promisify-call/-/promisify-call-2.0.4.tgz", - "integrity": "sha1-1IwtRWUszM1SgB3ey9UzptS9X7o=", - "dev": true, - "optional": true, - "requires": { - "with-callback": "^1.0.2" - } - }, "protractor": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/protractor/-/protractor-5.4.0.tgz", @@ -10309,42 +9535,6 @@ "ipaddr.js": "1.8.0" } }, - "proxy-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-3.0.0.tgz", - "integrity": "sha512-g6n6vnk8fRf705ShN+FEXFG/SDJaW++lSs0d9KaJh4uBWW/wi7en4Cpo5VYQW3SZzAE121lhB/KLQrbURoubZw==", - "dev": true, - "optional": true, - "requires": { - "agent-base": "^4.2.0", - "debug": "^3.1.0", - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1", - "lru-cache": "^4.1.2", - "pac-proxy-agent": "^2.0.1", - "proxy-from-env": "^1.0.0", - "socks-proxy-agent": "^3.0.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - } - } - }, - "proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=", - "dev": true, - "optional": true - }, "prr": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", @@ -10682,32 +9872,6 @@ "strip-indent": "^1.0.1" } }, - "redis": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", - "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", - "dev": true, - "optional": true, - "requires": { - "double-ended-queue": "^2.1.0-0", - "redis-commands": "^1.2.0", - "redis-parser": "^2.6.0" - } - }, - "redis-commands": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.3.5.tgz", - "integrity": "sha512-foGF8u6MXGFF++1TZVC6icGXuMYPftKXt1FBT2vrfU9ZATNtZJ8duRC5d1lEfE8hyVe3jhelHGB91oB7I6qLsA==", - "dev": true, - "optional": true - }, - "redis-parser": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", - "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=", - "dev": true, - "optional": true - }, "reflect-metadata": { "version": "0.1.12", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz", @@ -11043,28 +10207,6 @@ "tough-cookie": ">=2.3.3" } }, - "requestretry": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/requestretry/-/requestretry-1.13.0.tgz", - "integrity": "sha512-Lmh9qMvnQXADGAQxsXHP4rbgO6pffCfuR8XUBdP9aitJcLQJxhp7YZK4xAVYXnPJ5E52mwrfiKQtKonPL8xsmg==", - "dev": true, - "optional": true, - "requires": { - "extend": "^3.0.0", - "lodash": "^4.15.0", - "request": "^2.74.0", - "when": "^3.7.7" - }, - "dependencies": { - "when": { - "version": "3.7.8", - "resolved": "https://registry.npmjs.org/when/-/when-3.7.8.tgz", - "integrity": "sha1-xxMLan6gRpPoQs3J56Hyqjmjn4I=", - "dev": true, - "optional": true - } - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -11183,6 +10325,12 @@ "integrity": "sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q=", "dev": true }, + "rfdc": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.1.2.tgz", + "integrity": "sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==", + "dev": true + }, "right-align": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", @@ -11692,16 +10840,6 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, - "slack-node": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/slack-node/-/slack-node-0.2.0.tgz", - "integrity": "sha1-3kuN3aqLeT9h29KTgQT9q/N9+jA=", - "dev": true, - "optional": true, - "requires": { - "requestretry": "^1.2.2" - } - }, "slash": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", @@ -11720,22 +10858,6 @@ "integrity": "sha1-VusCfWW00tzmyy4tMsTUr8nh1wc=", "dev": true }, - "smart-buffer": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-1.1.15.tgz", - "integrity": "sha1-fxFLW2X6s+KjWqd1uxLw0cZJvxY=", - "dev": true - }, - "smtp-connection": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/smtp-connection/-/smtp-connection-2.12.0.tgz", - "integrity": "sha1-1275EnyyPCJZ7bHoNJwujV4tdME=", - "dev": true, - "requires": { - "httpntlm": "1.6.1", - "nodemailer-shared": "1.1.0" - } - }, "snapdragon": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", @@ -11843,27 +10965,29 @@ } } }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "optional": true, - "requires": { - "hoek": "2.x.x" - } - }, "socket.io": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.0.4.tgz", - "integrity": "sha1-waRZDO/4fs8TxyZS8Eb3FrKeYBQ=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-2.1.1.tgz", + "integrity": "sha512-rORqq9c+7W0DAK3cleWNSyfv/qKXV99hV4tZe+gGLfBECw3XEhBy7x85F3wypA9688LKjtwO9pX9L33/xQI8yA==", "dev": true, "requires": { - "debug": "~2.6.6", - "engine.io": "~3.1.0", + "debug": "~3.1.0", + "engine.io": "~3.2.0", + "has-binary2": "~1.0.2", "socket.io-adapter": "~1.1.0", - "socket.io-client": "2.0.4", - "socket.io-parser": "~3.1.1" + "socket.io-client": "2.1.1", + "socket.io-parser": "~3.2.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } } }, "socket.io-adapter": { @@ -11873,35 +10997,46 @@ "dev": true }, "socket.io-client": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.0.4.tgz", - "integrity": "sha1-CRilUkBtxeVAs4Dc2Xr8SmQzL44=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-2.1.1.tgz", + "integrity": "sha512-jxnFyhAuFxYfjqIgduQlhzqTcOEQSn+OHKVfAxWaNWa7ecP7xSNk2Dx/3UEsDcY7NcFafxvNvKPmmO7HTwTxGQ==", "dev": true, "requires": { "backo2": "1.0.2", "base64-arraybuffer": "0.1.5", "component-bind": "1.0.0", "component-emitter": "1.2.1", - "debug": "~2.6.4", - "engine.io-client": "~3.1.0", + "debug": "~3.1.0", + "engine.io-client": "~3.2.0", + "has-binary2": "~1.0.2", "has-cors": "1.1.0", "indexof": "0.0.1", "object-component": "0.0.3", "parseqs": "0.0.5", "parseuri": "0.0.5", - "socket.io-parser": "~3.1.1", + "socket.io-parser": "~3.2.0", "to-array": "0.1.4" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + } } }, "socket.io-parser": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.1.3.tgz", - "integrity": "sha512-g0a2HPqLguqAczs3dMECuA1RgoGFPyvDqcbaDEdCWY9g59kdUAz3YRmaJBNKXflrHNwB7Q12Gkf/0CZXfdHR7g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-3.2.0.tgz", + "integrity": "sha512-FYiBx7rc/KORMJlgsXysflWx/RIvtqZbyGLlHZvjfmPTPeuD/I8MaW7cfFrj5tRltICJdgwflhfZ3NVVbVLFQA==", "dev": true, "requires": { "component-emitter": "1.2.1", "debug": "~3.1.0", - "has-binary2": "~1.0.2", "isarray": "2.0.1" }, "dependencies": { @@ -11957,26 +11092,6 @@ } } }, - "socks": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/socks/-/socks-1.1.10.tgz", - "integrity": "sha1-W4t/x8jzQcU+0FbpKbe/Tei6e1o=", - "dev": true, - "requires": { - "ip": "^1.1.4", - "smart-buffer": "^1.0.13" - } - }, - "socks-proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-3.0.1.tgz", - "integrity": "sha512-ZwEDymm204mTzvdqyUqOdovVr2YRd2NYskrYrF2LXyZ9qDiMAoFESGK8CRphiO7rtbo2Y757k2Nia3x2hGtalA==", - "dev": true, - "requires": { - "agent-base": "^4.1.0", - "socks": "^1.1.10" - } - }, "source-list-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz", @@ -12393,13 +11508,19 @@ }, "dependencies": { "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.5.tgz", + "integrity": "sha512-D61LaDQPQkxJ5AUM2mbSJRbPkNs/TmdmOeLAi1hgDkpDfIfetSrjmWhccwtuResSwMbACjx/xXQofvM9CE/aeg==", "dev": true, "requires": { - "ms": "2.0.0" + "ms": "^2.1.1" } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true } } }, @@ -12423,13 +11544,6 @@ "safe-buffer": "~5.1.0" } }, - "stringstream": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", - "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", - "dev": true, - "optional": true - }, "strip-ansi": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -12694,13 +11808,6 @@ "xtend": "~4.0.0" } }, - "thunkify": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/thunkify/-/thunkify-2.1.2.tgz", - "integrity": "sha1-+qDp0jDFGsyVyhOjYawFyn4EVT0=", - "dev": true, - "optional": true - }, "thunky": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.0.2.tgz", @@ -12722,13 +11829,6 @@ "setimmediate": "^1.0.4" } }, - "timespan": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/timespan/-/timespan-2.3.0.tgz", - "integrity": "sha1-SQLOBAvRPYRcj1myfp1ZutbzmSk=", - "dev": true, - "optional": true - }, "tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -12981,13 +12081,6 @@ } } }, - "tsscmp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.5.tgz", - "integrity": "sha1-fcSjOvcVgatDN9qR2FylQn69mpc=", - "dev": true, - "optional": true - }, "tsutils": { "version": "2.27.1", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.27.1.tgz", @@ -13133,12 +12226,6 @@ "integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo=", "dev": true }, - "underscore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", - "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=", - "dev": true - }, "undertaker": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.2.0.tgz", @@ -13461,13 +12548,6 @@ "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", "dev": true }, - "uws": { - "version": "9.14.0", - "resolved": "https://registry.npmjs.org/uws/-/uws-9.14.0.tgz", - "integrity": "sha512-HNMztPP5A1sKuVFmdZ6BPVpBQd5bUjNC8EFMFiICK+oho/OQsAJy5hnIx4btMHiOk8j04f/DbIlqnEZ9d72dqg==", - "dev": true, - "optional": true - }, "v8flags": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.0.tgz", @@ -13986,13 +13066,6 @@ "dev": true, "optional": true }, - "with-callback": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/with-callback/-/with-callback-1.0.2.tgz", - "integrity": "sha1-oJYpuakgAo1yFAT7Q1vc/1yRvCE=", - "dev": true, - "optional": true - }, "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", @@ -14079,13 +13152,6 @@ "integrity": "sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4=", "dev": true }, - "xregexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz", - "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=", - "dev": true, - "optional": true - }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", From 0aa3bd0572a89866cdbf19654fd5649372135531 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 18 Sep 2018 10:20:42 +0100 Subject: [PATCH 020/114] Made some minor responsive changes to time window selection --- .../components/metrics-chart/metrics-chart.component.scss | 6 +++++- .../components/metrics-chart/metrics-chart.component.ts | 2 +- .../components/start-end-date/start-end-date.component.scss | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index 0a7ec9036d..c253dab02f 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -1,3 +1,4 @@ +@import '../../../../sass/mixins'; .metrics-chart { $animation-function: cubic-bezier(0, 0, .2, 1); $animation-time: 250ms; @@ -74,7 +75,10 @@ display: flex; } &__selected-range-dates { - display: flex; + @include breakpoint(tablet) { + display: flex; + } + display: none; font-size: 14px; opacity: .6; padding-left: 10px; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index d584812913..909858e205 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -95,7 +95,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { type: RangeType.ROLLING_WINDOW }, { - label: 'Over a specific period', + label: 'Set time window', type: RangeType.START_END } ]; diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss b/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss index b899d74b26..42b14c9b33 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.scss @@ -1,9 +1,10 @@ .start-end-date { align-items: center; display: flex; - max-width: 800px; + flex-wrap: wrap; + width: 100%; &__selector { - flex: auto; + flex: none; padding: 0 5px; } &__arrow { From 33e9acb2c564440311dbc7c63763b34eb9f7bfbf Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 18 Sep 2018 10:43:44 +0100 Subject: [PATCH 021/114] Some cc fixes --- .../tabs/metrics-tab/metrics-tab.component.scss | 2 +- .../components/date-time/date-time.component.scss | 2 +- .../metrics-chart/metrics-chart.component.scss | 14 +++++++------- .../metrics-chart/metrics-chart.component.ts | 5 +---- src/frontend/sass/_all-theme.scss | 2 +- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.scss b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.scss index 58eef9915b..c149aedff7 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.scss +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.scss @@ -1,4 +1,4 @@ .metric-chart-wrapper { - margin-bottom: 20px; height: 500px; + margin-bottom: 20px; } diff --git a/src/frontend/app/shared/components/date-time/date-time.component.scss b/src/frontend/app/shared/components/date-time/date-time.component.scss index d597e5cb53..2c847d5272 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.scss +++ b/src/frontend/app/shared/components/date-time/date-time.component.scss @@ -5,4 +5,4 @@ .form-field { display: flex; height: 66px; -} \ No newline at end of file +} diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index c253dab02f..f5ac0a85a2 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -6,7 +6,7 @@ width: 100%; &__header { align-items: center; - display: flex; + display: flex; } &__outer { display: flex; @@ -43,10 +43,10 @@ position: absolute; right: 0; top: 0; - } + } &-show { background-color: rgba(0, 0, 0, .6); - transition-delay:0s; + transition-delay: 0s; visibility: visible; .metrics-chart__overlay-inner { transform: translateY(0); @@ -58,7 +58,7 @@ padding: 50px; position: relative; transform: translateY(-100%); - transition: transform $animation-time $animation-function; + transition: transform $animation-time $animation-function; z-index: 2; } @@ -75,13 +75,13 @@ display: flex; } &__selected-range-dates { - @include breakpoint(tablet) { - display: flex; - } display: none; font-size: 14px; opacity: .6; padding-left: 10px; + @include breakpoint(tablet) { + display: flex; + } } &__selected-range-date { font-weight: bold; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 909858e205..b0d8710418 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -235,10 +235,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { if (isDifferent) { this.start = start; this.end = end; - this.committedStartEnd = [ - start, - end - ]; + this.committedStartEnd = [start, end]; } this.selectedTimeRange = this.times.find(time => time.type === RangeType.START_END); } else { diff --git a/src/frontend/sass/_all-theme.scss b/src/frontend/sass/_all-theme.scss index c6748e9e63..2537cde377 100644 --- a/src/frontend/sass/_all-theme.scss +++ b/src/frontend/sass/_all-theme.scss @@ -34,7 +34,7 @@ @import '../app/shared/components/upload-progress-indicator/upload-progress-indicator.component.theme'; @import '../app/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.theme'; @import '../app/shared/components/start-end-date/start-end-date.component.theme'; -@import '../app/shared/components/metrics-chart/metrics-chart.component.theme.scss'; +@import '../app/shared/components/metrics-chart/metrics-chart.component.theme'; @import './components/mat-tabs.theme'; @import './components/text-status.theme'; @import './components/hyperlinks.theme'; From 3ddfc78c7078f410356331dde4fdfcd98565c9bc Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 18 Sep 2018 10:45:00 +0100 Subject: [PATCH 022/114] Another fix --- .../shared/components/metrics-chart/metrics-chart.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index b0d8710418..3ca620d951 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -129,7 +129,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy { oldAction.cfGuid, new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { start: startUnix, - end: endUnix, + end: end.unix(), step: Math.max((endUnix - startUnix) / 200, 0) }), MetricQueryType.RANGE_QUERY From 411efb7e9263ae9f491b8c395d1fe340fea11ab7 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 18 Sep 2018 14:31:57 +0100 Subject: [PATCH 023/114] Fixing tests --- .../application-instance-chart.component.spec.ts | 2 ++ .../tabs/metrics-tab/metrics-tab.component.spec.ts | 2 ++ .../date-time/date-time.component.spec.ts | 13 +++++++++++-- .../metrics-chart/metrics-chart.component.spec.ts | 8 +++++--- .../start-end-date/start-end-date.component.spec.ts | 9 +++++++-- src/frontend/app/shared/shared.module.ts | 4 +++- 6 files changed, 30 insertions(+), 8 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.spec.ts b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.spec.ts index 7a1117593a..1698e865c8 100644 --- a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.spec.ts +++ b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.spec.ts @@ -5,6 +5,7 @@ import { CoreModule } from '../../../../core/core.module'; import { SharedModule } from '../../../../shared/shared.module'; import { createBasicStoreModule } from '../../../../test-framework/store-test-helper'; import { ApplicationInstanceChartComponent } from './application-instance-chart.component'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; describe('ApplicationInstanceChartComponent', () => { let component: ApplicationInstanceChartComponent; @@ -17,6 +18,7 @@ describe('ApplicationInstanceChartComponent', () => { RouterTestingModule, CoreModule, SharedModule, + NoopAnimationsModule ] }) .compileComponents(); diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.spec.ts b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.spec.ts index db535d0cdb..6fa19e6b1f 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.spec.ts +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.spec.ts @@ -11,6 +11,7 @@ import { createBasicStoreModule } from '../../../../../../test-framework/store-t import { StoreModule } from '@ngrx/store'; import { ApplicationStateService } from '../../../../../../shared/components/application-state/application-state.service'; import { ApplicationEnvVarsHelper } from '../build-tab/application-env-vars.service'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; describe('MetricsTabComponent', () => { let component: MetricsTabComponent; @@ -24,6 +25,7 @@ describe('MetricsTabComponent', () => { createBasicStoreModule(), SharedModule, MDAppModule, + NoopAnimationsModule ], providers: [ ApplicationStateService, diff --git a/src/frontend/app/shared/components/date-time/date-time.component.spec.ts b/src/frontend/app/shared/components/date-time/date-time.component.spec.ts index ef377ce7fd..ebe5136e75 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.spec.ts +++ b/src/frontend/app/shared/components/date-time/date-time.component.spec.ts @@ -1,6 +1,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { DateTimeComponent } from './date-time.component'; +import { ReactiveFormsModule, FormsModule } from '@angular/forms'; +import { MDAppModule } from '../../../core/md.module'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; describe('DateTimeComponent', () => { let component: DateTimeComponent; @@ -8,9 +11,15 @@ describe('DateTimeComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ DateTimeComponent ] + declarations: [DateTimeComponent], + imports: [ + FormsModule, + ReactiveFormsModule, + MDAppModule, + NoopAnimationsModule + ] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.spec.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.spec.ts index b64e4a82ac..c345484e67 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.spec.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.spec.ts @@ -6,7 +6,8 @@ import { CoreModule } from '../../../core/core.module'; import { SharedModule } from '../../shared.module'; import { createBasicStoreModule } from '../../../test-framework/store-test-helper'; import { MetricsLineChartConfig } from './metrics-chart.types'; -import { FetchApplicationMetricsAction } from '../../../store/actions/metrics.actions'; +import { FetchApplicationMetricsAction, MetricQueryConfig } from '../../../store/actions/metrics.actions'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; describe('MetricsChartComponent', () => { let component: MetricsChartComponent; @@ -18,7 +19,8 @@ describe('MetricsChartComponent', () => { MDAppModule, CoreModule, SharedModule, - createBasicStoreModule() + createBasicStoreModule(), + NoopAnimationsModule ] }) .compileComponents(); @@ -34,7 +36,7 @@ describe('MetricsChartComponent', () => { metricsAction: new FetchApplicationMetricsAction( '1', '2', - 'test', + new MetricQueryConfig('test'), ), getSeriesName: () => 'test' }; diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.spec.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.spec.ts index b1af5f7b2c..71a5338101 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.spec.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.spec.ts @@ -1,6 +1,8 @@ +import { SharedModule } from './../../shared.module'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { StartEndDateComponent } from './start-end-date.component'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; describe('StartEndDateComponent', () => { let component: StartEndDateComponent; @@ -8,9 +10,12 @@ describe('StartEndDateComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ StartEndDateComponent ] + imports: [ + SharedModule, + NoopAnimationsModule + ] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/src/frontend/app/shared/shared.module.ts b/src/frontend/app/shared/shared.module.ts index 6fbeef0d55..97794f16d5 100644 --- a/src/frontend/app/shared/shared.module.ts +++ b/src/frontend/app/shared/shared.module.ts @@ -330,7 +330,9 @@ import { MomentModule } from 'ngx-moment'; BindAppsStepComponent, CapitalizeFirstPipe, CfEndpointsMissingComponent, - RoutingIndicatorComponent + RoutingIndicatorComponent, + DateTimeComponent, + StartEndDateComponent, ], entryComponents: [ AppEventDetailDialogComponentComponent, From 9f7f14ea8b82f4eb36a99e7854318a57ec0ecf41 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 18 Sep 2018 14:44:59 +0100 Subject: [PATCH 024/114] Linting --- src/frontend/app/store/types/base-metric.types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/app/store/types/base-metric.types.ts b/src/frontend/app/store/types/base-metric.types.ts index d4bb0d2c25..4e281eb18b 100644 --- a/src/frontend/app/store/types/base-metric.types.ts +++ b/src/frontend/app/store/types/base-metric.types.ts @@ -1,4 +1,4 @@ -import { MetricQueryConfig, MetricQueryType } from "../actions/metrics.actions"; +import { MetricQueryConfig, MetricQueryType } from '../actions/metrics.actions'; export enum MetricResultTypes { MATRIX = 'matrix', From 99d8443340f3c50e2377a46f61c7e1cd16fcbe64 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 25 Sep 2018 14:25:53 +0100 Subject: [PATCH 025/114] Add `Cell` column to Application Instances table - Requires metrics endpoint for apps cf - Includes lots of tidy up encountered on the way - Depends on entityService.. which requires IRequestAction.. so MetricsAction now imlements it - Fixed instance of IMetricsData typing --- src/frontend/app/core/entity-service.ts | 8 +-- .../application-tabs-base.component.ts | 9 +-- .../instances-tab/instances-tab.component.ts | 2 +- .../metrics/metrics/metrics.component.ts | 19 ++--- .../metrics/services/metrics-service.ts | 5 +- .../table-cell/table-cell.component.ts | 2 + .../app-instance/app-instance-types.ts | 14 ++++ .../cf-app-instances-config.service.ts | 71 +++++++++++++++++-- .../cf-app-instances-data-source.ts | 14 +--- .../table-cell-cf-cell.component.html | 1 + .../table-cell-cf-cell.component.scss | 1 + .../table-cell-cf-cell.component.spec.ts | 36 ++++++++++ .../table-cell-cf-cell.component.ts | 31 ++++++++ .../table-cell-usage.component.spec.ts | 9 ++- .../table-cell-usage.component.ts | 1 + .../app/shared/components/list/list.types.ts | 1 + .../metrics-chart/metrics-chart.component.ts | 8 ++- .../app/store/actions/metrics.actions.ts | 8 ++- .../app/store/types/app-metadata.types.ts | 2 + .../app/store/types/base-metric.types.ts | 2 +- src/frontend/app/store/types/request.types.ts | 1 - 21 files changed, 189 insertions(+), 56 deletions(-) create mode 100644 src/frontend/app/shared/components/list/list-types/app-instance/app-instance-types.ts create mode 100644 src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html create mode 100644 src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.scss create mode 100644 src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.spec.ts create mode 100644 src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts diff --git a/src/frontend/app/core/entity-service.ts b/src/frontend/app/core/entity-service.ts index d5a5777173..6e1d50bce9 100644 --- a/src/frontend/app/core/entity-service.ts +++ b/src/frontend/app/core/entity-service.ts @@ -1,22 +1,20 @@ import { compose, Store } from '@ngrx/store'; import { combineLatest, Observable } from 'rxjs'; import { filter, first, map, publishReplay, refCount, switchMap, tap, withLatestFrom } from 'rxjs/operators'; + import { EntityMonitor } from '../shared/monitors/entity-monitor'; import { ValidateEntitiesStart } from '../store/actions/request.actions'; import { AppState } from '../store/app-state'; import { - ActionState, RequestInfoState, RequestSectionKeys, TRequestTypeKeys, - UpdatingSection + UpdatingSection, } from '../store/reducers/api-request-reducer/types'; import { getEntityUpdateSections, getUpdateSectionById } from '../store/selectors/api.selectors'; -import { APIResource, EntityInfo } from '../store/types/api.types'; +import { EntityInfo } from '../store/types/api.types'; import { ICFAction, IRequestAction } from '../store/types/request.types'; -type PollUntil = (apiResource: APIResource, updatingState: ActionState) => boolean; - export function isEntityBlocked(entityRequestInfo: RequestInfoState) { if (!entityRequestInfo) { return false; diff --git a/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts b/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts index b044dd75d4..59db865e40 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts +++ b/src/frontend/app/features/applications/application/application-tabs-base/application-tabs-base.component.ts @@ -43,13 +43,6 @@ const appRestartConfirmation = new ConfirmationDialogConfig( 'Are you sure you want to restart this Application?', 'Restart' ); -// App delete will have a richer delete experience -const appDeleteConfirmation = new ConfirmationDialogConfig( - 'Delete Application', - 'Are you sure you want to delete this Application?', - 'Delete', - true -); @Component({ selector: 'app-application-tabs-base', @@ -232,7 +225,7 @@ export class ApplicationTabsBaseComponent implements OnInit, OnDestroy { filter(({ resource }) => { return resource.entity.state === stateString; }), - ); + ); } startApplication() { diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/instances-tab/instances-tab.component.ts b/src/frontend/app/features/applications/application/application-tabs-base/tabs/instances-tab/instances-tab.component.ts index fec51df0e9..e3e020430b 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/instances-tab/instances-tab.component.ts +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/instances-tab/instances-tab.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, HostBinding } from '@angular/core'; +import { Component } from '@angular/core'; import { CfAppInstancesConfigService, diff --git a/src/frontend/app/features/metrics/metrics/metrics.component.ts b/src/frontend/app/features/metrics/metrics/metrics.component.ts index b85bc1310b..a6cab26dcf 100644 --- a/src/frontend/app/features/metrics/metrics/metrics.component.ts +++ b/src/frontend/app/features/metrics/metrics/metrics.component.ts @@ -1,12 +1,13 @@ -import { Component, OnInit } from '@angular/core'; -import { MetricsService, MetricsEndpointProvider } from '../services/metrics-service'; - -import { getNameForEndpointType } from '../../endpoints/endpoint-helpers'; -import { Observable } from 'rxjs'; +import { Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { getIdFromRoute } from '../../cloud-foundry/cf.helpers'; -import { map, first } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { first, map } from 'rxjs/operators'; + import { IHeaderBreadcrumb } from '../../../shared/components/page-header/page-header.types'; +import { getIdFromRoute } from '../../cloud-foundry/cf.helpers'; +import { getNameForEndpointType } from '../../endpoints/endpoint-helpers'; +import { MetricsEndpointProvider, MetricsService } from '../services/metrics-service'; + @Component({ selector: 'app-metrics', templateUrl: './metrics.component.html', @@ -19,7 +20,7 @@ export class MetricsComponent { public metricsEndpoint$: Observable; public breadcrumbs$: Observable; - constructor(private activatedRoute: ActivatedRoute, private metricsService: MetricsService) { + constructor(activatedRoute: ActivatedRoute, metricsService: MetricsService) { const metricsGuid = getIdFromRoute(activatedRoute, 'metricsId'); @@ -28,7 +29,7 @@ export class MetricsComponent { ); this.breadcrumbs$ = this.metricsEndpoint$.pipe( - map(endpoint => ([ + map(() => ([ { breadcrumbs: [ { diff --git a/src/frontend/app/features/metrics/services/metrics-service.ts b/src/frontend/app/features/metrics/services/metrics-service.ts index 6e2a2a5fd3..4228150c16 100644 --- a/src/frontend/app/features/metrics/services/metrics-service.ts +++ b/src/frontend/app/features/metrics/services/metrics-service.ts @@ -1,16 +1,14 @@ import { Injectable } from '@angular/core'; -import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { map, publishReplay, refCount } from 'rxjs/operators'; +import { CloudFoundryService } from '../../../shared/data-services/cloud-foundry.service'; import { PaginationMonitor } from '../../../shared/monitors/pagination-monitor'; import { PaginationMonitorFactory } from '../../../shared/monitors/pagination-monitor.factory'; -import { AppState } from '../../../store/app-state'; import { endpointSchemaKey, entityFactory } from '../../../store/helpers/entity-factory'; import { APIResource, EntityInfo } from '../../../store/types/api.types'; import { EndpointModel } from '../../../store/types/endpoint.types'; import { getFullEndpointApiUrl } from '../../endpoints/endpoint-helpers'; -import { CloudFoundryService } from '../../../shared/data-services/cloud-foundry.service'; export interface MetricsEndpointProvider { provider: EndpointModel; @@ -26,7 +24,6 @@ export class MetricsService { haveNoConnectedMetricsEndpoints$: Observable; constructor( - private store: Store, private paginationMonitorFactory: PaginationMonitorFactory ) { this.endpointsMonitor = this.paginationMonitorFactory.create( diff --git a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts index 2b32b1c360..6c65dfbf8f 100644 --- a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts +++ b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts @@ -103,6 +103,7 @@ import { TableCellSelectComponent } from '../table-cell-select/table-cell-select import { TableHeaderSelectComponent } from '../table-header-select/table-header-select.component'; import { ICellDefinition } from '../table.types'; import { TableCellSpaceNameComponent } from '../../list-types/cf-spaces-service-instances/table-cell-space-name/table-cell-space-name.component'; +import { TableCellCfCellComponent } from '../../list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component'; /* tslint:enable:max-line-length */ @@ -122,6 +123,7 @@ export const listTableCells = [ TableCellEndpointNameComponent, TableCellAppStatusComponent, TableCellUsageComponent, + TableCellCfCellComponent, TableCellRouteComponent, TableCellTCPRouteComponent, TableCellAppInstancesComponent, diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/app-instance-types.ts b/src/frontend/app/shared/components/list/list-types/app-instance/app-instance-types.ts new file mode 100644 index 0000000000..d99a54adc7 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/app-instance/app-instance-types.ts @@ -0,0 +1,14 @@ +import { AppStat } from '../../../../../store/types/app-metadata.types'; + +export interface ListAppInstanceUsage { + mem: number; + disk: number; + cpu: number; + hasStats: boolean; +} + +export interface ListAppInstance { + index: number; + value: AppStat; + usage: ListAppInstanceUsage; +} 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 33f11e6576..2e99a67ff1 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 @@ -1,27 +1,39 @@ - -import { of as observableOf, Observable } from 'rxjs'; import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { Store } from '@ngrx/store'; -import { map, switchMap, combineLatest } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { combineLatest, filter, map, switchMap } from 'rxjs/operators'; +import { EndpointsService } from '../../../../../core/endpoints.service'; +import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; import { UtilsService } from '../../../../../core/utils.service'; import { ApplicationService } from '../../../../../features/applications/application.service'; import { DeleteApplicationInstance } from '../../../../../store/actions/application.actions'; +import { + FetchApplicationMetricsAction, + MetricQueryConfig, + MetricQueryType, +} from '../../../../../store/actions/metrics.actions'; import { AppState } from '../../../../../store/app-state'; +import { entityFactory, metricSchemaKey } from '../../../../../store/helpers/entity-factory'; +import { IMetricMatrixResult, IMetrics } from '../../../../../store/types/base-metric.types'; +import { IMetricApplication } from '../../../../../store/types/metric.types'; import { ConfirmationDialogConfig } from '../../../confirmation-dialog.config'; import { ConfirmationDialogService } from '../../../confirmation-dialog.service'; +import { getIntegerFieldSortFunction } from '../../data-sources-controllers/local-filtering-sorting'; import { ITableColumn } from '../../list-table/table.types'; import { IListAction, IListConfig, ListViewTypes } from '../../list.component.types'; -import { CfAppInstancesDataSource, ListAppInstance } from './cf-app-instances-data-source'; +import { ListAppInstance } from './app-instance-types'; +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'; -import { getIntegerFieldSortFunction } from '../../data-sources-controllers/local-filtering-sorting'; @Injectable() export class CfAppInstancesConfigService implements IListConfig { instancesSource: CfAppInstancesDataSource; + metricResults$: Observable[]>; columns: Array> = [ { columnId: 'index', @@ -100,6 +112,16 @@ export class CfAppInstancesConfigService implements IListConfig }, cellFlex: '5' } ]; + cfCellColumn: ITableColumn = { + columnId: 'cell', + headerCell: () => 'Cell', + cellConfig: { + metricResults$: null + }, + cellComponent: TableCellCfCellComponent, + cellFlex: '1' + }; + viewType = ListViewTypes.TABLE_ONLY; enableTextFilter = true; text = { @@ -107,6 +129,7 @@ export class CfAppInstancesConfigService implements IListConfig filter: 'Search by state', noEntries: 'There are no application instances' }; + private initialised$: Observable; private listActionTerminate: IListAction = { action: (item) => { @@ -161,7 +184,22 @@ export class CfAppInstancesConfigService implements IListConfig private utilsService: UtilsService, private router: Router, private confirmDialog: ConfirmationDialogService, + private endpointsService: EndpointsService, + entityServiceFactory: EntityServiceFactory ) { + + this.initialised$ = this.endpointsService.hasMetrics(appService.cfGuid).pipe( + map(hasMetrics => { + if (hasMetrics) { + this.columns.splice(1, 0, this.cfCellColumn); + this.cfCellColumn.cellConfig = { + metricResults$: this.createMetricsResults(entityServiceFactory) + }; + } + return true; + }) + ); + this.instancesSource = new CfAppInstancesDataSource( this.store, this.appService.cfGuid, @@ -176,5 +214,28 @@ export class CfAppInstancesConfigService implements IListConfig getColumns = () => this.columns; getDataSource = () => this.instancesSource; getMultiFiltersConfigs = () => []; + getInitialised = () => this.initialised$; + + private createMetricsResults(entityServiceFactory: EntityServiceFactory) { + const metricsAction = new FetchApplicationMetricsAction( + this.appService.appGuid, + this.appService.cfGuid, + new MetricQueryConfig('firehose_container_metric_cpu_percentage', { + window: '5m' + }), + MetricQueryType.QUERY + ); + return entityServiceFactory.create>>( + metricSchemaKey, + entityFactory(metricSchemaKey), + metricsAction.metricId, + metricsAction, + false + ).entityObs$.pipe( + map(entityInfo => entityInfo.entity), + filter(metrics => !!metrics && !!metrics.data && !!metrics.data.result), + map(metrics => metrics.data.result) + ); + } } diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/cf-app-instances-data-source.ts b/src/frontend/app/shared/components/list/list-types/app-instance/cf-app-instances-data-source.ts index 13322a94f7..9036d711db 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/cf-app-instances-data-source.ts +++ b/src/frontend/app/shared/components/list/list-types/app-instance/cf-app-instances-data-source.ts @@ -10,19 +10,7 @@ import { APIResource } from '../../../../../store/types/api.types'; import { AppStat } from '../../../../../store/types/app-metadata.types'; import { ListDataSource } from '../../data-sources-controllers/list-data-source'; import { IListConfig } from '../../list.component.types'; - -export interface ListAppInstanceUsage { - mem: number; - disk: number; - cpu: number; - hasStats: boolean; -} - -export interface ListAppInstance { - index: number; - value: AppStat; - usage: ListAppInstanceUsage; -} +import { ListAppInstance, ListAppInstanceUsage } from './app-instance-types'; export class CfAppInstancesDataSource extends ListDataSource> { diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html new file mode 100644 index 0000000000..7b04287a78 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html @@ -0,0 +1 @@ +{{ cell$ | async }} diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.scss b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.scss new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.scss @@ -0,0 +1 @@ + diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.spec.ts b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.spec.ts new file mode 100644 index 0000000000..a027863492 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.spec.ts @@ -0,0 +1,36 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CoreModule } from '../../../../../../core/core.module'; +import { UtilsService } from '../../../../../../core/utils.service'; +import { EntityInfo } from '../../../../../../store/types/api.types'; +import { TableCellCfCellComponent } from './table-cell-cf-cell.component'; + +describe('TableCellCfCellComponent', () => { + let component: TableCellCfCellComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + TableCellCfCellComponent, + ], + imports: [ + CoreModule, + ], + providers: [ + UtilsService, + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TableCellCfCellComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts new file mode 100644 index 0000000000..6beb312690 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts @@ -0,0 +1,31 @@ +import { Component, Input } from '@angular/core'; +import { Observable } from 'rxjs'; +import { filter, map } from 'rxjs/operators'; + +import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; +import { IMetricApplication } from '../../../../../../store/types/metric.types'; +import { TableCellCustom } from '../../../list.types'; +import { ListAppInstance } from '../app-instance-types'; + +@Component({ + selector: 'app-table-cell-cf-cell-usage', + templateUrl: './table-cell-cf-cell.component.html', + styleUrls: ['./table-cell-cf-cell.component.scss'] +}) +export class TableCellCfCellComponent extends TableCellCustom { + + cell$: Observable; + + @Input('config') + set config(config: { metricResults$: Observable[]> }) { + if (!config) { + return; + } + this.cell$ = config.metricResults$.pipe( + filter(metricResults => !!metricResults[this.row.index]), + map(metricResults => metricResults[this.row.index].metric.bosh_job_id) + ); + } + +} + diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-usage/table-cell-usage.component.spec.ts b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-usage/table-cell-usage.component.spec.ts index 538e533ab8..75b2936a9b 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-usage/table-cell-usage.component.spec.ts +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-usage/table-cell-usage.component.spec.ts @@ -1,12 +1,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { TableCellUsageComponent } from './table-cell-usage.component'; +import { CoreModule } from '../../../../../../core/core.module'; +import { UtilsService } from '../../../../../../core/utils.service'; import { EntityInfo } from '../../../../../../store/types/api.types'; -import { UsageGaugeComponent } from '../../../../usage-gauge/usage-gauge.component'; import { PercentagePipe } from '../../../../../pipes/percentage.pipe'; -import { UtilsService } from '../../../../../../core/utils.service'; -import { CoreModule } from '../../../../../../core/core.module'; -import { SharedModule } from '../../../../../shared.module'; +import { UsageGaugeComponent } from '../../../../usage-gauge/usage-gauge.component'; +import { TableCellUsageComponent } from './table-cell-usage.component'; describe('TableCellUsageComponent', () => { let component: TableCellUsageComponent; diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-usage/table-cell-usage.component.ts b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-usage/table-cell-usage.component.ts index 8db854cb45..30b8692cc8 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-usage/table-cell-usage.component.ts +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-usage/table-cell-usage.component.ts @@ -1,4 +1,5 @@ import { Component, OnInit } from '@angular/core'; + import { TableCellCustom } from '../../../list.types'; @Component({ diff --git a/src/frontend/app/shared/components/list/list.types.ts b/src/frontend/app/shared/components/list/list.types.ts index 2c19309acd..ceda607fbf 100644 --- a/src/frontend/app/shared/components/list/list.types.ts +++ b/src/frontend/app/shared/components/list/list.types.ts @@ -1,6 +1,7 @@ import { IListDataSource, RowState } from './data-sources-controllers/list-data-source-types'; import { Observable } from 'rxjs'; import { Component } from '@angular/core'; +import { AppStat } from '../../../store/types/app-metadata.types'; export abstract class TableCellCustom { dataSource: IListDataSource; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 3ca620d951..89fab4e79b 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -3,7 +3,13 @@ import { Store } from '@ngrx/store'; import * as moment from 'moment'; import { Subscription } from 'rxjs'; import { debounceTime, map, takeWhile, tap } from 'rxjs/operators'; -import { FetchApplicationMetricsAction, MetricQueryConfig, MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; + +import { + FetchApplicationMetricsAction, + MetricQueryConfig, + MetricQueryType, + MetricsAction, +} from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { EntityMonitor } from '../../monitors/entity-monitor'; diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index d5bde7ca75..53f3b01bc9 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -1,4 +1,5 @@ -import { Action } from '@ngrx/store'; +import { metricSchemaKey } from '../helpers/entity-factory'; +import { IRequestAction } from '../types/request.types'; import { environment } from './../../../environments/environment.prod'; export const METRICS_START = '[Metrics] Fetch Start'; @@ -27,7 +28,7 @@ export class MetricQueryConfig { const { window = '', ...params - } = this.params; + } = this.params || {}; const windowString = window ? `{}[${window}]` : ''; const paramString = Object.keys(params).reduce((accum, key) => { return accum + `&${key}=${params[key]}`; @@ -36,10 +37,11 @@ export class MetricQueryConfig { } } -export abstract class MetricsAction implements Action { +export abstract class MetricsAction implements IRequestAction { constructor(public guid: string, public query: MetricQueryConfig, public queryType: MetricQueryType = MetricQueryType.QUERY) { this.metricId = MetricsAction.buildMetricKey(guid, query); } + entityKey = metricSchemaKey; type = METRICS_START; url: string; cfGuid: string; diff --git a/src/frontend/app/store/types/app-metadata.types.ts b/src/frontend/app/store/types/app-metadata.types.ts index 85204ec8d7..2750b773d5 100644 --- a/src/frontend/app/store/types/app-metadata.types.ts +++ b/src/frontend/app/store/types/app-metadata.types.ts @@ -12,6 +12,8 @@ export interface AppStats { } export interface AppStat { + cfGuid: string; + guid: string; state: string; stats: AppInstanceStats; } diff --git a/src/frontend/app/store/types/base-metric.types.ts b/src/frontend/app/store/types/base-metric.types.ts index 4e281eb18b..c9474e7c5a 100644 --- a/src/frontend/app/store/types/base-metric.types.ts +++ b/src/frontend/app/store/types/base-metric.types.ts @@ -18,7 +18,7 @@ export interface IMetricsData { export interface IMetrics { query: MetricQueryConfig; queryType: MetricQueryType; - data: IMetricsData; + data: IMetricsData; } interface IVectorResult { diff --git a/src/frontend/app/store/types/request.types.ts b/src/frontend/app/store/types/request.types.ts index b355dd7d09..12b1602668 100644 --- a/src/frontend/app/store/types/request.types.ts +++ b/src/frontend/app/store/types/request.types.ts @@ -41,7 +41,6 @@ export interface IRequestAction extends RequestAction { /** * For delete requests we clear the pagination sections (include all pages) of all list matching the same entity type. In some cases, * like local lists, we want to immediately remove that entry instead of clearing the table and refetching all data. This flag allows that - * */ removeEntityOnDelete?: boolean; } From 0db1280bb0c9f38e735f30e29476b8425eef2b0c Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 25 Sep 2018 15:46:13 +0100 Subject: [PATCH 026/114] WIP --- src/frontend/app/core/endpoints.service.ts | 8 +-- .../cloud-foundry-tabs-base.component.ts | 14 +++- .../cloud-foundry/cloud-foundry.module.ts | 2 + .../cloud-foundry/cloud-foundry.routing.ts | 5 ++ .../cloud-foundry-cells.component.html | 4 ++ .../cloud-foundry-cells.component.scss | 0 .../cloud-foundry-cells.component.spec.ts | 29 ++++++++ .../cloud-foundry-cells.component.ts | 39 +++++++++++ .../cloud-foundry-feature-flags.component.ts | 16 +---- .../cf-cells/cf-cells-data-source.ts | 26 +++++++ .../cf-cells/cf-cells-list-config.service.ts | 70 +++++++++++++++++++ .../app/store/actions/metrics.actions.ts | 6 +- 12 files changed, 197 insertions(+), 22 deletions(-) create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.scss create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.spec.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts create mode 100644 src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts create mode 100644 src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts diff --git a/src/frontend/app/core/endpoints.service.ts b/src/frontend/app/core/endpoints.service.ts index 7f202a7f99..95c4c7ac3d 100644 --- a/src/frontend/app/core/endpoints.service.ts +++ b/src/frontend/app/core/endpoints.service.ts @@ -1,7 +1,7 @@ -import {combineLatest as observableCombineLatest, Observable } from 'rxjs'; +import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; -import {withLatestFrom, skipWhile, map, first, filter } from 'rxjs/operators'; +import { withLatestFrom, skipWhile, map, first, filter, tap } from 'rxjs/operators'; import { endpointEntitiesSelector, endpointStatusSelector, @@ -47,7 +47,7 @@ export class EndpointsService implements CanActivate { this.haveRegistered$, this.haveConnected$, this.userService.isAdmin$, - ), + ), map(([state, haveRegistered, haveConnected, isAdmin]: [[AuthState, EndpointState], boolean, boolean, boolean]) => { const [authState] = state; if (authState.sessionData.valid) { @@ -68,7 +68,7 @@ export class EndpointsService implements CanActivate { } return false; - }), ); + })); } hasMetrics(endpointId: string) { diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts index fc25ce1a67..1ec0ed249b 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts @@ -10,6 +10,7 @@ import { ISubHeaderTabs } from '../../../shared/components/page-subheader/page-s import { canUpdateOrgSpaceRoles } from '../cf.helpers'; import { CloudFoundryEndpointService } from '../services/cloud-foundry-endpoint.service'; import { AppState } from './../../../store/app-state'; +import { EndpointsService } from '../../../core/endpoints.service'; @Component({ selector: 'app-cloud-foundry-tabs-base', @@ -19,6 +20,7 @@ import { AppState } from './../../../store/app-state'; export class CloudFoundryTabsBaseComponent implements OnInit { static firehose = 'firehose'; static users = 'users'; + static cells = 'cells'; public tabLinks: ISubHeaderTabs[]; @@ -32,7 +34,8 @@ export class CloudFoundryTabsBaseComponent implements OnInit { constructor( public cfEndpointService: CloudFoundryEndpointService, - private currentUserPermissionsService: CurrentUserPermissionsService + private currentUserPermissionsService: CurrentUserPermissionsService, + endpointsService: EndpointsService ) { const firehoseHidden$ = this.currentUserPermissionsService .can(CurrentUserPermissions.FIREHOSE_VIEW, this.cfEndpointService.cfGuid) @@ -43,6 +46,10 @@ export class CloudFoundryTabsBaseComponent implements OnInit { map(users => !users) ); + const cellsHidden$ = endpointsService.hasMetrics(cfEndpointService.cfGuid).pipe( + map(hasMetrics => !hasMetrics) + ); + this.tabLinks = [ { link: 'summary', label: 'Summary' }, { link: 'organizations', label: 'Organizations' }, @@ -56,6 +63,11 @@ export class CloudFoundryTabsBaseComponent implements OnInit { label: 'Firehose', hidden: firehoseHidden$ }, + { + link: CloudFoundryTabsBaseComponent.cells, + label: 'Cells', + hidden: cellsHidden$ + }, { link: 'feature-flags', label: 'Feature Flags' }, { link: 'build-packs', label: 'Build Packs' }, { link: 'stacks', label: 'Stacks' }, diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts index bc16ea4906..d6dbf672e7 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts @@ -77,6 +77,7 @@ import { UsersRolesComponent } from './users/manage-users/manage-users.component import { CfRolesService } from './users/manage-users/cf-roles.service'; import { UsersRolesSelectComponent } from './users/manage-users/manage-users-select/manage-users-select.component'; import { UsersRolesConfirmComponent } from './users/manage-users/manage-users-confirm/manage-users-confirm.component'; +import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component'; @NgModule({ @@ -90,6 +91,7 @@ import { UsersRolesConfirmComponent } from './users/manage-users/manage-users-co CloudFoundryUsersComponent, CloudFoundryFirehoseComponent, CloudFoundryFeatureFlagsComponent, + CloudFoundryCellsComponent, CloudFoundryBuildPacksComponent, CloudFoundryStacksComponent, CloudFoundrySecurityGroupsComponent, diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts index 4382b1938d..d1108d0f49 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts @@ -54,6 +54,7 @@ import { CloudFoundrySummaryTabComponent } from './tabs/cloud-foundry-summary-ta import { CloudFoundryUsersComponent } from './tabs/cloud-foundry-users/cloud-foundry-users.component'; import { EditOrganizationComponent } from './edit-organization/edit-organization.component'; import { CliInfoCloudFoundryComponent } from './cli-info-cloud-foundry/cli-info-cloud-foundry.component'; +import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component'; const usersRoles = [ { @@ -143,6 +144,10 @@ const cloudFoundry: Routes = [{ path: 'firehose', component: CloudFoundryFirehoseComponent }, + { + path: 'cells', + component: CloudFoundryCellsComponent + }, { path: 'feature-flags', component: CloudFoundryFeatureFlagsComponent diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html new file mode 100644 index 0000000000..182ea6f9c7 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html @@ -0,0 +1,4 @@ +
+ + hello world +
diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.spec.ts new file mode 100644 index 0000000000..94eae4a6f6 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.spec.ts @@ -0,0 +1,29 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CloudFoundryCellsComponent } from './cloud-foundry-cells.component'; +import { BaseTestModules } from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { ActiveRouteCfOrgSpace } from '../../cf-page.types'; + +describe('CloudFoundryCellsComponent', () => { + let component: CloudFoundryCellsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CloudFoundryCellsComponent], + imports: [...BaseTestModules], + providers: [ActiveRouteCfOrgSpace] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CloudFoundryCellsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts new file mode 100644 index 0000000000..fd9e54ca5e --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts @@ -0,0 +1,39 @@ +import { Component } from '@angular/core'; +import { Store } from '@ngrx/store'; + +import { + CfFeatureFlagsListConfigService, +} from '../../../../shared/components/list/list-types/cf-feature-flags/cf-feature-flags-list-config.service'; +import { ListConfig } from '../../../../shared/components/list/list.component.types'; +import { AppState } from '../../../../store/app-state'; +import { CloudFoundryEndpointService } from '../../services/cloud-foundry-endpoint.service'; +import { MetricQueryConfig, MetricQueryType, FetchCFMetricsAction } from '../../../../store/actions/metrics.actions'; + +@Component({ + selector: 'app-cloud-foundry-cells', + templateUrl: './cloud-foundry-cells.component.html', + styleUrls: ['./cloud-foundry-cells.component.scss'], + providers: [ + { + provide: ListConfig, + useClass: CfFeatureFlagsListConfigService + } + ] +}) +export class CloudFoundryCellsComponent { + + constructor( + private store: Store, + public cfEndpointService: CloudFoundryEndpointService, + ) { + // TODO: RC Move to config service + // response + // {"IcXF69N2sUyBYliHug7f-_17WCA":{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"firehose_value_metric_rep_unhealthy_cell","bosh_deployment":"pcfdev","bosh_job_id":"0","bosh_job_name":"pcfdev","doppler_endpoint":"wss://doppler.local.pcfdev.io:443","environment":"wss://doppler.local.pcfdev.io:443","instance":"kneeling-cheetah-f-exp-service:9186","job":"cf-firehose","origin":"rep","unit":"Metric"},"value":[1537886663.132,"0"]}]}}} + const action = new FetchCFMetricsAction( + cfEndpointService.cfGuid, + new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}), + MetricQueryType.QUERY + ); + store.dispatch(action); + } +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component.ts index f5cbe9361d..cdd1c68108 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component.ts @@ -1,12 +1,9 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; -import { IFeatureFlag } from '../../../../core/cf-api.types'; -import { ListDataSource } from '../../../../shared/components/list/data-sources-controllers/list-data-source'; import { CfFeatureFlagsListConfigService, } from '../../../../shared/components/list/list-types/cf-feature-flags/cf-feature-flags-list-config.service'; import { ListConfig } from '../../../../shared/components/list/list.component.types'; -import { APIResource } from '../../../../store/types/api.types'; @Component({ selector: 'app-cloud-foundry-feature-flags', @@ -19,13 +16,4 @@ import { APIResource } from '../../../../store/types/api.types'; } ] }) -export class CloudFoundryFeatureFlagsComponent implements OnInit { - - constructor(private listConfig: ListConfig) { - const dataSource: ListDataSource = listConfig.getDataSource(); - } - - ngOnInit() { - } - -} +export class CloudFoundryFeatureFlagsComponent { } diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts new file mode 100644 index 0000000000..b8e5f03899 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts @@ -0,0 +1,26 @@ +// import { Store } from '@ngrx/store'; + +// import { IFeatureFlag } from '../../../../../core/cf-api.types'; +// import { getRowMetadata } from '../../../../../features/cloud-foundry/cf.helpers'; +// import { AppState } from '../../../../../store/app-state'; +// import { entityFactory, featureFlagSchemaKey } from '../../../../../store/helpers/entity-factory'; +// import { APIResource } from '../../../../../store/types/api.types'; +// import { ListDataSource } from '../../data-sources-controllers/list-data-source'; +// import { IListConfig } from '../../list.component.types'; +// import { createCfFeatureFlagFetchAction } from './cf-feature-flags-data-source.helpers'; + +// export class CfFeatureFlagsDataSource extends ListDataSource> { +// constructor(store: Store, cfGuid: string, listConfig?: IListConfig>) { +// const action = createCfFeatureFlagFetchAction(cfGuid); +// super({ +// store, +// action, +// schema: entityFactory(featureFlagSchemaKey), +// getRowUniqueId: getRowMetadata, +// paginationKey: action.paginationKey, +// isLocal: true, +// transformEntities: [{ type: 'filter', field: 'entity.name' }], +// listConfig +// }); +// } +// } diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts new file mode 100644 index 0000000000..4488d747a4 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts @@ -0,0 +1,70 @@ +// import { Injectable } from '@angular/core'; +// import { Store } from '@ngrx/store'; + +// import { IFeatureFlag } from '../../../../../core/cf-api.types'; +// import { ActiveRouteCfOrgSpace } from '../../../../../features/cloud-foundry/cf-page.types'; +// import { ListView } from '../../../../../store/actions/list.actions'; +// import { AppState } from '../../../../../store/app-state'; +// import { APIResource } from '../../../../../store/types/api.types'; +// import { ITableColumn } from '../../list-table/table.types'; +// import { ListViewTypes } from '../../list.component.types'; +// import { BaseCfListConfig } from '../base-cf/base-cf-list-config'; +// import { CfFeatureFlagsDataSource, FeatureFlagDescriptions } from './cf-cells-data-source'; +// import { TableCellFeatureFlagStateComponent } from './table-cell-feature-flag-state/table-cell-feature-flag-state.component'; + +// @Injectable() +// export class CfCellsListConfigService extends BaseCfListConfig> { +// dataSource: CfFeatureFlagsDataSource; +// defaultView = 'table' as ListView; +// pageSizeOptions = [25, 50, 100]; +// viewType = ListViewTypes.TABLE_ONLY; +// enableTextFilter = true; +// text = { +// title: null, +// filter: 'Search by name', +// noEntries: 'There are no feature flags' +// }; + +// columns: Array>> = [ +// { +// columnId: 'name', +// headerCell: () => 'Name', +// cellDefinition: { +// getValue: (row) => `${row.entity.name}` +// }, +// class: 'table-column-select', +// cellFlex: '2', +// sort: { +// type: 'sort', +// orderKey: 'name', +// field: 'entity.name' +// } +// }, +// { +// columnId: 'description', +// headerCell: () => 'Description', +// cellDefinition: { +// getValue: (row) => FeatureFlagDescriptions[row.entity.name] +// }, +// class: 'table-column-select', +// cellFlex: '4' +// }, +// { +// columnId: 'state', +// headerCell: () => 'State', +// cellComponent: TableCellFeatureFlagStateComponent, +// sort: { +// type: 'sort', +// orderKey: 'state', +// field: 'entity.enabled' +// }, +// cellFlex: '1' +// } +// ]; +// constructor(private store: Store, private activeRouteCfOrgSpace: ActiveRouteCfOrgSpace) { +// super(); +// this.dataSource = new CfFeatureFlagsDataSource(this.store, activeRouteCfOrgSpace.cfGuid, this); +// } +// getColumns = () => this.columns; +// getDataSource = () => this.dataSource; +// } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 53f3b01bc9..fb4cbd1a1c 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -58,9 +58,9 @@ export abstract class MetricsAction implements IRequestAction { export class FetchCFMetricsAction extends MetricsAction { public cfGuid: string; - constructor(public guid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { - super(guid, query, queryType); - this.cfGuid = guid; + constructor(cfGuid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { + super(cfGuid, query, queryType); + this.cfGuid = cfGuid; this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; } } From c5d4e920f4a3c21bb5c1e07252db7e2cef385799 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Wed, 26 Sep 2018 12:37:37 +0100 Subject: [PATCH 027/114] WIP Pull out metrics chart time range selector --- src/frontend/app/custom.module.ts | 1 + .../home/home/home-page.component.html | 1 + .../features/home/home/home-page.component.ts | 11 +- .../metrics-range-selector.component.html | 33 +++ .../metrics-range-selector.component.scss | 75 ++++++ .../metrics-range-selector.component.spec.ts | 25 ++ .../metrics-range-selector.component.ts | 234 ++++++++++++++++++ src/frontend/app/shared/shared.module.ts | 3 + 8 files changed, 380 insertions(+), 3 deletions(-) create mode 120000 src/frontend/app/custom.module.ts create mode 100644 src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html create mode 100644 src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss create mode 100644 src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.spec.ts create mode 100644 src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts diff --git a/src/frontend/app/custom.module.ts b/src/frontend/app/custom.module.ts new file mode 120000 index 0000000000..d55e73834c --- /dev/null +++ b/src/frontend/app/custom.module.ts @@ -0,0 +1 @@ +/Users/nathanjones/go/src/github.com/cloudfoundry-incubator/stratos/src/frontend/misc/custom/custom.module.ts_ \ No newline at end of file diff --git a/src/frontend/app/features/home/home/home-page.component.html b/src/frontend/app/features/home/home/home-page.component.html index 6bb9252560..f11b6c5970 100644 --- a/src/frontend/app/features/home/home/home-page.component.html +++ b/src/frontend/app/features/home/home/home-page.component.html @@ -1,3 +1,4 @@

Dashboard

+ \ No newline at end of file diff --git a/src/frontend/app/features/home/home/home-page.component.ts b/src/frontend/app/features/home/home/home-page.component.ts index d62fe261c5..69eda66492 100644 --- a/src/frontend/app/features/home/home/home-page.component.ts +++ b/src/frontend/app/features/home/home/home-page.component.ts @@ -1,6 +1,7 @@ -import { AppState } from '../../../store/app-state'; +import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; -import { Component, OnInit, AfterContentInit } from '@angular/core'; +import { FetchApplicationMetricsAction, MetricQueryConfig } from '../../../store/actions/metrics.actions'; +import { AppState } from '../../../store/app-state'; @Component({ selector: 'app-home-page', @@ -10,6 +11,10 @@ import { Component, OnInit, AfterContentInit } from '@angular/core'; export class HomePageComponent implements OnInit { constructor(private store: Store) { } - + public metricsAction = new FetchApplicationMetricsAction( + 'fbb2e26f-491f-468c-8d5b-ed02028f7106', + 'rqljU7j5TF-v8_nyozXsd6kDUeU', + new MetricQueryConfig('firehose_container_metric_cpu_percentage') + ); ngOnInit() { } } diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html new file mode 100644 index 0000000000..913daf7cd0 --- /dev/null +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html @@ -0,0 +1,33 @@ +
+
+
+

Choose time window

+ +
+ + +
+
+
+
+ + + + {{time.label}} + + + +
+
+
{{ committedStartEnd[0] | amDateFormat:'Do MMM YYYY, HH:mm' }} +
+
to
+
{{ committedStartEnd[1] | amDateFormat:'Do MMM YYYY, HH:mm' }} +
+
+ No dates selected + Edit +
+
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss new file mode 100644 index 0000000000..83d4dd4bec --- /dev/null +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss @@ -0,0 +1,75 @@ +@import '../../../../sass/mixins'; +.metrics-chart { + $animation-function: cubic-bezier(0, 0, .2, 1); + $animation-time: 250ms; + &__overlay { + background-color: rgba(0, 0, 0, 0); + bottom: 0; + left: 0; + overflow: hidden; + position: absolute; + right: 0; + top: 0; + transition: visibility 0s linear $animation-time, background-color $animation-time $animation-function; + visibility: hidden; + z-index: 1; + &-buttons { + margin-top: 10px; + } + &-click { + bottom: 0; + left: 0; + position: absolute; + right: 0; + top: 0; + } + &-show { + background-color: rgba(0, 0, 0, .6); + transition-delay: 0s; + visibility: visible; + .metrics-chart__overlay-inner { + transform: translateY(0); + } + } + + &-inner { + box-shadow: -4px 3px 41px -1px rgba(0, 0, 0, .36); + padding: 50px; + position: relative; + transform: translateY(-100%); + transition: transform $animation-time $animation-function; + z-index: 2; + } + + &-set { + margin-right: 10px; + } + + &-overflow { + overflow: hidden; + } + } + &__selected-range { + align-items: center; + display: flex; + } + &__selected-range-dates { + display: none; + font-size: 14px; + opacity: .6; + padding-left: 10px; + @include breakpoint(tablet) { + display: flex; + } + } + &__selected-range-date { + font-weight: bold; + } + &__selected-range-to { + padding: 0 10px; + } + &__selected-range-edit { + cursor: pointer; + margin-left: 10px; + } +} \ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.spec.ts b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.spec.ts new file mode 100644 index 0000000000..7b42cde8ca --- /dev/null +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MetricsRangeSelectorComponent } from './metrics-range-selector.component'; + +describe('MetricsRangeSelectorComponent', () => { + let component: MetricsRangeSelectorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MetricsRangeSelectorComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MetricsRangeSelectorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts new file mode 100644 index 0000000000..df32012e04 --- /dev/null +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts @@ -0,0 +1,234 @@ +import { Component, OnInit, Output, EventEmitter, OnDestroy, Input } from '@angular/core'; +import * as moment from 'moment'; +import { FetchApplicationMetricsAction, MetricQueryConfig, MetricsAction, MetricQueryType } from '../../../store/actions/metrics.actions'; +import { EntityMonitor } from '../../monitors/entity-monitor'; +import { IMetrics } from '../../../store/types/base-metric.types'; +import { debounceTime, tap, takeWhile } from 'rxjs/operators'; +import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; +import { metricSchemaKey, entityFactory } from '../../../store/helpers/entity-factory'; +import { Subscription } from 'rxjs'; + +enum RangeType { + ROLLING_WINDOW = 'ROLLING_WINDOW', + START_END = 'START_END' +} + +interface ITimeRange { + value?: string; + label: string; + type: RangeType; +} + +@Component({ + selector: 'app-metrics-range-selector', + templateUrl: './metrics-range-selector.component.html', + styleUrls: ['./metrics-range-selector.component.scss'] +}) +export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { + + private committedAction: MetricsAction; + + public metricsMonitor: EntityMonitor; + + private readonly startIndex = 0; + + private readonly endIndex = 1; + + private startEnd: [moment.Moment, moment.Moment] = [null, null]; + + private initSub: Subscription; + + @Output() + public metricsAction = new EventEmitter(); + + @Input() + public baseAction: MetricsAction; + + public commit: Function = null; + + public dateValid = false; + + public committedStartEnd: [moment.Moment, moment.Moment] = [null, null]; + + public rangeTypes = RangeType; + + public times: ITimeRange[] = [ + { + value: '5m', + label: 'The past 5 minutes', + type: RangeType.ROLLING_WINDOW + }, + { + value: '1h', + label: 'The past hour', + type: RangeType.ROLLING_WINDOW + }, + { + value: '1w', + label: 'The past week', + type: RangeType.ROLLING_WINDOW + }, + { + label: 'Set time window', + type: RangeType.START_END + } + ]; + + public selectedTimeRangeValue: ITimeRange; + + set showOverlay(show: boolean) { + this.showOverlayValue = show; + } + + get showOverlay() { + return this.showOverlayValue; + } + + public showOverlayValue = false; + + private commitDate(date: moment.Moment, type: 'start' | 'end') { + const index = type === 'start' ? this.startIndex : this.endIndex; + const oldDate = this.startEnd[index]; + if (!date.isValid() || date.isSame(oldDate)) { + return; + } + this.startEnd[index] = date; + const [start, end] = this.startEnd; + if (start && end) { + const startUnix = start.unix(); + const endUnix = end.unix(); + const oldAction = this.baseAction; + const action = new FetchApplicationMetricsAction( + oldAction.guid, + oldAction.cfGuid, + new MetricQueryConfig(this.baseAction.query.metric, { + start: startUnix, + end: end.unix(), + step: Math.max((endUnix - startUnix) / 200, 0) + }), + MetricQueryType.RANGE_QUERY + ); + + this.commit = () => { + console.log('commiting'); + this.committedStartEnd = [ + this.startEnd[0], + this.startEnd[1] + ]; + this.metricsAction.emit(action); + }; + } + } + + get selectedTimeRange() { + return this.selectedTimeRangeValue; + } + + set selectedTimeRange(timeRange: ITimeRange) { + this.commit = null; + this.selectedTimeRangeValue = timeRange; + if (this.selectedTimeRangeValue.type === RangeType.ROLLING_WINDOW) { + this.commitWindow(this.selectedTimeRangeValue); + } else if (this.selectedTimeRangeValue.type === RangeType.START_END) { + if (!this.startEnd[0] || !this.startEnd[1]) { + this.showOverlay = true; + } + } + } + + private getInitSub(entityMonitor: EntityMonitor) { + return entityMonitor.entity$.pipe( + debounceTime(1), + tap(metrics => { + if (!this.selectedTimeRange) { + if (metrics) { + if (metrics.queryType === MetricQueryType.RANGE_QUERY) { + const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); + const end = moment.unix(parseInt(metrics.query.params.end as string, 10)); + const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); + if (isDifferent) { + this.start = start; + this.end = end; + this.committedStartEnd = [start, end]; + } + this.selectedTimeRange = this.times.find(time => time.type === RangeType.START_END); + } else { + const newWindow = metrics.query.params.window ? + this.times.find(time => time.value === metrics.query.params.window) : + this.getDefaultTimeRange(); + if (this.selectedTimeRange !== newWindow) { + this.selectedTimeRange = newWindow; + } + } + } else { + this.selectedTimeRange = this.getDefaultTimeRange(); + } + } + }), + takeWhile(metrics => !metrics) + ).subscribe(); + } + + private commitWindow(window: ITimeRange) { + if (!window) { + return; + } + this.committedStartEnd = [null, null]; + this.startEnd = [null, null]; + const oldAction = this.baseAction; + const action = new FetchApplicationMetricsAction( + oldAction.guid, + oldAction.cfGuid, + new MetricQueryConfig(this.baseAction.query.metric, { + window: window.value + }) + ); + this.commitAction(action); + } + + set start(start: moment.Moment) { + this.commitDate(start, 'start'); + } + + get start() { + return this.startEnd[this.startIndex]; + } + + set end(end: moment.Moment) { + this.commitDate(end, 'end'); + } + + get end() { + return this.startEnd[this.endIndex]; + } + + private getDefaultTimeRange() { + return this.times.find(time => time.value === '1h') || this.times[0]; + } + + private commitAction(action: MetricsAction) { + this.committedAction = action; + this.commit = null; + } + + constructor(private entityMonitorFactory: EntityMonitorFactory) { } + + ngOnInit() { + + this.committedAction = this.baseAction; + this.metricsMonitor = this.entityMonitorFactory.create( + this.baseAction.metricId, + metricSchemaKey, + entityFactory(metricSchemaKey) + ); + + this.initSub = this.getInitSub(this.metricsMonitor); + } + + ngOnDestroy() { + if (this.initSub) { + this.initSub.unsubscribe(); + } + } + +} diff --git a/src/frontend/app/shared/shared.module.ts b/src/frontend/app/shared/shared.module.ts index 97794f16d5..303f99efd5 100644 --- a/src/frontend/app/shared/shared.module.ts +++ b/src/frontend/app/shared/shared.module.ts @@ -136,6 +136,7 @@ import { RoutingIndicatorComponent } from './components/routing-indicator/routin import { DateTimeComponent } from './components/date-time/date-time.component'; import { StartEndDateComponent } from './components/start-end-date/start-end-date.component'; import { MomentModule } from 'ngx-moment'; +import { MetricsRangeSelectorComponent } from './components/metrics-range-selector/metrics-range-selector.component'; @NgModule({ imports: [ @@ -242,6 +243,7 @@ import { MomentModule } from 'ngx-moment'; RoutingIndicatorComponent, DateTimeComponent, StartEndDateComponent, + MetricsRangeSelectorComponent, ], exports: [ FormsModule, @@ -333,6 +335,7 @@ import { MomentModule } from 'ngx-moment'; RoutingIndicatorComponent, DateTimeComponent, StartEndDateComponent, + MetricsRangeSelectorComponent, ], entryComponents: [ AppEventDetailDialogComponentComponent, From d263966076d7122e0575dee002a5ba33e374cde4 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 26 Sep 2018 11:20:25 +0100 Subject: [PATCH 028/114] Basic Cloud Foundry Cells List --- .../cloud-foundry/cloud-foundry.routing.ts | 8 +- .../cloud-foundry-cells.component.html | 5 +- .../cloud-foundry-cells.component.ts | 28 +--- .../boolean-indicator.component.html | 4 +- .../boolean-indicator.component.ts | 3 +- .../list-data-source-config.ts | 2 +- ...able-cell-boolean-indicator.component.html | 1 + ...able-cell-boolean-indicator.component.scss | 0 ...e-cell-boolean-indicator.component.spec.ts | 29 ++++ .../table-cell-boolean-indicator.component.ts | 48 ++++++ .../table-cell/table-cell.component.ts | 2 + .../cf-cells/cf-cells-data-source.ts | 65 +++++--- .../cf-cells/cf-cells-list-config.service.ts | 151 ++++++++++-------- .../app/store/actions/metrics.actions.ts | 11 +- .../app/store/effects/metrics.effects.ts | 3 +- .../pagination-reducer-success.ts | 5 +- .../app/store/types/base-metric.types.ts | 1 + src/frontend/app/store/types/metric.types.ts | 6 +- 18 files changed, 235 insertions(+), 137 deletions(-) create mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.html create mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.scss create mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.spec.ts create mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.ts diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts index d1108d0f49..b9d85ccb17 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts @@ -140,14 +140,14 @@ const cloudFoundry: Routes = [{ path: 'users', component: CloudFoundryUsersComponent }, - { - path: 'firehose', - component: CloudFoundryFirehoseComponent - }, { path: 'cells', component: CloudFoundryCellsComponent }, + { + path: 'firehose', + component: CloudFoundryFirehoseComponent + }, { path: 'feature-flags', component: CloudFoundryFeatureFlagsComponent diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html index 182ea6f9c7..f2d54fe259 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html @@ -1,4 +1 @@ -
- - hello world -
+ diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts index fd9e54ca5e..1b5c66d55d 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts @@ -1,13 +1,9 @@ import { Component } from '@angular/core'; -import { Store } from '@ngrx/store'; import { - CfFeatureFlagsListConfigService, -} from '../../../../shared/components/list/list-types/cf-feature-flags/cf-feature-flags-list-config.service'; + CfCellsListConfigService, +} from '../../../../shared/components/list/list-types/cf-cells/cf-cells-list-config.service'; import { ListConfig } from '../../../../shared/components/list/list.component.types'; -import { AppState } from '../../../../store/app-state'; -import { CloudFoundryEndpointService } from '../../services/cloud-foundry-endpoint.service'; -import { MetricQueryConfig, MetricQueryType, FetchCFMetricsAction } from '../../../../store/actions/metrics.actions'; @Component({ selector: 'app-cloud-foundry-cells', @@ -16,24 +12,8 @@ import { MetricQueryConfig, MetricQueryType, FetchCFMetricsAction } from '../../ providers: [ { provide: ListConfig, - useClass: CfFeatureFlagsListConfigService + useClass: CfCellsListConfigService } ] }) -export class CloudFoundryCellsComponent { - - constructor( - private store: Store, - public cfEndpointService: CloudFoundryEndpointService, - ) { - // TODO: RC Move to config service - // response - // {"IcXF69N2sUyBYliHug7f-_17WCA":{"status":"success","data":{"resultType":"vector","result":[{"metric":{"__name__":"firehose_value_metric_rep_unhealthy_cell","bosh_deployment":"pcfdev","bosh_job_id":"0","bosh_job_name":"pcfdev","doppler_endpoint":"wss://doppler.local.pcfdev.io:443","environment":"wss://doppler.local.pcfdev.io:443","instance":"kneeling-cheetah-f-exp-service:9186","job":"cf-firehose","origin":"rep","unit":"Metric"},"value":[1537886663.132,"0"]}]}}} - const action = new FetchCFMetricsAction( - cfEndpointService.cfGuid, - new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}), - MetricQueryType.QUERY - ); - store.dispatch(action); - } -} +export class CloudFoundryCellsComponent { } diff --git a/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.html b/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.html index e0920f4fc1..6f7cc4f786 100644 --- a/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.html +++ b/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.html @@ -1,10 +1,10 @@
{{ getIcon() }} -
{{ getText() }}
+
{{ getText() }}
{{ getIcon() }} -
{{ getText() }}
+
{{ getText() }}
diff --git a/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.ts b/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.ts index 9242810eaa..e175209f04 100644 --- a/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.ts +++ b/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.ts @@ -19,8 +19,9 @@ export class BooleanIndicatorComponent implements OnInit { @Input() isTrue: boolean; @Input() type: BooleanIndicatorType; + @Input() showText: boolean; - // Should we use a subtle display - this won't show the No option as dandger (typically red) + // Should we use a subtle display - this won't show the No option as danger (typically red) @Input() subtle = true; private icons = { diff --git a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source-config.ts b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source-config.ts index a3c890f8c4..6baa80d1c2 100644 --- a/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source-config.ts +++ b/src/frontend/app/shared/components/list/data-sources-controllers/list-data-source-config.ts @@ -22,7 +22,7 @@ export interface IListDataSourceConfig { /** * A function which will return a unique id for the given row/entity */ - getRowUniqueId: getRowUniqueId; + getRowUniqueId: getRowUniqueId; /** * The key used to uniquely identify this instance of the data in the pagination section of the store */ diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.html b/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.html new file mode 100644 index 0000000000..d1af6dec62 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.html @@ -0,0 +1 @@ + diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.scss b/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.spec.ts b/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.spec.ts new file mode 100644 index 0000000000..87af86627f --- /dev/null +++ b/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.spec.ts @@ -0,0 +1,29 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BaseTestModulesNoShared } from '../../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { BooleanIndicatorComponent } from '../../../boolean-indicator/boolean-indicator.component'; +import { TableCellBooleanIndicatorComponent } from './table-cell-boolean-indicator.component'; + + +describe('TableCellBooleanIndicatorComponent', () => { + let component: TableCellBooleanIndicatorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [TableCellBooleanIndicatorComponent, BooleanIndicatorComponent], + imports: [...BaseTestModulesNoShared] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TableCellBooleanIndicatorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.ts new file mode 100644 index 0000000000..a65e9912c9 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component.ts @@ -0,0 +1,48 @@ +import { Component, Input } from '@angular/core'; + +import { TableCellCustom } from '../../list.types'; + +export interface TableCellBooleanIndicatorComponentConfig { + isEnabled: (row: T) => boolean; + type?: string; + subtle?: boolean; + showText?: boolean; +} + +@Component({ + selector: 'app-table-cell-boolean-indicator', + templateUrl: './table-cell-boolean-indicator.component.html', + styleUrls: ['./table-cell-boolean-indicator.component.scss'] +}) +export class TableCellBooleanIndicatorComponent extends TableCellCustom { + + private _row: T; + @Input('row') + get row() { return this._row; } + set row(row: T) { + this._row = row; + if (this.config) { + this.enabled = this.config.isEnabled(row); + } + } + + private _config: TableCellBooleanIndicatorComponentConfig; + @Input('config') + get config() { return this._config; } + set config(config: TableCellBooleanIndicatorComponentConfig) { + this._config = config; + if (!config) { + return; + } + this.enabled = config.isEnabled(this.row); + this.type = config.type; + this.subtle = config.subtle; + this.showText = config.showText; + } + + enabled: boolean; + type = 'enabled-disabled'; + subtle = true; + showText = true; + +} diff --git a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts index 6c65dfbf8f..02d60c297b 100644 --- a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts +++ b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts @@ -104,6 +104,7 @@ import { TableHeaderSelectComponent } from '../table-header-select/table-header- import { ICellDefinition } from '../table.types'; import { TableCellSpaceNameComponent } from '../../list-types/cf-spaces-service-instances/table-cell-space-name/table-cell-space-name.component'; import { TableCellCfCellComponent } from '../../list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component'; +import { TableCellBooleanIndicatorComponent } from '../table-cell-boolean-indicator/table-cell-boolean-indicator.component'; /* tslint:enable:max-line-length */ @@ -124,6 +125,7 @@ export const listTableCells = [ TableCellAppStatusComponent, TableCellUsageComponent, TableCellCfCellComponent, + TableCellBooleanIndicatorComponent, TableCellRouteComponent, TableCellTCPRouteComponent, TableCellAppInstancesComponent, diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts index b8e5f03899..8ab563defe 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts @@ -1,26 +1,43 @@ -// import { Store } from '@ngrx/store'; +import { Store } from '@ngrx/store'; +import { map } from 'rxjs/operators'; -// import { IFeatureFlag } from '../../../../../core/cf-api.types'; -// import { getRowMetadata } from '../../../../../features/cloud-foundry/cf.helpers'; -// import { AppState } from '../../../../../store/app-state'; -// import { entityFactory, featureFlagSchemaKey } from '../../../../../store/helpers/entity-factory'; -// import { APIResource } from '../../../../../store/types/api.types'; -// import { ListDataSource } from '../../data-sources-controllers/list-data-source'; -// import { IListConfig } from '../../list.component.types'; -// import { createCfFeatureFlagFetchAction } from './cf-feature-flags-data-source.helpers'; +import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; +import { AppState } from '../../../../../store/app-state'; +import { entityFactory } from '../../../../../store/helpers/entity-factory'; +import { IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; +import { IMetricApplication } from '../../../../../store/types/metric.types'; +import { ListDataSource } from '../../data-sources-controllers/list-data-source'; +import { IListConfig } from '../../list.component.types'; -// export class CfFeatureFlagsDataSource extends ListDataSource> { -// constructor(store: Store, cfGuid: string, listConfig?: IListConfig>) { -// const action = createCfFeatureFlagFetchAction(cfGuid); -// super({ -// store, -// action, -// schema: entityFactory(featureFlagSchemaKey), -// getRowUniqueId: getRowMetadata, -// paginationKey: action.paginationKey, -// isLocal: true, -// transformEntities: [{ type: 'filter', field: 'entity.name' }], -// listConfig -// }); -// } -// } +export class CfCellsDataSource + extends ListDataSource, IMetrics>> { + + static cellIdPath = 'metric.bosh_job_id'; + static cellNamePath = 'metric.bosh_job_name'; + static cellHealthyPath = 'value.1'; + + constructor(store: Store, cfGuid: string, listConfig: IListConfig>) { + const action = new FetchCFMetricsAction( + cfGuid, + new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}), + MetricQueryType.QUERY + ); + + super({ + store, + action, + schema: entityFactory(action.entityKey), + getRowUniqueId: (row) => row.metric.bosh_job_id, + paginationKey: action.paginationKey, + isLocal: true, + transformEntities: [{ type: 'filter', field: CfCellsDataSource.cellNamePath }], + transformEntity: map((response) => { + if (!response || response.length === 0) { + return []; + } + return response[0].data.result; + }), + listConfig + }); + } +} diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts index 4488d747a4..cd9e3db6cf 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts @@ -1,70 +1,87 @@ -// import { Injectable } from '@angular/core'; -// import { Store } from '@ngrx/store'; +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { Store } from '@ngrx/store'; -// import { IFeatureFlag } from '../../../../../core/cf-api.types'; -// import { ActiveRouteCfOrgSpace } from '../../../../../features/cloud-foundry/cf-page.types'; -// import { ListView } from '../../../../../store/actions/list.actions'; -// import { AppState } from '../../../../../store/app-state'; -// import { APIResource } from '../../../../../store/types/api.types'; -// import { ITableColumn } from '../../list-table/table.types'; -// import { ListViewTypes } from '../../list.component.types'; -// import { BaseCfListConfig } from '../base-cf/base-cf-list-config'; -// import { CfFeatureFlagsDataSource, FeatureFlagDescriptions } from './cf-cells-data-source'; -// import { TableCellFeatureFlagStateComponent } from './table-cell-feature-flag-state/table-cell-feature-flag-state.component'; +import { ActiveRouteCfOrgSpace } from '../../../../../features/cloud-foundry/cf-page.types'; +import { ListView } from '../../../../../store/actions/list.actions'; +import { AppState } from '../../../../../store/app-state'; +import { IMetricVectorResult } from '../../../../../store/types/base-metric.types'; +import { IMetricApplication } from '../../../../../store/types/metric.types'; +import { getIntegerFieldSortFunction } from '../../data-sources-controllers/local-filtering-sorting'; +import { + TableCellBooleanIndicatorComponent, + TableCellBooleanIndicatorComponentConfig, +} from '../../list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component'; +import { ITableColumn } from '../../list-table/table.types'; +import { ListViewTypes } from '../../list.component.types'; +import { BaseCfListConfig } from '../base-cf/base-cf-list-config'; +import { CfCellsDataSource } from './cf-cells-data-source'; -// @Injectable() -// export class CfCellsListConfigService extends BaseCfListConfig> { -// dataSource: CfFeatureFlagsDataSource; -// defaultView = 'table' as ListView; -// pageSizeOptions = [25, 50, 100]; -// viewType = ListViewTypes.TABLE_ONLY; -// enableTextFilter = true; -// text = { -// title: null, -// filter: 'Search by name', -// noEntries: 'There are no feature flags' -// }; +@Injectable() +export class CfCellsListConfigService extends BaseCfListConfig> { -// columns: Array>> = [ -// { -// columnId: 'name', -// headerCell: () => 'Name', -// cellDefinition: { -// getValue: (row) => `${row.entity.name}` -// }, -// class: 'table-column-select', -// cellFlex: '2', -// sort: { -// type: 'sort', -// orderKey: 'name', -// field: 'entity.name' -// } -// }, -// { -// columnId: 'description', -// headerCell: () => 'Description', -// cellDefinition: { -// getValue: (row) => FeatureFlagDescriptions[row.entity.name] -// }, -// class: 'table-column-select', -// cellFlex: '4' -// }, -// { -// columnId: 'state', -// headerCell: () => 'State', -// cellComponent: TableCellFeatureFlagStateComponent, -// sort: { -// type: 'sort', -// orderKey: 'state', -// field: 'entity.enabled' -// }, -// cellFlex: '1' -// } -// ]; -// constructor(private store: Store, private activeRouteCfOrgSpace: ActiveRouteCfOrgSpace) { -// super(); -// this.dataSource = new CfFeatureFlagsDataSource(this.store, activeRouteCfOrgSpace.cfGuid, this); -// } -// getColumns = () => this.columns; -// getDataSource = () => this.dataSource; -// } + + dataSource: CfCellsDataSource; + defaultView = 'table' as ListView; + viewType = ListViewTypes.TABLE_ONLY; + enableTextFilter = true; + text = { + title: null, + filter: 'Search by name', + noEntries: 'There are no cells' + }; + + private boolIndicatorConfig: TableCellBooleanIndicatorComponentConfig> = { + // "0 signifies healthy, and 1 signifies unhealthy" + isEnabled: (row: IMetricVectorResult) => row ? row.value[1] === '0' : false, + type: 'enabled-disabled', + subtle: false, + showText: false + }; + + columns: Array>> = [ + { + columnId: 'id', + headerCell: () => 'ID', + cellDefinition: { + valuePath: CfCellsDataSource.cellIdPath + }, + class: 'table-column-select', + cellFlex: '0 0 100px', + sort: getIntegerFieldSortFunction(CfCellsDataSource.cellIdPath) + }, + { + columnId: 'name', + headerCell: () => 'Name', + cellDefinition: { + valuePath: CfCellsDataSource.cellNamePath + }, + cellFlex: '1', + sort: { + type: 'sort', + orderKey: 'name', + field: CfCellsDataSource.cellNamePath + } + }, + { + columnId: 'healthy', + headerCell: () => 'Healthy', + cellComponent: TableCellBooleanIndicatorComponent, + cellConfig: this.boolIndicatorConfig, + cellFlex: '1', + sort: { + type: 'sort', + orderKey: 'healthy', + field: CfCellsDataSource.cellHealthyPath + } + }, + ]; + + constructor(private store: Store, private activeRouteCfOrgSpace: ActiveRouteCfOrgSpace, private router: Router) { + super(); + this.dataSource = new CfCellsDataSource(this.store, activeRouteCfOrgSpace.cfGuid, this); + } + + getColumns = () => this.columns; + getDataSource = () => this.dataSource; +} diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index fb4cbd1a1c..58dcd65efd 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -1,6 +1,7 @@ import { metricSchemaKey } from '../helpers/entity-factory'; import { IRequestAction } from '../types/request.types'; import { environment } from './../../../environments/environment.prod'; +import { PaginatedAction } from '../types/pagination.types'; export const METRICS_START = '[Metrics] Fetch Start'; export const METRICS_START_SUCCESS = '[Metrics] Fetch Succeeded'; @@ -56,13 +57,20 @@ export abstract class MetricsAction implements IRequestAction { } } -export class FetchCFMetricsAction extends MetricsAction { +export class FetchCFMetricsAction extends MetricsAction implements PaginatedAction { public cfGuid: string; constructor(cfGuid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { super(cfGuid, query, queryType); this.cfGuid = cfGuid; this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; + this.paginationKey = this.metricId; } + actions = []; + paginationKey: string; + initialParams = { + 'order-direction': 'desc', + 'order-direction-field': 'id', + }; } export class FetchApplicationMetricsAction extends MetricsAction { @@ -70,4 +78,5 @@ export class FetchApplicationMetricsAction extends MetricsAction { super(guid, query, queryType); this.url = `${MetricsAction.getBaseMetricsURL()}/cf/app/${guid}`; } + } diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index d6a11eee83..cb2f5564a9 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -25,8 +25,7 @@ export class MetricsEffect { mergeMap(action => { const fullUrl = this.buildFullUrl(action); const apiAction = { - guid: action.metricId, - entityKey: metricSchemaKey + ...action, } as IRequestAction; this.store.dispatch(new StartRequestAction(apiAction)); return this.httpClient.get<{ [cfguid: string]: IMetricsResponse }>(fullUrl, { diff --git a/src/frontend/app/store/reducers/pagination-reducer/pagination-reducer-success.ts b/src/frontend/app/store/reducers/pagination-reducer/pagination-reducer-success.ts index f7ee08a16b..548a1280a3 100644 --- a/src/frontend/app/store/reducers/pagination-reducer/pagination-reducer-success.ts +++ b/src/frontend/app/store/reducers/pagination-reducer/pagination-reducer-success.ts @@ -1,7 +1,4 @@ -import { RequestAction } from '../../types/request.types'; -import { State } from '@ngrx/store'; -import { AppState } from '../../app-state'; -import { PaginationAction, PaginationEntityState } from '../../types/pagination.types'; +import { PaginationEntityState } from '../../types/pagination.types'; import { spreadClientPagination } from './pagination-reducer.helper'; export function paginationSuccess(state: PaginationEntityState, action): PaginationEntityState { diff --git a/src/frontend/app/store/types/base-metric.types.ts b/src/frontend/app/store/types/base-metric.types.ts index c9474e7c5a..594f07ab25 100644 --- a/src/frontend/app/store/types/base-metric.types.ts +++ b/src/frontend/app/store/types/base-metric.types.ts @@ -11,6 +11,7 @@ export interface IMetricsResponse { status: string; data: IMetrics; } + export interface IMetricsData { resultType: string; result: [T]; diff --git a/src/frontend/app/store/types/metric.types.ts b/src/frontend/app/store/types/metric.types.ts index 2b2b3a358c..894486a750 100644 --- a/src/frontend/app/store/types/metric.types.ts +++ b/src/frontend/app/store/types/metric.types.ts @@ -1,12 +1,12 @@ export interface IMetricApplication { __name__: string; - application_id: string; + application_id?: string; bosh_deployment: string; bosh_job_id: string; - bosh_job_ip: string; + bosh_job_ip?: string; bosh_job_name: string; instance: string; - instance_index: string; + instance_index?: string; job: string; origin: string; } From d83f551f12c1cfbf4fbd61e03da890bbd1939719 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 26 Sep 2018 15:20:27 +0100 Subject: [PATCH 029/114] WIP --- .../cloud-foundry/cloud-foundry.module.ts | 18 ++-- .../cloud-foundry/cloud-foundry.routing.ts | 32 +++++-- .../cf-cell-summary-chart.component.html | 1 + .../cf-cell-summary-chart.component.scss | 0 .../cf-cell-summary-chart.component.spec.ts | 36 ++++++++ .../cf-cell-summary-chart.component.ts | 78 ++++++++++++++++ .../cloud-foundry-cell-summary.component.html | 30 +++++++ .../cloud-foundry-cell-summary.component.scss | 4 + ...oud-foundry-cell-summary.component.spec.ts | 28 ++++++ .../cloud-foundry-cell-summary.component.ts | 90 +++++++++++++++++++ .../cf-cells/cf-cells-data-source.ts | 8 +- .../cf-cells/cf-cells-list-config.service.ts | 11 ++- .../metrics-chart/metrics-chart.component.ts | 56 +++++++----- .../app/store/actions/metrics.actions.ts | 9 +- src/frontend/app/store/types/metric.types.ts | 1 + 15 files changed, 364 insertions(+), 38 deletions(-) create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts index d6dbf672e7..13a2312586 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts @@ -1,3 +1,4 @@ +/* tslint:disable:max-line-length */ import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { NgxChartsModule } from '@swimlane/ngx-charts'; @@ -27,6 +28,13 @@ import { EditSpaceComponent } from './edit-space/edit-space.component'; import { CloudFoundryEndpointService } from './services/cloud-foundry-endpoint.service'; import { CloudFoundryOrganizationService } from './services/cloud-foundry-organization.service'; import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; +import { + CfCellSummaryChartComponent, +} from './tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component'; +import { + CloudFoundryCellSummaryComponent, +} from './tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component'; +import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component'; import { CloudFoundryFeatureFlagsComponent } from './tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component'; import { CloudFoundryFirehoseComponent } from './tabs/cloud-foundry-firehose/cloud-foundry-firehose.component'; import { @@ -38,7 +46,6 @@ import { import { CloudFoundrySpaceBaseComponent, } from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component'; -/* tslint:disable:max-line-length */ import { CloudFoundrySpaceAppsComponent, } from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-apps/cloud-foundry-space-apps.component'; @@ -69,15 +76,14 @@ import { import { CloudFoundryStacksComponent } from './tabs/cloud-foundry-stacks/cloud-foundry-stacks.component'; import { CloudFoundrySummaryTabComponent } from './tabs/cloud-foundry-summary-tab/cloud-foundry-summary-tab.component'; import { CloudFoundryUsersComponent } from './tabs/cloud-foundry-users/cloud-foundry-users.component'; +import { CfRolesService } from './users/manage-users/cf-roles.service'; +import { UsersRolesConfirmComponent } from './users/manage-users/manage-users-confirm/manage-users-confirm.component'; import { UsersRolesModifyComponent } from './users/manage-users/manage-users-modify/manage-users-modify.component'; import { SpaceRolesListWrapperComponent, } from './users/manage-users/manage-users-modify/space-roles-list-wrapper/space-roles-list-wrapper.component'; -import { UsersRolesComponent } from './users/manage-users/manage-users.component'; -import { CfRolesService } from './users/manage-users/cf-roles.service'; import { UsersRolesSelectComponent } from './users/manage-users/manage-users-select/manage-users-select.component'; -import { UsersRolesConfirmComponent } from './users/manage-users/manage-users-confirm/manage-users-confirm.component'; -import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component'; +import { UsersRolesComponent } from './users/manage-users/manage-users.component'; @NgModule({ @@ -92,6 +98,8 @@ import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-feature-cells/c CloudFoundryFirehoseComponent, CloudFoundryFeatureFlagsComponent, CloudFoundryCellsComponent, + CloudFoundryCellSummaryComponent, + CfCellSummaryChartComponent, CloudFoundryBuildPacksComponent, CloudFoundryStacksComponent, CloudFoundrySecurityGroupsComponent, diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts index b9d85ccb17..b5767ad837 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts @@ -3,12 +3,17 @@ import { RouterModule, Routes } from '@angular/router'; import { AddOrganizationComponent } from './add-organization/add-organization.component'; import { AddSpaceComponent } from './add-space/add-space.component'; +import { CliInfoCloudFoundryComponent } from './cli-info-cloud-foundry/cli-info-cloud-foundry.component'; import { CloudFoundryBaseComponent } from './cloud-foundry-base/cloud-foundry-base.component'; import { CloudFoundryTabsBaseComponent } from './cloud-foundry-tabs-base/cloud-foundry-tabs-base.component'; import { CloudFoundryComponent } from './cloud-foundry/cloud-foundry.component'; +import { EditOrganizationComponent } from './edit-organization/edit-organization.component'; import { EditSpaceComponent } from './edit-space/edit-space.component'; -import { UsersRolesComponent } from './users/manage-users/manage-users.component'; import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; +import { + CloudFoundryCellSummaryComponent, +} from './tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component'; +import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component'; import { CloudFoundryFeatureFlagsComponent } from './tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component'; import { CloudFoundryFirehoseComponent } from './tabs/cloud-foundry-firehose/cloud-foundry-firehose.component'; import { @@ -20,7 +25,6 @@ import { import { CloudFoundrySpaceBaseComponent, } from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/cloud-foundry-space-base/cloud-foundry-space-base.component'; -/* tslint:disable:max-line-length */ import { CloudFoundrySpaceAppsComponent, } from './tabs/cloud-foundry-organizations/cloud-foundry-organization-spaces/tabs/cloud-foundry-space-apps/cloud-foundry-space-apps.component'; @@ -48,14 +52,13 @@ import { import { CloudFoundrySecurityGroupsComponent, } from './tabs/cloud-foundry-security-groups/cloud-foundry-security-groups.component'; -/* tslint:enable:max-line-length */ import { CloudFoundryStacksComponent } from './tabs/cloud-foundry-stacks/cloud-foundry-stacks.component'; import { CloudFoundrySummaryTabComponent } from './tabs/cloud-foundry-summary-tab/cloud-foundry-summary-tab.component'; import { CloudFoundryUsersComponent } from './tabs/cloud-foundry-users/cloud-foundry-users.component'; -import { EditOrganizationComponent } from './edit-organization/edit-organization.component'; -import { CliInfoCloudFoundryComponent } from './cli-info-cloud-foundry/cli-info-cloud-foundry.component'; -import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component'; +import { UsersRolesComponent } from './users/manage-users/manage-users.component'; +/* tslint:disable:max-line-length */ +/* tslint:enable:max-line-length */ const usersRoles = [ { path: 'users/manage', @@ -170,6 +173,23 @@ const cloudFoundry: Routes = [{ path: '', // Root for Tabs children: [ + { + path: 'cells/:cellId', + data: { + uiFullView: true + }, + children: [ + { + path: '', + redirectTo: 'summary', + pathMatch: 'full' + }, + { + path: 'summary', + component: CloudFoundryCellSummaryComponent + }, + ] + }, { path: 'organizations/:orgId', component: CloudFoundryOrganizationBaseComponent, diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html new file mode 100644 index 0000000000..2d35f7f03a --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html @@ -0,0 +1 @@ + diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts new file mode 100644 index 0000000000..b8879f2f6b --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts @@ -0,0 +1,36 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { RouterTestingModule } from '@angular/router/testing'; + +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { CfCellSummaryChartComponent } from './cf-cell-summary-chart.component'; +import { createBasicStoreModule } from '../../../../../test-framework/store-test-helper'; +import { CoreModule } from '../../../../../core/core.module'; +import { SharedModule } from '../../../../../shared/shared.module'; + +describe('CfCellSummaryChartComponent', () => { + let component: CfCellSummaryChartComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ + createBasicStoreModule(), + RouterTestingModule, + CoreModule, + SharedModule, + NoopAnimationsModule + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CfCellSummaryChartComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts new file mode 100644 index 0000000000..919945a453 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts @@ -0,0 +1,78 @@ +import { Component, Input, OnInit } from '@angular/core'; +import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types'; +import { MetricsConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.component'; +import { IMetricMatrixResult } from '../../../../../store/types/base-metric.types'; +import { IMetricApplication } from '../../../../../store/types/metric.types'; +import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; +import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; + + +@Component({ + selector: 'app-cf-cell-summary-chart', + templateUrl: './cf-cell-summary-chart.component.html', + styleUrls: ['./cf-cell-summary-chart.component.scss'] +}) +export class CfCellSummaryChartComponent implements OnInit { + + @Input() + private cellId: string; + + @Input() + private endpointGuid: string; + + @Input() + private yAxisLabel: string; + + // Prometheus query string + @Input() + private queryString: string; + + @Input() + private seriesTranslation: string; + + @Input() + private queryRange = false; + + @Input() + public title: string; + + public instanceChartConfig: MetricsLineChartConfig; + + public instanceMetricConfig: MetricsConfig>; + + constructor() { } + + private buildChartConfig() { + const lineChartConfig = new MetricsLineChartConfig(); + lineChartConfig.xAxisLabel = 'Time'; + lineChartConfig.yAxisLabel = this.yAxisLabel; + return lineChartConfig; + } + + ngOnInit() { + this.instanceChartConfig = this.buildChartConfig(); + this.instanceMetricConfig = { + getSeriesName: result => `Cell ${result.metric.bosh_job_id}`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + sort: MetricsChartHelpers.sortBySeriesName, + // mapSeriesItemValue: this.mapSeriesItemValue(), + metricsAction: new FetchCFMetricsAction( + this.endpointGuid, + new MetricQueryConfig(this.queryString), + // TODO: RC MetricQueryType.RANGE_QUERY causes failure + // this.queryRange ? MetricQueryType.RANGE_QUERY : MetricQueryType.QUERY + MetricQueryType.QUERY + ), + }; + } + + private mapSeriesItemValue() { + switch (this.seriesTranslation) { + case 'mb': + return (bytes) => (bytes / 1000000).toFixed(2); + default: + return undefined; + } + } + +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html new file mode 100644 index 0000000000..c4f1872576 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html @@ -0,0 +1,30 @@ + +

{{ name$ | async }}

+
+ + + + +
Summary
+
+ + + + + + +
+ + + + + + + + + + + + + +
diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss new file mode 100644 index 0000000000..c149aedff7 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss @@ -0,0 +1,4 @@ +.metric-chart-wrapper { + height: 500px; + margin-bottom: 20px; +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts new file mode 100644 index 0000000000..32257629e4 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts @@ -0,0 +1,28 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CloudFoundryCellSummaryComponent } from './cloud-foundry-cell-summary.component'; +import { BaseTestModules } from '../../../../../test-framework/cloud-foundry-endpoint-service.helper'; + +describe('CloudFoundryCellSummaryComponent', () => { + let component: CloudFoundryCellSummaryComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CloudFoundryCellSummaryComponent], + imports: [...BaseTestModules], + providers: [ActiveRouteCfOrgSpace] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CloudFoundryCellSummaryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts new file mode 100644 index 0000000000..5a0f8f9a5f --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts @@ -0,0 +1,90 @@ +import { Component } from '@angular/core'; +import { CloudFoundryEndpointService } from '../../../services/cloud-foundry-endpoint.service'; +import { Observable } from 'rxjs'; +import { IHeaderBreadcrumb } from '../../../../../shared/components/page-header/page-header.types'; +import { map, first, startWith } from 'rxjs/operators'; +import { ActivatedRouteSnapshot, ActivatedRoute } from '@angular/router'; +import { getIdFromRoute } from '../../../cf.helpers'; +import { CardStatus } from '../../../../../shared/components/application-state/application-state.service'; +import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; +import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; +import { IMetrics, IMetricMatrixResult, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; +import { IMetricApplication } from '../../../../../store/types/metric.types'; +import { metricSchemaKey, entityFactory } from '../../../../../store/helpers/entity-factory'; + +@Component({ + selector: 'app-cloud-foundry-cell-summary', + templateUrl: './cloud-foundry-cell-summary.component.html', + styleUrls: ['./cloud-foundry-cell-summary.component.scss'], +}) +export class CloudFoundryCellSummaryComponent { + + public breadcrumbs$: Observable; + public name$: Observable; + public status$: Observable; + public cellId: string; + public entityId: string; + public entitySchema = entityFactory(metricSchemaKey); + public healthyAction: FetchCFMetricsAction; + + constructor( + public cfEndpointService: CloudFoundryEndpointService, + activatedRoute: ActivatedRoute, + entityServiceFactory: EntityServiceFactory + ) { + + this.healthyAction = new FetchCFMetricsAction( + cfEndpointService.cfGuid, + new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}), + MetricQueryType.QUERY + ); + this.entityId = this.healthyAction.metricId; + const cellHealth = entityServiceFactory.create>>( + metricSchemaKey, + this.entitySchema, + this.healthyAction.metricId, + this.healthyAction, + false + ).waitForEntity$.pipe( + map(entityInfo => entityInfo.entity) + ); + this.name$ = cellHealth.pipe( + map(entity => entity.data.result[0].metric.bosh_job_name) + ); + this.status$ = cellHealth.pipe( + map(entity => { + if (!entity.data || !entity.data.result) { + return CardStatus.NONE; + } + // TODO: RC + const health = entity.data.result[0]; + return health.value[1] === '0' ? CardStatus.OK : CardStatus.ERROR; + }) + // map(entityInfo => entityInfo.entity), + // filter(metrics => !!metrics && !!metrics.data && !!metrics.data.result), + // map(metrics => metrics.data.result) + ); + // this.status$.subscribe(status => console.log(status)); + + // this.status$ = cellHealth$.pipe( + // startWith(CardStatus.NONE) + // ); + + + this.cellId = getIdFromRoute(activatedRoute, 'cellId'); + + this.breadcrumbs$ = cfEndpointService.endpoint$.pipe( + map(endpoint => ([ + { + breadcrumbs: [ + { + value: endpoint.entity.name, + routerLink: `/cloud-foundry/${endpoint.entity.guid}/cells` + } + ] + } + ])), + first() + ); + } +} diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts index 8ab563defe..af3f5eea6c 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts @@ -1,7 +1,11 @@ import { Store } from '@ngrx/store'; import { map } from 'rxjs/operators'; -import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; +import { + FetchCFMetricsPaginatedAction, + MetricQueryConfig, + MetricQueryType, +} from '../../../../../store/actions/metrics.actions'; import { AppState } from '../../../../../store/app-state'; import { entityFactory } from '../../../../../store/helpers/entity-factory'; import { IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; @@ -17,7 +21,7 @@ export class CfCellsDataSource static cellHealthyPath = 'value.1'; constructor(store: Store, cfGuid: string, listConfig: IListConfig>) { - const action = new FetchCFMetricsAction( + const action = new FetchCFMetricsPaginatedAction( cfGuid, new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}), MetricQueryType.QUERY diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts index cd9e3db6cf..2f192be290 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts @@ -13,7 +13,7 @@ import { TableCellBooleanIndicatorComponentConfig, } from '../../list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component'; import { ITableColumn } from '../../list-table/table.types'; -import { ListViewTypes } from '../../list.component.types'; +import { IListAction, ListViewTypes } from '../../list.component.types'; import { BaseCfListConfig } from '../base-cf/base-cf-list-config'; import { CfCellsDataSource } from './cf-cells-data-source'; @@ -39,6 +39,14 @@ export class CfCellsListConfigService extends BaseCfListConfig> = { + action: (cell) => { + this.router.navigate([`cloud-foundry/${this.activeRouteCfOrgSpace.cfGuid}/cells/${cell.metric.bosh_job_id}`]); + }, + label: 'Summary', + description: `` + }; + columns: Array>> = [ { columnId: 'id', @@ -82,6 +90,7 @@ export class CfCellsListConfigService extends BaseCfListConfig [this.summaryAction]; getColumns = () => this.columns; getDataSource = () => this.dataSource; } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 89fab4e79b..d6a5999130 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -4,12 +4,7 @@ import * as moment from 'moment'; import { Subscription } from 'rxjs'; import { debounceTime, map, takeWhile, tap } from 'rxjs/operators'; -import { - FetchApplicationMetricsAction, - MetricQueryConfig, - MetricQueryType, - MetricsAction, -} from '../../../store/actions/metrics.actions'; +import { MetricQueryConfig, MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { EntityMonitor } from '../../monitors/entity-monitor'; @@ -118,6 +113,13 @@ export class MetricsChartComponent implements OnInit, OnDestroy { public showOverlayValue = false; + private newMetricsAction(action: MetricsAction, newQuery: MetricQueryConfig): MetricsAction { + return { + ...action, + query: newQuery, + }; + } + private commitDate(date: moment.Moment, type: 'start' | 'end') { const index = type === 'start' ? this.startIndex : this.endIndex; const oldDate = this.startEnd[index]; @@ -130,16 +132,21 @@ export class MetricsChartComponent implements OnInit, OnDestroy { const startUnix = start.unix(); const endUnix = end.unix(); const oldAction = this.metricsConfig.metricsAction; - const action = new FetchApplicationMetricsAction( - oldAction.guid, - oldAction.cfGuid, - new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { - start: startUnix, - end: end.unix(), - step: Math.max((endUnix - startUnix) / 200, 0) - }), - MetricQueryType.RANGE_QUERY - ); + const action = this.newMetricsAction(oldAction, new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { + start: startUnix, + end: end.unix(), + step: Math.max((endUnix - startUnix) / 200, 0) + })); + // const action = new FetchApplicationMetricsAction( + // oldAction.guid, + // oldAction.cfGuid, + // new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { + // start: startUnix, + // end: end.unix(), + // step: Math.max((endUnix - startUnix) / 200, 0) + // }), + // MetricQueryType.RANGE_QUERY + // ); this.commit = () => { this.committedStartEnd = [ @@ -174,13 +181,16 @@ export class MetricsChartComponent implements OnInit, OnDestroy { this.committedStartEnd = [null, null]; this.startEnd = [null, null]; const oldAction = this.metricsConfig.metricsAction; - const action = new FetchApplicationMetricsAction( - oldAction.guid, - oldAction.cfGuid, - new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { - window: window.value - }) - ); + const action = this.newMetricsAction(oldAction, new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { + window: window.value + })); + // const action = new FetchApplicationMetricsAction( + // oldAction.guid, + // oldAction.cfGuid, + // new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { + // window: window.value + // }) + // ); this.commitAction(action); } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 58dcd65efd..638ffc5312 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -57,16 +57,23 @@ export abstract class MetricsAction implements IRequestAction { } } -export class FetchCFMetricsAction extends MetricsAction implements PaginatedAction { +export class FetchCFMetricsAction extends MetricsAction { public cfGuid: string; constructor(cfGuid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { super(cfGuid, query, queryType); this.cfGuid = cfGuid; this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; + } +} + +export class FetchCFMetricsPaginatedAction extends FetchCFMetricsAction implements PaginatedAction { + constructor(cfGuid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { + super(cfGuid, query, queryType); this.paginationKey = this.metricId; } actions = []; paginationKey: string; + // TODO: RC Move this to DataSource initialParams = { 'order-direction': 'desc', 'order-direction-field': 'id', diff --git a/src/frontend/app/store/types/metric.types.ts b/src/frontend/app/store/types/metric.types.ts index 894486a750..5b8cf80100 100644 --- a/src/frontend/app/store/types/metric.types.ts +++ b/src/frontend/app/store/types/metric.types.ts @@ -10,3 +10,4 @@ export interface IMetricApplication { job: string; origin: string; } +// TODO: RC create IMetricCell From 3d97f43ecb8dfa14d37dcec0b11890fd6c95a4c0 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Wed, 26 Sep 2018 17:00:06 +0100 Subject: [PATCH 030/114] Split out time selector and metrics component --- .../application-instance-chart.component.html | 4 +- .../metrics-tab/metrics-tab.component.html | 12 +- .../list-pagination-controller.ts | 2 - .../metrics-chart.component.html | 39 +--- .../metrics-chart/metrics-chart.component.ts | 211 ++---------------- .../metrics-range-selector.component.html | 30 +-- .../metrics-range-selector.component.scss | 5 +- ...etrics-range-selector.component.theme.scss | 11 + .../metrics-range-selector.component.ts | 30 ++- src/frontend/sass/_all-theme.scss | 2 + 10 files changed, 87 insertions(+), 259 deletions(-) create mode 100644 src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.theme.scss diff --git a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.html b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.html index 2d35f7f03a..b6338311e6 100644 --- a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.html +++ b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.html @@ -1 +1,3 @@ - + + + \ No newline at end of file diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html index fcba09f8cc..55b9398234 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html @@ -1,15 +1,15 @@ - + - + - + \ No newline at end of file diff --git a/src/frontend/app/shared/components/list/data-sources-controllers/list-pagination-controller.ts b/src/frontend/app/shared/components/list/data-sources-controllers/list-pagination-controller.ts index efa5637832..c9c8da7e70 100644 --- a/src/frontend/app/shared/components/list/data-sources-controllers/list-pagination-controller.ts +++ b/src/frontend/app/shared/components/list/data-sources-controllers/list-pagination-controller.ts @@ -133,8 +133,6 @@ export class ListPaginationController implements IListPaginationController } } }); - - } private cloneMultiFilter(paginationClientFilter: PaginationClientFilter) { diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 073a17d679..cd6739acb2 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,42 +1,15 @@
-
-
-

Choose time window

- -
- - -
-
-
-
- +
- + {{ chartConfig.chartType }} chart type not found
diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 3ca620d951..0147f38035 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild, ContentChild, AfterContentInit } from '@angular/core'; import { Store } from '@ngrx/store'; import * as moment from 'moment'; import { Subscription } from 'rxjs'; @@ -11,6 +11,7 @@ import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.service'; import { MetricsChartTypes } from './metrics-chart.types'; import { MetricsChartManager } from './metrics.component.manager'; +import { MetricsRangeSelectorComponent } from '../metrics-range-selector/metrics-range-selector.component'; export interface MetricsConfig { metricsAction: MetricsAction; @@ -26,24 +27,12 @@ export interface MetricsChartConfig { yAxisLabel?: string; } -enum RangeType { - ROLLING_WINDOW = 'ROLLING_WINDOW', - START_END = 'START_END' -} - -interface ITimeRange { - value?: string; - label: string; - type: RangeType; -} - @Component({ selector: 'app-metrics-chart', templateUrl: './metrics-chart.component.html', styleUrls: ['./metrics-chart.component.scss'] }) -export class MetricsChartComponent implements OnInit, OnDestroy { - private committedAction: MetricsAction; +export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentInit { @Input() public metricsConfig: MetricsConfig; @Input() @@ -51,148 +40,23 @@ export class MetricsChartComponent implements OnInit, OnDestroy { @Input() public title: string; - @ViewChild('noDatesSelected') - public noDatesSelected: ElementRef; - - public chartTypes = MetricsChartTypes; + @ContentChild(MetricsRangeSelectorComponent) + public timeRangeSelector: MetricsRangeSelectorComponent; - private startEnd: [moment.Moment, moment.Moment] = [null, null]; + @Input() + set metricsAction(action: MetricsAction) { + this.commitAction(action); + } - public committedStartEnd: [moment.Moment, moment.Moment] = [null, null]; + public chartTypes = MetricsChartTypes; private pollSub: Subscription; - private initSub: Subscription; - - public commit: Function = null; - public results$; - private readonly startIndex = 0; - - private readonly endIndex = 1; - public metricsMonitor: EntityMonitor; - public rangeTypes = RangeType; - - public dateValid = false; - - public times: ITimeRange[] = [ - { - value: '5m', - label: 'The past 5 minutes', - type: RangeType.ROLLING_WINDOW - }, - { - value: '1h', - label: 'The past hour', - type: RangeType.ROLLING_WINDOW - }, - { - value: '1w', - label: 'The past week', - type: RangeType.ROLLING_WINDOW - }, - { - label: 'Set time window', - type: RangeType.START_END - } - ]; - - public selectedTimeRangeValue: ITimeRange; - - set showOverlay(show: boolean) { - this.showOverlayValue = show; - } - - get showOverlay() { - return this.showOverlayValue; - } - - public showOverlayValue = false; - - private commitDate(date: moment.Moment, type: 'start' | 'end') { - const index = type === 'start' ? this.startIndex : this.endIndex; - const oldDate = this.startEnd[index]; - if (!date.isValid() || date.isSame(oldDate)) { - return; - } - this.startEnd[index] = date; - const [start, end] = this.startEnd; - if (start && end) { - const startUnix = start.unix(); - const endUnix = end.unix(); - const oldAction = this.metricsConfig.metricsAction; - const action = new FetchApplicationMetricsAction( - oldAction.guid, - oldAction.cfGuid, - new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { - start: startUnix, - end: end.unix(), - step: Math.max((endUnix - startUnix) / 200, 0) - }), - MetricQueryType.RANGE_QUERY - ); - - this.commit = () => { - this.committedStartEnd = [ - this.startEnd[0], - this.startEnd[1] - ]; - this.commitAction(action); - }; - } - } - - get selectedTimeRange() { - return this.selectedTimeRangeValue; - } - - set selectedTimeRange(timeRange: ITimeRange) { - this.commit = null; - this.selectedTimeRangeValue = timeRange; - if (this.selectedTimeRangeValue.type === RangeType.ROLLING_WINDOW) { - this.commitWindow(this.selectedTimeRangeValue); - } else if (this.selectedTimeRangeValue.type === RangeType.START_END) { - if (!this.startEnd[0] || !this.startEnd[1]) { - this.showOverlay = true; - } - } - } - - private commitWindow(window: ITimeRange) { - if (!window) { - return; - } - this.committedStartEnd = [null, null]; - this.startEnd = [null, null]; - const oldAction = this.metricsConfig.metricsAction; - const action = new FetchApplicationMetricsAction( - oldAction.guid, - oldAction.cfGuid, - new MetricQueryConfig(this.metricsConfig.metricsAction.query.metric, { - window: window.value - }) - ); - this.commitAction(action); - } - - set start(start: moment.Moment) { - this.commitDate(start, 'start'); - } - - get start() { - return this.startEnd[this.startIndex]; - } - - set end(end: moment.Moment) { - this.commitDate(end, 'end'); - } - - get end() { - return this.startEnd[this.endIndex]; - } + private committedAction: MetricsAction; constructor( private store: Store, @@ -222,44 +86,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy { return metricsArray; } - private getInitSub(entityMonitor: EntityMonitor) { - return entityMonitor.entity$.pipe( - debounceTime(1), - tap(metrics => { - if (!this.selectedTimeRange) { - if (metrics) { - if (metrics.queryType === MetricQueryType.RANGE_QUERY) { - const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); - const end = moment.unix(parseInt(metrics.query.params.end as string, 10)); - const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); - if (isDifferent) { - this.start = start; - this.end = end; - this.committedStartEnd = [start, end]; - } - this.selectedTimeRange = this.times.find(time => time.type === RangeType.START_END); - } else { - const newWindow = metrics.query.params.window ? - this.times.find(time => time.value === metrics.query.params.window) : - this.getDefaultTimeRange(); - if (this.selectedTimeRange !== newWindow) { - this.selectedTimeRange = newWindow; - } - } - } else { - this.selectedTimeRange = this.getDefaultTimeRange(); - } - } - }), - takeWhile(metrics => !metrics) - ).subscribe(); - } - - private getDefaultTimeRange() { - return this.times.find(time => time.value === '1h') || this.times[0]; - } - ngOnInit() { + this.committedAction = this.metricsConfig.metricsAction; this.metricsMonitor = this.entityMonitorFactory.create( this.metricsConfig.metricsAction.metricId, @@ -267,8 +95,6 @@ export class MetricsChartComponent implements OnInit, OnDestroy { entityFactory(metricSchemaKey) ); - this.initSub = this.getInitSub(this.metricsMonitor); - this.results$ = this.metricsMonitor.entity$.pipe( map(metrics => { const metricsArray = this.mapMetricsToChartData(metrics, this.metricsConfig); @@ -280,6 +106,15 @@ export class MetricsChartComponent implements OnInit, OnDestroy { ); } + ngAfterContentInit() { + if (this.timeRangeSelector) { + this.timeRangeSelector.baseAction = this.metricsConfig.metricsAction; + const listener = this.timeRangeSelector.metricsAction.subscribe((action) => { + this.commitAction(action); + }); + } + } + private setup(action: MetricsAction) { if (this.pollSub) { this.pollSub.unsubscribe(); @@ -300,9 +135,6 @@ export class MetricsChartComponent implements OnInit, OnDestroy { if (this.pollSub) { this.pollSub.unsubscribe(); } - if (this.initSub) { - this.initSub.unsubscribe(); - } } private mapMetricsToChartData(metrics: IMetrics, metricsConfig: MetricsConfig) { @@ -326,6 +158,5 @@ export class MetricsChartComponent implements OnInit, OnDestroy { this.committedAction = action; this.setup(action); this.store.dispatch(action); - this.commit = null; } } diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html index 913daf7cd0..15a7abbea0 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html @@ -1,16 +1,16 @@ -
-
-
+
+
+

Choose time window

-
-
-
+
@@ -19,15 +19,17 @@

Choose time window

-
-
-
{{ committedStartEnd[0] | amDateFormat:'Do MMM YYYY, HH:mm' }} +
+
+
{{ committedStartEnd[0] | amDateFormat:'Do MMM YYYY, + HH:mm' }}
-
to
-
{{ committedStartEnd[1] | amDateFormat:'Do MMM YYYY, HH:mm' }} +
to
+
{{ committedStartEnd[1] | amDateFormat:'Do MMM YYYY, + HH:mm' }}
- No dates selected - Edit + No dates selected + Edit
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss index 83d4dd4bec..febc2ce8ab 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss @@ -1,7 +1,8 @@ @import '../../../../sass/mixins'; -.metrics-chart { +.metrics-range-selector { $animation-function: cubic-bezier(0, 0, .2, 1); $animation-time: 250ms; + position: relative; &__overlay { background-color: rgba(0, 0, 0, 0); bottom: 0; @@ -27,7 +28,7 @@ background-color: rgba(0, 0, 0, .6); transition-delay: 0s; visibility: visible; - .metrics-chart__overlay-inner { + .metrics-range-selector__overlay-inner { transform: translateY(0); } } diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.theme.scss b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.theme.scss new file mode 100644 index 0000000000..bc0be4568d --- /dev/null +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.theme.scss @@ -0,0 +1,11 @@ +@import '~@angular/material/theming'; +@mixin metrics-range-selector-theme($theme, $app-theme) { + $primary: map-get($theme, primary); + .metrics-range-selector { + &__overlay { + &-inner { + background-color: mat-contrast($primary, 500); + } + } + } +} diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts index df32012e04..5a12eeb8d6 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, Output, EventEmitter, OnDestroy, Input } from '@angular/core'; +import { Component, OnInit, Output, EventEmitter, OnDestroy, Input, ViewChild, TemplateRef } from '@angular/core'; import * as moment from 'moment'; import { FetchApplicationMetricsAction, MetricQueryConfig, MetricsAction, MetricQueryType } from '../../../store/actions/metrics.actions'; import { EntityMonitor } from '../../monitors/entity-monitor'; @@ -41,8 +41,23 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { @Output() public metricsAction = new EventEmitter(); + private baseActionValue: MetricsAction; + @Input() - public baseAction: MetricsAction; + set baseAction(action: MetricsAction) { + this.baseActionValue = action; + this.committedAction = action; + this.metricsMonitor = this.entityMonitorFactory.create( + action.metricId, + metricSchemaKey, + entityFactory(metricSchemaKey) + ); + this.initSub = this.getInitSub(this.metricsMonitor); + } + + get baseAction() { + return this.baseActionValue; + } public commit: Function = null; @@ -110,12 +125,11 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { ); this.commit = () => { - console.log('commiting'); this.committedStartEnd = [ this.startEnd[0], this.startEnd[1] ]; - this.metricsAction.emit(action); + this.commitAction(action); }; } } @@ -207,6 +221,7 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { } private commitAction(action: MetricsAction) { + this.metricsAction.emit(action); this.committedAction = action; this.commit = null; } @@ -215,14 +230,7 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { ngOnInit() { - this.committedAction = this.baseAction; - this.metricsMonitor = this.entityMonitorFactory.create( - this.baseAction.metricId, - metricSchemaKey, - entityFactory(metricSchemaKey) - ); - this.initSub = this.getInitSub(this.metricsMonitor); } ngOnDestroy() { diff --git a/src/frontend/sass/_all-theme.scss b/src/frontend/sass/_all-theme.scss index 2537cde377..5971502b1a 100644 --- a/src/frontend/sass/_all-theme.scss +++ b/src/frontend/sass/_all-theme.scss @@ -35,6 +35,7 @@ @import '../app/shared/components/list/list-cards/meta-card/meta-card-base/meta-card.component.theme'; @import '../app/shared/components/start-end-date/start-end-date.component.theme'; @import '../app/shared/components/metrics-chart/metrics-chart.component.theme'; +@import '../app/shared/components/metrics-range-selector/metrics-range-selector.component.theme'; @import './components/mat-tabs.theme'; @import './components/text-status.theme'; @import './components/hyperlinks.theme'; @@ -109,6 +110,7 @@ $side-nav-light-active: #484848; @include meta-card-component($theme, $app-theme); @include start-end-theme($theme, $app-theme); @include metrics-chart-theme($theme, $app-theme); + @include metrics-range-selector-theme($theme, $app-theme); } @function app-generate-nav-theme($theme, $nav-theme: null) { From b0f47ff9869c12ae55f71e536e1ccb5656432884 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 27 Sep 2018 10:35:03 +0100 Subject: [PATCH 031/114] Tidy up --- .../components/metrics-chart/metrics-chart.component.ts | 9 ++++----- .../metrics-range-selector.component.scss | 3 ++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 0147f38035..19186e1b9f 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,17 +1,16 @@ -import { Component, ElementRef, Input, OnDestroy, OnInit, ViewChild, ContentChild, AfterContentInit } from '@angular/core'; +import { AfterContentInit, Component, ContentChild, Input, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; -import * as moment from 'moment'; import { Subscription } from 'rxjs'; -import { debounceTime, map, takeWhile, tap } from 'rxjs/operators'; -import { FetchApplicationMetricsAction, MetricQueryConfig, MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; +import { map } from 'rxjs/operators'; +import { MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { EntityMonitor } from '../../monitors/entity-monitor'; +import { MetricsRangeSelectorComponent } from '../metrics-range-selector/metrics-range-selector.component'; import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types/base-metric.types'; import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.service'; import { MetricsChartTypes } from './metrics-chart.types'; import { MetricsChartManager } from './metrics.component.manager'; -import { MetricsRangeSelectorComponent } from '../metrics-range-selector/metrics-range-selector.component'; export interface MetricsConfig { metricsAction: MetricsAction; diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss index febc2ce8ab..bcfe44ba59 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.scss @@ -2,7 +2,7 @@ .metrics-range-selector { $animation-function: cubic-bezier(0, 0, .2, 1); $animation-time: 250ms; - position: relative; + display: flex; &__overlay { background-color: rgba(0, 0, 0, 0); bottom: 0; @@ -53,6 +53,7 @@ &__selected-range { align-items: center; display: flex; + margin-left: 10px; } &__selected-range-dates { display: none; From de7f99d092919a2a9851c97e98b2afe5041ab8a6 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 27 Sep 2018 13:53:19 +0100 Subject: [PATCH 032/114] Begining to create parent range selector --- .../application-instance-chart.component.ts | 3 +- .../metrics-chart/metrics-chart.component.ts | 11 +- ...trics-parent-range-selector.component.html | 3 + ...trics-parent-range-selector.component.scss | 0 ...cs-parent-range-selector.component.spec.ts | 25 ++++ ...metrics-parent-range-selector.component.ts | 15 ++ .../metrics-range-selector.component.html | 6 +- .../metrics-range-selector.component.ts | 141 +++++------------- ...ics-range-selector-manager.service.spec.ts | 15 ++ .../metrics-range-selector-manager.service.ts | 135 +++++++++++++++++ .../metrics-range-selector.service.spec.ts | 15 ++ .../metrics-range-selector.service.ts | 91 +++++++++++ .../services/metrics-range-selector.types.ts | 18 +++ src/frontend/app/shared/shared.module.ts | 6 +- .../app/store/actions/metrics.actions.ts | 28 ++-- .../app/store/effects/metrics.effects.ts | 4 +- .../app/store/types/base-metric.types.ts | 3 +- 17 files changed, 389 insertions(+), 130 deletions(-) create mode 100644 src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html create mode 100644 src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss create mode 100644 src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.spec.ts create mode 100644 src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts create mode 100644 src/frontend/app/shared/services/metrics-range-selector-manager.service.spec.ts create mode 100644 src/frontend/app/shared/services/metrics-range-selector-manager.service.ts create mode 100644 src/frontend/app/shared/services/metrics-range-selector.service.spec.ts create mode 100644 src/frontend/app/shared/services/metrics-range-selector.service.ts create mode 100644 src/frontend/app/shared/services/metrics-range-selector.types.ts diff --git a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts index ccc5546060..0f65b1f030 100644 --- a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts +++ b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts @@ -2,9 +2,10 @@ import { Component, OnInit, Input } from '@angular/core'; import { MetricsLineChartConfig } from '../../../../shared/components/metrics-chart/metrics-chart.types'; import { MetricsConfig } from '../../../../shared/components/metrics-chart/metrics-chart.component'; import { IMetricMatrixResult } from '../../../../store/types/base-metric.types'; -import { FetchApplicationMetricsAction, MetricQueryType, MetricQueryConfig } from '../../../../store/actions/metrics.actions'; +import { FetchApplicationMetricsAction, MetricQueryConfig } from '../../../../store/actions/metrics.actions'; import { MetricsChartHelpers } from '../../../../shared/components/metrics-chart/metrics.component.helpers'; import { IMetricApplication } from '../../../../store/types/metric.types'; +import { MetricQueryType } from '../../../../shared/services/metrics-range-selector.types'; @Component({ selector: 'app-application-instance-chart', diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 19186e1b9f..80f93845b2 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -2,7 +2,7 @@ import { AfterContentInit, Component, ContentChild, Input, OnDestroy, OnInit } f import { Store } from '@ngrx/store'; import { Subscription } from 'rxjs'; import { map } from 'rxjs/operators'; -import { MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; +import { MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { EntityMonitor } from '../../monitors/entity-monitor'; @@ -11,6 +11,7 @@ import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.service'; import { MetricsChartTypes } from './metrics-chart.types'; import { MetricsChartManager } from './metrics.component.manager'; +import { MetricQueryType } from '../../services/metrics-range-selector.types'; export interface MetricsConfig { metricsAction: MetricsAction; @@ -51,6 +52,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni private pollSub: Subscription; + private timeSelectorSub: Subscription; + public results$; public metricsMonitor: EntityMonitor; @@ -86,7 +89,6 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni } ngOnInit() { - this.committedAction = this.metricsConfig.metricsAction; this.metricsMonitor = this.entityMonitorFactory.create( this.metricsConfig.metricsAction.metricId, @@ -108,7 +110,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni ngAfterContentInit() { if (this.timeRangeSelector) { this.timeRangeSelector.baseAction = this.metricsConfig.metricsAction; - const listener = this.timeRangeSelector.metricsAction.subscribe((action) => { + this.timeSelectorSub = this.timeRangeSelector.metricsAction.subscribe((action) => { this.commitAction(action); }); } @@ -134,6 +136,9 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni if (this.pollSub) { this.pollSub.unsubscribe(); } + if (this.timeSelectorSub) { + this.timeSelectorSub.unsubscribe(); + } } private mapMetricsToChartData(metrics: IMetrics, metricsConfig: MetricsConfig) { diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html new file mode 100644 index 0000000000..fb198a6c8e --- /dev/null +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html @@ -0,0 +1,3 @@ +

+ metrics-parent-range-selector works! +

diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.spec.ts b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.spec.ts new file mode 100644 index 0000000000..30befca0a6 --- /dev/null +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MetricsParentRangeSelectorComponent } from './metrics-parent-range-selector.component'; + +describe('MetricsParentRangeSelectorComponent', () => { + let component: MetricsParentRangeSelectorComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ MetricsParentRangeSelectorComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MetricsParentRangeSelectorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts new file mode 100644 index 0000000000..7201e2d884 --- /dev/null +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts @@ -0,0 +1,15 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'app-metrics-parent-range-selector', + templateUrl: './metrics-parent-range-selector.component.html', + styleUrls: ['./metrics-parent-range-selector.component.scss'] +}) +export class MetricsParentRangeSelectorComponent implements OnInit { + + constructor() { } + + ngOnInit() { + } + +} diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html index 15a7abbea0..a8893f9dcd 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html @@ -1,5 +1,5 @@
-

Choose time window

@@ -19,7 +19,7 @@

Choose time window

-
+
{{ committedStartEnd[0] | amDateFormat:'Do MMM YYYY, HH:mm' }} @@ -30,6 +30,6 @@

Choose time window

No dates selected - Edit + Edit
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts index 5a12eeb8d6..ceee380a65 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts @@ -1,32 +1,31 @@ -import { Component, OnInit, Output, EventEmitter, OnDestroy, Input, ViewChild, TemplateRef } from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; import * as moment from 'moment'; -import { FetchApplicationMetricsAction, MetricQueryConfig, MetricsAction, MetricQueryType } from '../../../store/actions/metrics.actions'; -import { EntityMonitor } from '../../monitors/entity-monitor'; +import { Subscription } from 'rxjs'; +import { debounceTime, takeWhile, tap } from 'rxjs/operators'; +import { FetchApplicationMetricsAction, MetricsAction } from '../../../store/actions/metrics.actions'; +import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { IMetrics } from '../../../store/types/base-metric.types'; -import { debounceTime, tap, takeWhile } from 'rxjs/operators'; +import { EntityMonitor } from '../../monitors/entity-monitor'; import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; -import { metricSchemaKey, entityFactory } from '../../../store/helpers/entity-factory'; -import { Subscription } from 'rxjs'; - -enum RangeType { - ROLLING_WINDOW = 'ROLLING_WINDOW', - START_END = 'START_END' -} - -interface ITimeRange { - value?: string; - label: string; - type: RangeType; -} +import { MetricsRangeSelectorService } from '../../services/metrics-range-selector.service'; +import { ITimeRange, MetricQueryType } from '../../services/metrics-range-selector.types'; +import { MetricsRangeSelectorManagerService } from '../../services/metrics-range-selector-manager.service'; @Component({ selector: 'app-metrics-range-selector', templateUrl: './metrics-range-selector.component.html', - styleUrls: ['./metrics-range-selector.component.scss'] + styleUrls: ['./metrics-range-selector.component.scss'], + providers: [ + MetricsRangeSelectorManagerService + ] }) -export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { +export class MetricsRangeSelectorComponent implements OnDestroy { - private committedAction: MetricsAction; + constructor( + private entityMonitorFactory: EntityMonitorFactory, + private metricRangeService: MetricsRangeSelectorService, + public rangeSelectorManager: MetricsRangeSelectorManagerService + ) { } public metricsMonitor: EntityMonitor; @@ -46,12 +45,12 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { @Input() set baseAction(action: MetricsAction) { this.baseActionValue = action; - this.committedAction = action; this.metricsMonitor = this.entityMonitorFactory.create( action.metricId, metricSchemaKey, entityFactory(metricSchemaKey) ); + // this.rangeSelectorManager.init(this.metricsMonitor); this.initSub = this.getInitSub(this.metricsMonitor); } @@ -65,29 +64,9 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { public committedStartEnd: [moment.Moment, moment.Moment] = [null, null]; - public rangeTypes = RangeType; - - public times: ITimeRange[] = [ - { - value: '5m', - label: 'The past 5 minutes', - type: RangeType.ROLLING_WINDOW - }, - { - value: '1h', - label: 'The past hour', - type: RangeType.ROLLING_WINDOW - }, - { - value: '1w', - label: 'The past week', - type: RangeType.ROLLING_WINDOW - }, - { - label: 'Set time window', - type: RangeType.START_END - } - ]; + public rangeTypes = MetricQueryType; + + public times = this.metricRangeService.times; public selectedTimeRangeValue: ITimeRange; @@ -110,20 +89,7 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { this.startEnd[index] = date; const [start, end] = this.startEnd; if (start && end) { - const startUnix = start.unix(); - const endUnix = end.unix(); - const oldAction = this.baseAction; - const action = new FetchApplicationMetricsAction( - oldAction.guid, - oldAction.cfGuid, - new MetricQueryConfig(this.baseAction.query.metric, { - start: startUnix, - end: end.unix(), - step: Math.max((endUnix - startUnix) / 200, 0) - }), - MetricQueryType.RANGE_QUERY - ); - + const action = this.metricRangeService.getNewDateRangeAction(this.baseAction, start, end); this.commit = () => { this.committedStartEnd = [ this.startEnd[0], @@ -141,9 +107,9 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { set selectedTimeRange(timeRange: ITimeRange) { this.commit = null; this.selectedTimeRangeValue = timeRange; - if (this.selectedTimeRangeValue.type === RangeType.ROLLING_WINDOW) { + if (this.selectedTimeRangeValue.queryType === MetricQueryType.QUERY) { this.commitWindow(this.selectedTimeRangeValue); - } else if (this.selectedTimeRangeValue.type === RangeType.START_END) { + } else if (this.selectedTimeRangeValue.queryType === MetricQueryType.RANGE_QUERY) { if (!this.startEnd[0] || !this.startEnd[1]) { this.showOverlay = true; } @@ -155,28 +121,17 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { debounceTime(1), tap(metrics => { if (!this.selectedTimeRange) { - if (metrics) { - if (metrics.queryType === MetricQueryType.RANGE_QUERY) { - const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); - const end = moment.unix(parseInt(metrics.query.params.end as string, 10)); - const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); - if (isDifferent) { - this.start = start; - this.end = end; - this.committedStartEnd = [start, end]; - } - this.selectedTimeRange = this.times.find(time => time.type === RangeType.START_END); - } else { - const newWindow = metrics.query.params.window ? - this.times.find(time => time.value === metrics.query.params.window) : - this.getDefaultTimeRange(); - if (this.selectedTimeRange !== newWindow) { - this.selectedTimeRange = newWindow; - } + const { timeRange, start, end } = this.metricRangeService.getDateFromStoreMetric(metrics); + + if (timeRange.queryType === MetricQueryType.RANGE_QUERY) { + const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); + if (isDifferent) { + this.start = start; + this.end = end; + this.committedStartEnd = [start, end]; } - } else { - this.selectedTimeRange = this.getDefaultTimeRange(); } + this.selectedTimeRange = timeRange; } }), takeWhile(metrics => !metrics) @@ -189,15 +144,7 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { } this.committedStartEnd = [null, null]; this.startEnd = [null, null]; - const oldAction = this.baseAction; - const action = new FetchApplicationMetricsAction( - oldAction.guid, - oldAction.cfGuid, - new MetricQueryConfig(this.baseAction.query.metric, { - window: window.value - }) - ); - this.commitAction(action); + this.commitAction(this.metricRangeService.getNewTimeWindowAction(this.baseAction, window)); } set start(start: moment.Moment) { @@ -216,27 +163,13 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { return this.startEnd[this.endIndex]; } - private getDefaultTimeRange() { - return this.times.find(time => time.value === '1h') || this.times[0]; - } - private commitAction(action: MetricsAction) { this.metricsAction.emit(action); - this.committedAction = action; this.commit = null; } - constructor(private entityMonitorFactory: EntityMonitorFactory) { } - - ngOnInit() { - - - } - ngOnDestroy() { - if (this.initSub) { - this.initSub.unsubscribe(); - } + this.rangeSelectorManager.destroy(); } } diff --git a/src/frontend/app/shared/services/metrics-range-selector-manager.service.spec.ts b/src/frontend/app/shared/services/metrics-range-selector-manager.service.spec.ts new file mode 100644 index 0000000000..62108907ee --- /dev/null +++ b/src/frontend/app/shared/services/metrics-range-selector-manager.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { MetricsRangeSelectorManagerService } from './metrics-range-selector-manager.service'; + +describe('MetricsRangeSelectorManagerService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [MetricsRangeSelectorManagerService] + }); + }); + + it('should be created', inject([MetricsRangeSelectorManagerService], (service: MetricsRangeSelectorManagerService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts new file mode 100644 index 0000000000..50d58bfb25 --- /dev/null +++ b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts @@ -0,0 +1,135 @@ +import { MetricsRangeSelectorService } from './metrics-range-selector.service'; +import { MetricsAction } from './../../store/actions/metrics.actions'; +import { Injectable, EventEmitter } from '@angular/core'; + +import * as moment from 'moment'; +import { MetricQueryType, ITimeRange } from './metrics-range-selector.types'; +import { EntityMonitor } from '../monitors/entity-monitor'; +import { IMetrics } from '../../store/types/base-metric.types'; +import { Subscription } from 'rxjs'; +import { debounceTime, tap, takeWhile } from 'rxjs/operators'; + +@Injectable() +export class MetricsRangeSelectorManagerService { + + public commit: Function = null; + + public dateValid = false; + + public committedStartEnd: [moment.Moment, moment.Moment] = [null, null]; + + public rangeTypes = MetricQueryType; + + public times = this.metricRangeService.times; + + public metricsMonitor: EntityMonitor; + + private readonly startIndex = 0; + + private readonly endIndex = 1; + + private startEnd: [moment.Moment, moment.Moment] = [null, null]; + + private initSub: Subscription; + + public selectedTimeRangeValue: ITimeRange; + + public metricsActionEmitter = new EventEmitter(); + + constructor(public metricRangeService: MetricsRangeSelectorService) { } + + private commitDate(date: moment.Moment, type: 'start' | 'end') { + const index = type === 'start' ? this.startIndex : this.endIndex; + const oldDate = this.startEnd[index]; + if (!date.isValid() || date.isSame(oldDate)) { + return; + } + this.startEnd[index] = date; + const [start, end] = this.startEnd; + if (start && end) { + const action = this.metricRangeService.getNewDateRangeAction(this.baseAction, start, end); + this.commit = () => { + this.committedStartEnd = [ + this.startEnd[0], + this.startEnd[1] + ]; + this.commitAction(action); + }; + } + } + + public init(entityMonitor: EntityMonitor) { + this.initSub = entityMonitor.entity$.pipe( + debounceTime(1), + tap(metrics => { + if (!this.selectedTimeRange) { + const { timeRange, start, end } = this.metricRangeService.getDateFromStoreMetric(metrics); + + if (timeRange.queryType === MetricQueryType.RANGE_QUERY) { + const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); + if (isDifferent) { + this.start = start; + this.end = end; + this.committedStartEnd = [start, end]; + } + } + this.selectedTimeRange = timeRange; + } + }), + takeWhile(metrics => !metrics) + ).subscribe(); + } + + public destroy() { + if (this.initSub) { + this.initSub.unsubscribe(); + } + } + + get selectedTimeRange() { + return this.selectedTimeRangeValue; + } + + set selectedTimeRange(timeRange: ITimeRange) { + this.commit = null; + this.selectedTimeRangeValue = timeRange; + if (this.selectedTimeRangeValue.queryType === MetricQueryType.QUERY) { + this.commitWindow(this.selectedTimeRangeValue); + } else if (this.selectedTimeRangeValue.queryType === MetricQueryType.RANGE_QUERY) { + if (!this.startEnd[0] || !this.startEnd[1]) { + this.showOverlay = true; + } + } + } + + set start(start: moment.Moment) { + this.commitDate(start, 'start'); + } + + get start() { + return this.startEnd[this.startIndex]; + } + + set end(end: moment.Moment) { + this.commitDate(end, 'end'); + } + + get end() { + return this.startEnd[this.endIndex]; + } + + private commitWindow(window: ITimeRange) { + if (!window) { + return; + } + this.committedStartEnd = [null, null]; + this.startEnd = [null, null]; + this.commitAction(this.metricRangeService.getNewTimeWindowAction(this.baseAction, window)); + } + + private commitAction(action: MetricsAction) { + this.metricsActionEmitter.emit(action); + this.commit = null; + } + +} diff --git a/src/frontend/app/shared/services/metrics-range-selector.service.spec.ts b/src/frontend/app/shared/services/metrics-range-selector.service.spec.ts new file mode 100644 index 0000000000..f6cf5544e2 --- /dev/null +++ b/src/frontend/app/shared/services/metrics-range-selector.service.spec.ts @@ -0,0 +1,15 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { MetricsRangeSelectorService } from './metrics-range-selector.service'; + +describe('MetricsRangeSelectorService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [MetricsRangeSelectorService] + }); + }); + + it('should be created', inject([MetricsRangeSelectorService], (service: MetricsRangeSelectorService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/src/frontend/app/shared/services/metrics-range-selector.service.ts b/src/frontend/app/shared/services/metrics-range-selector.service.ts new file mode 100644 index 0000000000..dfaa32911c --- /dev/null +++ b/src/frontend/app/shared/services/metrics-range-selector.service.ts @@ -0,0 +1,91 @@ +import { Injectable } from '@angular/core'; +import * as moment from 'moment'; +import { MetricQueryConfig, MetricsAction } from '../../store/actions/metrics.actions'; +import { IMetrics } from '../../store/types/base-metric.types'; +import { ITimeRange, StoreMetricTimeRange, MetricQueryType } from './metrics-range-selector.types'; + +@Injectable() +export class MetricsRangeSelectorService { + + constructor() { } + + public times: ITimeRange[] = [ + { + value: '5m', + label: 'The past 5 minutes', + queryType: MetricQueryType.QUERY + }, + { + value: '1h', + label: 'The past hour', + queryType: MetricQueryType.QUERY + }, + { + value: '1w', + label: 'The past week', + queryType: MetricQueryType.QUERY + }, + { + label: 'Set time window', + queryType: MetricQueryType.RANGE_QUERY + } + ]; + + public getNewDateRangeAction(action: MetricsAction, start: moment.Moment, end: moment.Moment) { + const startUnix = start.unix(); + const endUnix = end.unix(); + return new MetricsAction( + action.guid, + action.endpointGuid, + new MetricQueryConfig(action.query.metric, { + start: startUnix, + end: end.unix(), + step: Math.max((endUnix - startUnix) / 200, 0) + }), + action.url, + MetricQueryType.RANGE_QUERY + ); + } + + public getNewTimeWindowAction(action: MetricsAction, window: ITimeRange) { + return new MetricsAction( + action.guid, + action.endpointGuid, + new MetricQueryConfig(action.query.metric, { + window: window.value + }), + action.url, + MetricQueryType.QUERY + ); + } + + public getDateFromStoreMetric(metrics: IMetrics, times = this.times): StoreMetricTimeRange { + if (metrics) { + if (metrics.queryType === MetricQueryType.RANGE_QUERY) { + const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); + const end = moment.unix(parseInt(metrics.query.params.end as string, 10)); + return { + timeRange: times.find(time => time.queryType === MetricQueryType.RANGE_QUERY), + start, + end + }; + } else { + return { + timeRange: metrics.query.params.window ? + times.find(time => time.value === metrics.query.params.window) : + this.getDefaultTimeRange(times) + }; + } + } else { + const timeRange = this.getDefaultTimeRange(times); + return { + timeRange + }; + } + } + + private getDefaultTimeRange(times = this.times) { + return times.find(time => time.value === '1h') || this.times[0]; + } + +} diff --git a/src/frontend/app/shared/services/metrics-range-selector.types.ts b/src/frontend/app/shared/services/metrics-range-selector.types.ts new file mode 100644 index 0000000000..d62623f9f2 --- /dev/null +++ b/src/frontend/app/shared/services/metrics-range-selector.types.ts @@ -0,0 +1,18 @@ +import * as moment from 'moment'; + +export interface ITimeRange { + value?: string; + label: string; + queryType: MetricQueryType; +} + +export interface StoreMetricTimeRange { + timeRange: ITimeRange; + start?: moment.Moment; + end?: moment.Moment; +} + +export enum MetricQueryType { + QUERY = 'query', + RANGE_QUERY = 'query_range' +} diff --git a/src/frontend/app/shared/shared.module.ts b/src/frontend/app/shared/shared.module.ts index 303f99efd5..cbf8b93fc3 100644 --- a/src/frontend/app/shared/shared.module.ts +++ b/src/frontend/app/shared/shared.module.ts @@ -137,6 +137,8 @@ import { DateTimeComponent } from './components/date-time/date-time.component'; import { StartEndDateComponent } from './components/start-end-date/start-end-date.component'; import { MomentModule } from 'ngx-moment'; import { MetricsRangeSelectorComponent } from './components/metrics-range-selector/metrics-range-selector.component'; +import { MetricsParentRangeSelectorComponent } from './components/metrics-parent-range-selector/metrics-parent-range-selector.component'; +import { MetricsRangeSelectorService } from './services/metrics-range-selector.service'; @NgModule({ imports: [ @@ -244,6 +246,7 @@ import { MetricsRangeSelectorComponent } from './components/metrics-range-select DateTimeComponent, StartEndDateComponent, MetricsRangeSelectorComponent, + MetricsParentRangeSelectorComponent, ], exports: [ FormsModule, @@ -353,7 +356,8 @@ import { MetricsRangeSelectorComponent } from './components/metrics-range-select PaginationMonitorFactory, CloudFoundryService, InternalEventMonitorFactory, - ServiceActionHelperService + ServiceActionHelperService, + MetricsRangeSelectorService ] }) export class SharedModule { } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index d5bde7ca75..b9b38b5409 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -1,15 +1,13 @@ import { Action } from '@ngrx/store'; import { environment } from './../../../environments/environment.prod'; +import { MetricQueryType } from '../../shared/services/metrics-range-selector.types'; export const METRICS_START = '[Metrics] Fetch Start'; export const METRICS_START_SUCCESS = '[Metrics] Fetch Succeeded'; export const METRICS_START_FAILED = '[Metrics] Fetch Failed'; const { proxyAPIVersion } = environment; -export enum MetricQueryType { - QUERY = 'query', - RANGE_QUERY = 'query_range' -} + export interface IMetricQueryConfigParams { window?: string; [key: string]: string | number; @@ -36,13 +34,16 @@ export class MetricQueryConfig { } } -export abstract class MetricsAction implements Action { - constructor(public guid: string, public query: MetricQueryConfig, public queryType: MetricQueryType = MetricQueryType.QUERY) { +export class MetricsAction implements Action { + constructor( + public guid: string, + public endpointGuid: string, + public query: MetricQueryConfig, + public url: string, + public queryType: MetricQueryType = MetricQueryType.QUERY) { this.metricId = MetricsAction.buildMetricKey(guid, query); } type = METRICS_START; - url: string; - cfGuid: string; metricId: string; static getBaseMetricsURL() { return `/pp/${proxyAPIVersion}/metrics`; @@ -55,17 +56,14 @@ export abstract class MetricsAction implements Action { } export class FetchCFMetricsAction extends MetricsAction { - public cfGuid: string; + public endpointGuid: string; constructor(public guid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { - super(guid, query, queryType); - this.cfGuid = guid; - this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; + super(guid, guid, query, `${MetricsAction.getBaseMetricsURL()}/cf`, queryType); } } export class FetchApplicationMetricsAction extends MetricsAction { - constructor(guid: string, public cfGuid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { - super(guid, query, queryType); - this.url = `${MetricsAction.getBaseMetricsURL()}/cf/app/${guid}`; + constructor(guid: string, cfGuid: string, query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { + super(guid, cfGuid, query, `${MetricsAction.getBaseMetricsURL()}/cf/app/${guid}`, queryType); } } diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index d6a11eee83..ea580ee0f5 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -30,10 +30,10 @@ export class MetricsEffect { } as IRequestAction; this.store.dispatch(new StartRequestAction(apiAction)); return this.httpClient.get<{ [cfguid: string]: IMetricsResponse }>(fullUrl, { - headers: { 'x-cap-cnsi-list': action.cfGuid } + headers: { 'x-cap-cnsi-list': action.endpointGuid } }).pipe( map(metrics => { - const metric = metrics[action.cfGuid]; + const metric = metrics[action.endpointGuid]; const metricObject = metric ? { [action.metricId]: { query: action.query, diff --git a/src/frontend/app/store/types/base-metric.types.ts b/src/frontend/app/store/types/base-metric.types.ts index 4e281eb18b..023ba77457 100644 --- a/src/frontend/app/store/types/base-metric.types.ts +++ b/src/frontend/app/store/types/base-metric.types.ts @@ -1,4 +1,5 @@ -import { MetricQueryConfig, MetricQueryType } from '../actions/metrics.actions'; +import { MetricQueryConfig } from '../actions/metrics.actions'; +import { MetricQueryType } from '../../shared/services/metrics-range-selector.types'; export enum MetricResultTypes { MATRIX = 'matrix', From 592d0a1ae4a0eff82a3f739a00cd7eb9d8537dab Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 27 Sep 2018 14:08:38 +0100 Subject: [PATCH 033/114] Refactor - Cell summary + chart tabs. added cell service --- .../add-space/add-space.component.ts | 4 +- .../features/cloud-foundry/cf-page.types.ts | 6 ++ .../app/features/cloud-foundry/cf.helpers.ts | 17 +++- .../cloud-foundry-tabs-base.component.ts | 10 +-- .../cloud-foundry/cloud-foundry.module.ts | 24 +++-- .../cloud-foundry/cloud-foundry.routing.ts | 17 +++- .../cf-cell-summary-chart.component.html | 0 .../cf-cell-summary-chart.component.scss | 0 .../cf-cell-summary-chart.component.spec.ts | 0 .../cf-cell-summary-chart.component.ts | 0 .../cloud-foundry-cell-base.component.html | 6 ++ .../cloud-foundry-cell-base.component.scss} | 0 .../cloud-foundry-cell-base.component.spec.ts | 30 +++++++ .../cloud-foundry-cell-base.component.ts | 68 ++++++++++++++ .../cloud-foundry-cell-charts.component.html | 12 +++ .../cloud-foundry-cell-charts.component.scss | 4 + ...loud-foundry-cell-charts.component.spec.ts | 29 ++++++ .../cloud-foundry-cell-charts.component.ts | 16 ++++ .../cloud-foundry-cell-summary.component.html | 16 ++++ .../cloud-foundry-cell-summary.component.scss | 4 + ...oud-foundry-cell-summary.component.spec.ts | 2 +- .../cloud-foundry-cell-summary.component.ts | 43 +++++++++ .../cloud-foundry-cell.service.ts | 45 ++++++++++ .../cloud-foundry-cells.component.html | 0 .../cloud-foundry-cells.component.scss | 0 .../cloud-foundry-cells.component.spec.ts | 0 .../cloud-foundry-cells.component.ts | 0 .../cloud-foundry-cell-summary.component.html | 30 ------- .../cloud-foundry-cell-summary.component.ts | 90 ------------------- .../components/list/list-table/table.types.ts | 2 +- .../cf-app-instances-config.service.ts | 11 +-- .../table-cell-cf-cell.component.html | 6 +- .../table-cell-cf-cell.component.ts | 18 ++-- .../cf-cells/cf-cells-data-source.ts | 1 + .../cf-cells/cf-cells-list-config.service.ts | 31 ++++--- .../detach-apps-list-config.service.ts | 12 +-- src/frontend/app/store/types/metric.types.ts | 11 ++- 37 files changed, 394 insertions(+), 171 deletions(-) rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells => cloud-foundry-cells}/cf-cell-summary-chart/cf-cell-summary-chart.component.html (100%) rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells => cloud-foundry-cells}/cf-cell-summary-chart/cf-cell-summary-chart.component.scss (100%) rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells => cloud-foundry-cells}/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts (100%) rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells => cloud-foundry-cells}/cf-cell-summary-chart/cf-cell-summary-chart.component.ts (100%) create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.html rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss => cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.scss} (100%) create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.spec.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.scss create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells => cloud-foundry-cells/cloud-foundry-cell}/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts (88%) create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells => cloud-foundry-cells}/cloud-foundry-cells.component.html (100%) rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells => cloud-foundry-cells}/cloud-foundry-cells.component.scss (100%) rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells => cloud-foundry-cells}/cloud-foundry-cells.component.spec.ts (100%) rename src/frontend/app/features/cloud-foundry/tabs/{cloud-foundry-feature-cells => cloud-foundry-cells}/cloud-foundry-cells.component.ts (100%) delete mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html delete mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts diff --git a/src/frontend/app/features/cloud-foundry/add-space/add-space.component.ts b/src/frontend/app/features/cloud-foundry/add-space/add-space.component.ts index 6d65f847f6..8459b05f91 100644 --- a/src/frontend/app/features/cloud-foundry/add-space/add-space.component.ts +++ b/src/frontend/app/features/cloud-foundry/add-space/add-space.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; -import { getActiveRouteCfOrgSpaceProvider } from '../cf.helpers'; + import { ActiveRouteCfOrgSpace } from '../cf-page.types'; +import { getActiveRouteCfOrgSpaceProvider } from '../cf.helpers'; @Component({ diff --git a/src/frontend/app/features/cloud-foundry/cf-page.types.ts b/src/frontend/app/features/cloud-foundry/cf-page.types.ts index 783be1f42c..9b06f4068b 100644 --- a/src/frontend/app/features/cloud-foundry/cf-page.types.ts +++ b/src/frontend/app/features/cloud-foundry/cf-page.types.ts @@ -6,3 +6,9 @@ export class ActiveRouteCfOrgSpace { orgGuid: string; spaceGuid: string; } + +@Injectable() +export class ActiveRouteCfCell { + cfGuid: string; + cellId: string; +} diff --git a/src/frontend/app/features/cloud-foundry/cf.helpers.ts b/src/frontend/app/features/cloud-foundry/cf.helpers.ts index d61d85c470..47e1ed4982 100644 --- a/src/frontend/app/features/cloud-foundry/cf.helpers.ts +++ b/src/frontend/app/features/cloud-foundry/cf.helpers.ts @@ -22,7 +22,7 @@ import { UserRoleInSpace, } from '../../store/types/user.types'; import { UserRoleLabels } from '../../store/types/users-roles.types'; -import { ActiveRouteCfOrgSpace } from './cf-page.types'; +import { ActiveRouteCfOrgSpace, ActiveRouteCfCell } from './cf-page.types'; import { ICfRolesState } from '../../store/types/current-user-roles.types'; import { getCurrentUserCFEndpointRolesState } from '../../store/selectors/current-user-roles-permissions-selectors/role.selectors'; import { EndpointModel } from '../../store/types/endpoint.types'; @@ -180,6 +180,13 @@ export function getActiveRouteCfOrgSpace(activatedRoute: ActivatedRoute) { }); } +export function getActiveRouteCfCell(activatedRoute: ActivatedRoute) { + return ({ + cfGuid: getIdFromRoute(activatedRoute, 'cfId'), + cellId: getIdFromRoute(activatedRoute, 'cellId'), + }); +} + export const getActiveRouteCfOrgSpaceProvider = { provide: ActiveRouteCfOrgSpace, useFactory: getActiveRouteCfOrgSpace, @@ -188,6 +195,14 @@ export const getActiveRouteCfOrgSpaceProvider = { ] }; +export const getActiveRouteCfCellProvider = { + provide: ActiveRouteCfCell, + useFactory: getActiveRouteCfCell, + deps: [ + ActivatedRoute, + ] +}; + export function goToAppWall(store: Store, cfGuid: string, orgGuid?: string, spaceGuid?: string) { const appWallPagKey = 'applicationWall'; store.dispatch(new SetClientFilter(applicationSchemaKey, appWallPagKey, diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts index 1ec0ed249b..63a22a0888 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts @@ -58,16 +58,16 @@ export class CloudFoundryTabsBaseComponent implements OnInit { label: 'Users', hidden: usersHidden$ }, - { - link: CloudFoundryTabsBaseComponent.firehose, - label: 'Firehose', - hidden: firehoseHidden$ - }, { link: CloudFoundryTabsBaseComponent.cells, label: 'Cells', hidden: cellsHidden$ }, + { + link: CloudFoundryTabsBaseComponent.firehose, + label: 'Firehose', + hidden: firehoseHidden$ + }, { link: 'feature-flags', label: 'Feature Flags' }, { link: 'build-packs', label: 'Build Packs' }, { link: 'stacks', label: 'Stacks' }, diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts index 13a2312586..8f29c75e0d 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts @@ -15,7 +15,7 @@ import { } from './add-organization/create-organization-step/create-organization-step.component'; import { AddSpaceComponent } from './add-space/add-space.component'; import { CreateSpaceStepComponent } from './add-space/create-space-step/create-space-step.component'; -import { ActiveRouteCfOrgSpace } from './cf-page.types'; +import { ActiveRouteCfCell, ActiveRouteCfOrgSpace } from './cf-page.types'; import { CliInfoCloudFoundryComponent } from './cli-info-cloud-foundry/cli-info-cloud-foundry.component'; import { CloudFoundryBaseComponent } from './cloud-foundry-base/cloud-foundry-base.component'; import { CloudFoundryTabsBaseComponent } from './cloud-foundry-tabs-base/cloud-foundry-tabs-base.component'; @@ -30,11 +30,18 @@ import { CloudFoundryOrganizationService } from './services/cloud-foundry-organi import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; import { CfCellSummaryChartComponent, -} from './tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component'; +} from './tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component'; +import { + CloudFoundryCellBaseComponent, +} from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component'; +import { + CloudFoundryCellChartsComponent, +} from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component'; import { CloudFoundryCellSummaryComponent, -} from './tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component'; -import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component'; +} from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component'; +import { CloudFoundryCellService } from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service'; +import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-cells/cloud-foundry-cells.component'; import { CloudFoundryFeatureFlagsComponent } from './tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component'; import { CloudFoundryFirehoseComponent } from './tabs/cloud-foundry-firehose/cloud-foundry-firehose.component'; import { @@ -98,7 +105,9 @@ import { UsersRolesComponent } from './users/manage-users/manage-users.component CloudFoundryFirehoseComponent, CloudFoundryFeatureFlagsComponent, CloudFoundryCellsComponent, + CloudFoundryCellBaseComponent, CloudFoundryCellSummaryComponent, + CloudFoundryCellChartsComponent, CfCellSummaryChartComponent, CloudFoundryBuildPacksComponent, CloudFoundryStacksComponent, @@ -135,9 +144,14 @@ import { UsersRolesComponent } from './users/manage-users/manage-users.component provide: ActiveRouteCfOrgSpace, useValue: {} }, + { + provide: ActiveRouteCfCell, + useValue: {} + }, CloudFoundryOrganizationService, CloudFoundryEndpointService, - CfRolesService + CfRolesService, + CloudFoundryCellService ], }) export class CloudFoundryModule { } diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts index b5767ad837..0b9f46ca32 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts @@ -1,3 +1,4 @@ +/* tslint:disable:max-line-length */ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; @@ -10,10 +11,16 @@ import { CloudFoundryComponent } from './cloud-foundry/cloud-foundry.component'; import { EditOrganizationComponent } from './edit-organization/edit-organization.component'; import { EditSpaceComponent } from './edit-space/edit-space.component'; import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; +import { + CloudFoundryCellBaseComponent, +} from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component'; +import { + CloudFoundryCellChartsComponent, +} from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component'; import { CloudFoundryCellSummaryComponent, -} from './tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component'; -import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component'; +} from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component'; +import { CloudFoundryCellsComponent } from './tabs/cloud-foundry-cells/cloud-foundry-cells.component'; import { CloudFoundryFeatureFlagsComponent } from './tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component'; import { CloudFoundryFirehoseComponent } from './tabs/cloud-foundry-firehose/cloud-foundry-firehose.component'; import { @@ -57,7 +64,6 @@ import { CloudFoundrySummaryTabComponent } from './tabs/cloud-foundry-summary-ta import { CloudFoundryUsersComponent } from './tabs/cloud-foundry-users/cloud-foundry-users.component'; import { UsersRolesComponent } from './users/manage-users/manage-users.component'; -/* tslint:disable:max-line-length */ /* tslint:enable:max-line-length */ const usersRoles = [ { @@ -175,6 +181,7 @@ const cloudFoundry: Routes = [{ children: [ { path: 'cells/:cellId', + component: CloudFoundryCellBaseComponent, data: { uiFullView: true }, @@ -188,6 +195,10 @@ const cloudFoundry: Routes = [{ path: 'summary', component: CloudFoundryCellSummaryComponent }, + { + path: 'charts', + component: CloudFoundryCellChartsComponent + } ] }, { diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html similarity index 100% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss similarity index 100% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts similarity index 100% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts similarity index 100% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.html new file mode 100644 index 0000000000..1ffad9bb46 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.html @@ -0,0 +1,6 @@ + +

{{ name$ | async }}

+
+ + + diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.scss similarity index 100% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.scss diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.spec.ts new file mode 100644 index 0000000000..cee3688755 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.spec.ts @@ -0,0 +1,30 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { ActiveRouteCfOrgSpace } from '../../../../cf-page.types'; +import { CloudFoundryCellBaseComponent } from './cloud-foundry-cell-base.component'; + + +describe('CloudFoundryCellBaseComponent', () => { + let component: CloudFoundryCellBaseComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CloudFoundryCellBaseComponent], + imports: [...BaseTestModules], + providers: [ActiveRouteCfOrgSpace] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CloudFoundryCellBaseComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts new file mode 100644 index 0000000000..70d38728a1 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts @@ -0,0 +1,68 @@ +import { Component } from '@angular/core'; +import { Observable } from 'rxjs'; +import { first, map } from 'rxjs/operators'; + +import { IHeaderBreadcrumb } from '../../../../../../shared/components/page-header/page-header.types'; +import { ISubHeaderTabs } from '../../../../../../shared/components/page-subheader/page-subheader.types'; +import { entityFactory, metricSchemaKey } from '../../../../../../store/helpers/entity-factory'; +import { CloudFoundryEndpointService } from '../../../../services/cloud-foundry-endpoint.service'; +import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; +import { getActiveRouteCfCellProvider } from '../../../../cf.helpers'; + +@Component({ + selector: 'app-cloud-foundry-cell-base', + templateUrl: './cloud-foundry-cell-base.component.html', + styleUrls: ['./cloud-foundry-cell-base.component.scss'], + providers: [ + getActiveRouteCfCellProvider, + CloudFoundryCellService + ] +}) +export class CloudFoundryCellBaseComponent { + + tabLinks: ISubHeaderTabs[] = [ + { + link: 'summary', + label: 'Summary' + }, + { + link: 'charts', + label: 'Charts' + }, + ]; + + public breadcrumbs$: Observable; + public name$: Observable; + public waitForEntityId: string; + public waitForEntitySchema = entityFactory(metricSchemaKey); + public cfCellService: CloudFoundryCellService; + + + constructor( + cfEndpointService: CloudFoundryEndpointService, + cfCellService: CloudFoundryCellService + ) { + + this.waitForEntityId = cfCellService.healthyAction.metricId; + this.name$ = cfCellService.healthy$.pipe( + map(entity => { + console.log(entity); + return entity.data.result[0].metric.bosh_job_name; + }) + ); + + this.breadcrumbs$ = cfEndpointService.endpoint$.pipe( + map(endpoint => ([ + { + breadcrumbs: [ + { + value: endpoint.entity.name, + routerLink: `/cloud-foundry/${endpoint.entity.guid}/cells` + } + ] + } + ])), + first() + ); + } +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html new file mode 100644 index 0000000000..d6876436d9 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.scss new file mode 100644 index 0000000000..c149aedff7 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.scss @@ -0,0 +1,4 @@ +.metric-chart-wrapper { + height: 500px; + margin-bottom: 20px; +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts new file mode 100644 index 0000000000..b45acf5239 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts @@ -0,0 +1,29 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { ActiveRouteCfOrgSpace } from '../../../../cf-page.types'; +import { CloudFoundryCellChartsComponent } from './cloud-foundry-cell-charts.component'; + +describe('CloudFoundryCellChartsComponent', () => { + let component: CloudFoundryCellChartsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CloudFoundryCellChartsComponent], + imports: [...BaseTestModules], + providers: [ActiveRouteCfOrgSpace] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CloudFoundryCellChartsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts new file mode 100644 index 0000000000..eef8223e8a --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; + +import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; + +@Component({ + selector: 'app-cloud-foundry-cell-charts', + templateUrl: './cloud-foundry-cell-charts.component.html', + styleUrls: ['./cloud-foundry-cell-charts.component.scss'], +}) +export class CloudFoundryCellChartsComponent { + + + constructor(public cfCellService: CloudFoundryCellService) { + + } +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html new file mode 100644 index 0000000000..08663e7431 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html @@ -0,0 +1,16 @@ + + + +
Summary
+
+ + + + + + +
+ diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss new file mode 100644 index 0000000000..c149aedff7 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss @@ -0,0 +1,4 @@ +.metric-chart-wrapper { + height: 500px; + margin-bottom: 20px; +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts similarity index 88% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts index 32257629e4..b97e7b4517 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts @@ -1,7 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { CloudFoundryCellSummaryComponent } from './cloud-foundry-cell-summary.component'; -import { BaseTestModules } from '../../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; describe('CloudFoundryCellSummaryComponent', () => { let component: CloudFoundryCellSummaryComponent; diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts new file mode 100644 index 0000000000..20c32cc447 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts @@ -0,0 +1,43 @@ +import { Component } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { CardStatus } from '../../../../../../shared/components/application-state/application-state.service'; +import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; + +@Component({ + selector: 'app-cloud-foundry-cell-summary', + templateUrl: './cloud-foundry-cell-summary.component.html', + styleUrls: ['./cloud-foundry-cell-summary.component.scss'], +}) +export class CloudFoundryCellSummaryComponent { + + public status$: Observable; + + constructor( + public cfCellService: CloudFoundryCellService + ) { + + this.status$ = cfCellService.healthy$.pipe( + map(entity => { + if (!entity.data || !entity.data.result) { + return CardStatus.NONE; + } + // TODO: RC + const health = entity.data.result[0]; + return health.value[1] === '0' ? CardStatus.OK : CardStatus.ERROR; + }) + // map(entityInfo => entityInfo.entity), + // filter(metrics => !!metrics && !!metrics.data && !!metrics.data.result), + // map(metrics => metrics.data.result) + ); + // this.status$.subscribe(status => console.log(status)); + + // this.status$ = cellHealth$.pipe( + // startWith(CardStatus.NONE) + // ); + + + + } +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts new file mode 100644 index 0000000000..0ef1c6e401 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; +import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; +import { entityFactory, metricSchemaKey } from '../../../../../store/helpers/entity-factory'; +import { IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; +import { IMetricCell } from '../../../../../store/types/metric.types'; +import { ActiveRouteCfCell } from '../../../cf-page.types'; + +@Injectable() +export class CloudFoundryCellService { + + cfGuid: string; + cellId: string; + healthyAction: FetchCFMetricsAction; + healthy$: Observable>>; + + constructor( + activeRouteCfCell: ActiveRouteCfCell, + entityServiceFactory: EntityServiceFactory) { + + this.cellId = activeRouteCfCell.cellId; + this.cfGuid = activeRouteCfCell.cfGuid; + + // TODO: RC limit by cell + this.healthyAction = new FetchCFMetricsAction( + this.cfGuid, + new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}), + MetricQueryType.QUERY + ); + this.healthy$ = entityServiceFactory.create>>( + metricSchemaKey, + entityFactory(metricSchemaKey), + this.healthyAction.metricId, + this.healthyAction, + false + ).waitForEntity$.pipe( + map(entityInfo => entityInfo.entity) + ); + } + + +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.html similarity index 100% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.html rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.html diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.scss similarity index 100% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.scss rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.scss diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts similarity index 100% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.spec.ts rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.ts similarity index 100% rename from src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cells.component.ts rename to src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.ts diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html deleted file mode 100644 index c4f1872576..0000000000 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html +++ /dev/null @@ -1,30 +0,0 @@ - -

{{ name$ | async }}

-
- - - - -
Summary
-
- - - - - - -
- - - - - - - - - - - - - -
diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts deleted file mode 100644 index 5a0f8f9a5f..0000000000 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-cells/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts +++ /dev/null @@ -1,90 +0,0 @@ -import { Component } from '@angular/core'; -import { CloudFoundryEndpointService } from '../../../services/cloud-foundry-endpoint.service'; -import { Observable } from 'rxjs'; -import { IHeaderBreadcrumb } from '../../../../../shared/components/page-header/page-header.types'; -import { map, first, startWith } from 'rxjs/operators'; -import { ActivatedRouteSnapshot, ActivatedRoute } from '@angular/router'; -import { getIdFromRoute } from '../../../cf.helpers'; -import { CardStatus } from '../../../../../shared/components/application-state/application-state.service'; -import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; -import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; -import { IMetrics, IMetricMatrixResult, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; -import { IMetricApplication } from '../../../../../store/types/metric.types'; -import { metricSchemaKey, entityFactory } from '../../../../../store/helpers/entity-factory'; - -@Component({ - selector: 'app-cloud-foundry-cell-summary', - templateUrl: './cloud-foundry-cell-summary.component.html', - styleUrls: ['./cloud-foundry-cell-summary.component.scss'], -}) -export class CloudFoundryCellSummaryComponent { - - public breadcrumbs$: Observable; - public name$: Observable; - public status$: Observable; - public cellId: string; - public entityId: string; - public entitySchema = entityFactory(metricSchemaKey); - public healthyAction: FetchCFMetricsAction; - - constructor( - public cfEndpointService: CloudFoundryEndpointService, - activatedRoute: ActivatedRoute, - entityServiceFactory: EntityServiceFactory - ) { - - this.healthyAction = new FetchCFMetricsAction( - cfEndpointService.cfGuid, - new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}), - MetricQueryType.QUERY - ); - this.entityId = this.healthyAction.metricId; - const cellHealth = entityServiceFactory.create>>( - metricSchemaKey, - this.entitySchema, - this.healthyAction.metricId, - this.healthyAction, - false - ).waitForEntity$.pipe( - map(entityInfo => entityInfo.entity) - ); - this.name$ = cellHealth.pipe( - map(entity => entity.data.result[0].metric.bosh_job_name) - ); - this.status$ = cellHealth.pipe( - map(entity => { - if (!entity.data || !entity.data.result) { - return CardStatus.NONE; - } - // TODO: RC - const health = entity.data.result[0]; - return health.value[1] === '0' ? CardStatus.OK : CardStatus.ERROR; - }) - // map(entityInfo => entityInfo.entity), - // filter(metrics => !!metrics && !!metrics.data && !!metrics.data.result), - // map(metrics => metrics.data.result) - ); - // this.status$.subscribe(status => console.log(status)); - - // this.status$ = cellHealth$.pipe( - // startWith(CardStatus.NONE) - // ); - - - this.cellId = getIdFromRoute(activatedRoute, 'cellId'); - - this.breadcrumbs$ = cfEndpointService.endpoint$.pipe( - map(endpoint => ([ - { - breadcrumbs: [ - { - value: endpoint.entity.name, - routerLink: `/cloud-foundry/${endpoint.entity.guid}/cells` - } - ] - } - ])), - first() - ); - } -} diff --git a/src/frontend/app/shared/components/list/list-table/table.types.ts b/src/frontend/app/shared/components/list/list-table/table.types.ts index 6e34ab8641..cdf7a50d6e 100644 --- a/src/frontend/app/shared/components/list/list-table/table.types.ts +++ b/src/frontend/app/shared/components/list/list-table/table.types.ts @@ -12,7 +12,7 @@ export interface ICellDefinition { getValue?: (row: T) => string; // Should the value of getLink be used in a href or routerLink externalLink?: boolean; - // Automatically turns the row into a link + // Automatically turns the cell into a link getLink?: (row: T) => string; newTab?: boolean; } 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 2e99a67ff1..41e4cbb8ae 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 @@ -70,7 +70,7 @@ export class CfAppInstancesConfigService implements IListConfig type: 'sort', orderKey: 'memory', field: 'usage.mem' - }, cellFlex: '3' + }, cellFlex: '2' }, { columnId: 'disk', headerCell: () => 'Disk', @@ -85,7 +85,7 @@ export class CfAppInstancesConfigService implements IListConfig type: 'sort', orderKey: 'disk', field: 'usage.disk' - }, cellFlex: '3' + }, cellFlex: '2' }, { columnId: 'cpu', headerCell: () => 'CPU', @@ -109,7 +109,7 @@ export class CfAppInstancesConfigService implements IListConfig type: 'sort', orderKey: 'uptime', field: 'value.stats.uptime' - }, cellFlex: '5' + }, cellFlex: '3' } ]; cfCellColumn: ITableColumn = { @@ -119,7 +119,7 @@ export class CfAppInstancesConfigService implements IListConfig metricResults$: null }, cellComponent: TableCellCfCellComponent, - cellFlex: '1' + cellFlex: '3' }; viewType = ListViewTypes.TABLE_ONLY; @@ -193,7 +193,8 @@ export class CfAppInstancesConfigService implements IListConfig if (hasMetrics) { this.columns.splice(1, 0, this.cfCellColumn); this.cfCellColumn.cellConfig = { - metricResults$: this.createMetricsResults(entityServiceFactory) + metricResults$: this.createMetricsResults(entityServiceFactory), + cfGuid: this.appService.cfGuid }; } return true; diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html index 7b04287a78..2675dc5630 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html @@ -1 +1,5 @@ -{{ cell$ | async }} + + Deployment: {{cellMetric.bosh_deployment}}
+ Name: {{cellMetric.bosh_job_name}}
+ Id: {{cellMetric.bosh_job_id}} +
diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts index 6beb312690..c767de7cb4 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts @@ -1,6 +1,6 @@ import { Component, Input } from '@angular/core'; import { Observable } from 'rxjs'; -import { filter, map } from 'rxjs/operators'; +import { filter, map, tap } from 'rxjs/operators'; import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; import { IMetricApplication } from '../../../../../../store/types/metric.types'; @@ -14,17 +14,25 @@ import { ListAppInstance } from '../app-instance-types'; }) export class TableCellCfCellComponent extends TableCellCustom { - cell$: Observable; + cellMetric$: Observable; + cellLink: string; @Input('config') - set config(config: { metricResults$: Observable[]> }) { + set config(config: { + metricResults$: Observable[]> + cfGuid: string + }) { if (!config) { return; } - this.cell$ = config.metricResults$.pipe( + const { metricResults$, cfGuid } = config; + + this.cellMetric$ = metricResults$.pipe( filter(metricResults => !!metricResults[this.row.index]), - map(metricResults => metricResults[this.row.index].metric.bosh_job_id) + map(metricResults => metricResults[this.row.index].metric), + tap(metric => this.cellLink = `/cloud-foundry/${cfGuid}/cells/${metric.bosh_job_id}/summary`) ); + } } diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts index af3f5eea6c..8b23af7c3e 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts @@ -19,6 +19,7 @@ export class CfCellsDataSource static cellIdPath = 'metric.bosh_job_id'; static cellNamePath = 'metric.bosh_job_name'; static cellHealthyPath = 'value.1'; + static cellDeploymentPath = 'metric.bosh_deployment'; constructor(store: Store, cfGuid: string, listConfig: IListConfig>) { const action = new FetchCFMetricsPaginatedAction( diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts index 2f192be290..16d86b078b 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts @@ -20,7 +20,6 @@ import { CfCellsDataSource } from './cf-cells-data-source'; @Injectable() export class CfCellsListConfigService extends BaseCfListConfig> { - dataSource: CfCellsDataSource; defaultView = 'table' as ListView; viewType = ListViewTypes.TABLE_ONLY; @@ -39,14 +38,6 @@ export class CfCellsListConfigService extends BaseCfListConfig> = { - action: (cell) => { - this.router.navigate([`cloud-foundry/${this.activeRouteCfOrgSpace.cfGuid}/cells/${cell.metric.bosh_job_id}`]); - }, - label: 'Summary', - description: `` - }; - columns: Array>> = [ { columnId: 'id', @@ -62,7 +53,9 @@ export class CfCellsListConfigService extends BaseCfListConfig 'Name', cellDefinition: { - valuePath: CfCellsDataSource.cellNamePath + valuePath: CfCellsDataSource.cellNamePath, + getLink: (row: IMetricVectorResult) => + `/cloud-foundry/${this.activeRouteCfOrgSpace.cfGuid}/cells/${row.metric.bosh_job_id}/summary` }, cellFlex: '1', sort: { @@ -71,6 +64,19 @@ export class CfCellsListConfigService extends BaseCfListConfig 'Deployment', + cellDefinition: { + valuePath: CfCellsDataSource.cellDeploymentPath + }, + cellFlex: '1', + sort: { + type: 'sort', + orderKey: 'deployment', + field: CfCellsDataSource.cellDeploymentPath + } + }, { columnId: 'healthy', headerCell: () => 'Healthy', @@ -85,12 +91,11 @@ export class CfCellsListConfigService extends BaseCfListConfig, private activeRouteCfOrgSpace: ActiveRouteCfOrgSpace, private router: Router) { + constructor(store: Store, private activeRouteCfOrgSpace: ActiveRouteCfOrgSpace) { super(); - this.dataSource = new CfCellsDataSource(this.store, activeRouteCfOrgSpace.cfGuid, this); + this.dataSource = new CfCellsDataSource(store, activeRouteCfOrgSpace.cfGuid, this); } - getSingleActions = () => [this.summaryAction]; getColumns = () => this.columns; getDataSource = () => this.dataSource; } diff --git a/src/frontend/app/shared/components/list/list-types/detach-apps/detach-apps-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/detach-apps/detach-apps-list-config.service.ts index b65e96df53..88a4e297df 100644 --- a/src/frontend/app/shared/components/list/list-types/detach-apps/detach-apps-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/detach-apps/detach-apps-list-config.service.ts @@ -1,19 +1,15 @@ +import { DatePipe } from '@angular/common'; import { Injectable } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; import { Store } from '@ngrx/store'; -import { - CloudFoundryOrganizationService, -} from '../../../../../features/cloud-foundry/services/cloud-foundry-organization.service'; +import { IServiceBinding } from '../../../../../core/cf-api-svc.types'; import { ListView } from '../../../../../store/actions/list.actions'; import { AppState } from '../../../../../store/app-state'; import { APIResource } from '../../../../../store/types/api.types'; -import { IListConfig, ListViewTypes } from '../../list.component.types'; import { ITableColumn } from '../../list-table/table.types'; -import { ISpace } from '../../../../../core/cf-api.types'; +import { IListConfig, ListViewTypes } from '../../list.component.types'; import { DetachAppsDataSource } from './detach-apps-data-source'; -import { IServiceBinding } from '../../../../../core/cf-api-svc.types'; -import { ActivatedRoute } from '@angular/router'; -import { DatePipe } from '@angular/common'; @Injectable() export class DetachAppsListConfigService implements IListConfig { diff --git a/src/frontend/app/store/types/metric.types.ts b/src/frontend/app/store/types/metric.types.ts index 5b8cf80100..368e93b659 100644 --- a/src/frontend/app/store/types/metric.types.ts +++ b/src/frontend/app/store/types/metric.types.ts @@ -10,4 +10,13 @@ export interface IMetricApplication { job: string; origin: string; } -// TODO: RC create IMetricCell +// TODO: RC search and replace for all cell stuff +export interface IMetricCell { + __name__: string; + bosh_deployment: string; + bosh_job_id: string; + bosh_job_name: string; + instance: string; + job: string; + origin: string; +} From f4face38ec8c24d422b94c31bcc7cd6588ae9021 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 27 Sep 2018 16:12:07 +0100 Subject: [PATCH 034/114] Finished range-selector-manager --- .../metrics-range-selector.component.html | 27 ++-- .../metrics-range-selector.component.ts | 137 ++++-------------- .../metrics-range-selector-manager.service.ts | 30 ++-- 3 files changed, 62 insertions(+), 132 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html index a8893f9dcd..c550d484b0 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html @@ -1,35 +1,38 @@ -
-
+

Choose time window

- +
- +
- - + + {{time.label}} -
-
-
{{ committedStartEnd[0] | amDateFormat:'Do MMM YYYY, +
+
+
{{ rangeSelectorManager.committedStartEnd[0] | + amDateFormat:'Do MMM YYYY, HH:mm' }}
to
-
{{ committedStartEnd[1] | amDateFormat:'Do MMM YYYY, +
{{ rangeSelectorManager.committedStartEnd[1] | + amDateFormat:'Do MMM YYYY, HH:mm' }}
No dates selected - Edit + Edit
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts index ceee380a65..7a64890520 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts @@ -1,15 +1,12 @@ import { Component, EventEmitter, Input, OnDestroy, Output } from '@angular/core'; -import * as moment from 'moment'; import { Subscription } from 'rxjs'; -import { debounceTime, takeWhile, tap } from 'rxjs/operators'; import { FetchApplicationMetricsAction, MetricsAction } from '../../../store/actions/metrics.actions'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { IMetrics } from '../../../store/types/base-metric.types'; import { EntityMonitor } from '../../monitors/entity-monitor'; import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; -import { MetricsRangeSelectorService } from '../../services/metrics-range-selector.service'; -import { ITimeRange, MetricQueryType } from '../../services/metrics-range-selector.types'; import { MetricsRangeSelectorManagerService } from '../../services/metrics-range-selector-manager.service'; +import { MetricQueryType } from '../../services/metrics-range-selector.types'; @Component({ selector: 'app-metrics-range-selector', @@ -20,22 +17,33 @@ import { MetricsRangeSelectorManagerService } from '../../services/metrics-range ] }) export class MetricsRangeSelectorComponent implements OnDestroy { + private rangeSelectorSub: Subscription; + actionSub: Subscription; constructor( private entityMonitorFactory: EntityMonitorFactory, - private metricRangeService: MetricsRangeSelectorService, public rangeSelectorManager: MetricsRangeSelectorManagerService - ) { } + ) { + this.rangeSelectorSub = this.rangeSelectorManager.timeWindow$.subscribe(selectedTimeRangeValue => { - public metricsMonitor: EntityMonitor; - - private readonly startIndex = 0; + if (selectedTimeRangeValue.queryType === MetricQueryType.RANGE_QUERY) { + console.log(selectedTimeRangeValue) + if (!this.rangeSelectorManager.startEnd[0] || !this.rangeSelectorManager.startEnd[1]) { + this.showOverlay = true; + } + } + }); + this.actionSub = this.rangeSelectorManager.metricsAction$.subscribe(newAction => { + if (newAction) { + this.commitAction(newAction); + } + }); + } - private readonly endIndex = 1; + public metricsMonitor: EntityMonitor; - private startEnd: [moment.Moment, moment.Moment] = [null, null]; + public rangeTypes = MetricQueryType; - private initSub: Subscription; @Output() public metricsAction = new EventEmitter(); @@ -50,25 +58,13 @@ export class MetricsRangeSelectorComponent implements OnDestroy { metricSchemaKey, entityFactory(metricSchemaKey) ); - // this.rangeSelectorManager.init(this.metricsMonitor); - this.initSub = this.getInitSub(this.metricsMonitor); + this.rangeSelectorManager.init(this.metricsMonitor, action); } get baseAction() { return this.baseActionValue; } - public commit: Function = null; - - public dateValid = false; - - public committedStartEnd: [moment.Moment, moment.Moment] = [null, null]; - - public rangeTypes = MetricQueryType; - - public times = this.metricRangeService.times; - - public selectedTimeRangeValue: ITimeRange; set showOverlay(show: boolean) { this.showOverlayValue = show; @@ -80,96 +76,19 @@ export class MetricsRangeSelectorComponent implements OnDestroy { public showOverlayValue = false; - private commitDate(date: moment.Moment, type: 'start' | 'end') { - const index = type === 'start' ? this.startIndex : this.endIndex; - const oldDate = this.startEnd[index]; - if (!date.isValid() || date.isSame(oldDate)) { - return; - } - this.startEnd[index] = date; - const [start, end] = this.startEnd; - if (start && end) { - const action = this.metricRangeService.getNewDateRangeAction(this.baseAction, start, end); - this.commit = () => { - this.committedStartEnd = [ - this.startEnd[0], - this.startEnd[1] - ]; - this.commitAction(action); - }; - } - } - - get selectedTimeRange() { - return this.selectedTimeRangeValue; - } - - set selectedTimeRange(timeRange: ITimeRange) { - this.commit = null; - this.selectedTimeRangeValue = timeRange; - if (this.selectedTimeRangeValue.queryType === MetricQueryType.QUERY) { - this.commitWindow(this.selectedTimeRangeValue); - } else if (this.selectedTimeRangeValue.queryType === MetricQueryType.RANGE_QUERY) { - if (!this.startEnd[0] || !this.startEnd[1]) { - this.showOverlay = true; - } - } - } - - private getInitSub(entityMonitor: EntityMonitor) { - return entityMonitor.entity$.pipe( - debounceTime(1), - tap(metrics => { - if (!this.selectedTimeRange) { - const { timeRange, start, end } = this.metricRangeService.getDateFromStoreMetric(metrics); - - if (timeRange.queryType === MetricQueryType.RANGE_QUERY) { - const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); - if (isDifferent) { - this.start = start; - this.end = end; - this.committedStartEnd = [start, end]; - } - } - this.selectedTimeRange = timeRange; - } - }), - takeWhile(metrics => !metrics) - ).subscribe(); + private commitAction(action: MetricsAction) { + this.metricsAction.emit(action); } - private commitWindow(window: ITimeRange) { - if (!window) { - return; + private tidyUp() { + this.rangeSelectorManager.destroy(); + if (this.rangeSelectorSub) { + this.rangeSelectorSub.unsubscribe(); } - this.committedStartEnd = [null, null]; - this.startEnd = [null, null]; - this.commitAction(this.metricRangeService.getNewTimeWindowAction(this.baseAction, window)); - } - - set start(start: moment.Moment) { - this.commitDate(start, 'start'); - } - - get start() { - return this.startEnd[this.startIndex]; - } - - set end(end: moment.Moment) { - this.commitDate(end, 'end'); - } - - get end() { - return this.startEnd[this.endIndex]; - } - - private commitAction(action: MetricsAction) { - this.metricsAction.emit(action); - this.commit = null; } ngOnDestroy() { - this.rangeSelectorManager.destroy(); + this.tidyUp(); } } diff --git a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts index 50d58bfb25..09f81492bb 100644 --- a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts @@ -6,12 +6,14 @@ import * as moment from 'moment'; import { MetricQueryType, ITimeRange } from './metrics-range-selector.types'; import { EntityMonitor } from '../monitors/entity-monitor'; import { IMetrics } from '../../store/types/base-metric.types'; -import { Subscription } from 'rxjs'; +import { Subscription, Subject } from 'rxjs'; import { debounceTime, tap, takeWhile } from 'rxjs/operators'; @Injectable() export class MetricsRangeSelectorManagerService { + public timeWindow$ = new Subject(); + public commit: Function = null; public dateValid = false; @@ -28,13 +30,15 @@ export class MetricsRangeSelectorManagerService { private readonly endIndex = 1; - private startEnd: [moment.Moment, moment.Moment] = [null, null]; + public startEnd: [moment.Moment, moment.Moment] = [null, null]; private initSub: Subscription; public selectedTimeRangeValue: ITimeRange; - public metricsActionEmitter = new EventEmitter(); + public metricsAction$ = new Subject(); + + private baseAction: MetricsAction; constructor(public metricRangeService: MetricsRangeSelectorService) { } @@ -58,7 +62,8 @@ export class MetricsRangeSelectorManagerService { } } - public init(entityMonitor: EntityMonitor) { + public init(entityMonitor: EntityMonitor, baseAction: MetricsAction) { + this.baseAction = baseAction; this.initSub = entityMonitor.entity$.pipe( debounceTime(1), tap(metrics => { @@ -92,18 +97,19 @@ export class MetricsRangeSelectorManagerService { set selectedTimeRange(timeRange: ITimeRange) { this.commit = null; + this.start = null; + this.end = null; this.selectedTimeRangeValue = timeRange; + this.timeWindow$.next(this.selectedTimeRangeValue); if (this.selectedTimeRangeValue.queryType === MetricQueryType.QUERY) { this.commitWindow(this.selectedTimeRangeValue); - } else if (this.selectedTimeRangeValue.queryType === MetricQueryType.RANGE_QUERY) { - if (!this.startEnd[0] || !this.startEnd[1]) { - this.showOverlay = true; - } } } set start(start: moment.Moment) { - this.commitDate(start, 'start'); + if (start) { + this.commitDate(start, 'start'); + } } get start() { @@ -111,7 +117,9 @@ export class MetricsRangeSelectorManagerService { } set end(end: moment.Moment) { - this.commitDate(end, 'end'); + if (end) { + this.commitDate(end, 'end'); + } } get end() { @@ -128,7 +136,7 @@ export class MetricsRangeSelectorManagerService { } private commitAction(action: MetricsAction) { - this.metricsActionEmitter.emit(action); + this.metricsAction$.next(action); this.commit = null; } From e8b2623a4f278851b516bbbfd6311577d952afd3 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 27 Sep 2018 16:37:26 +0100 Subject: [PATCH 035/114] Cell summary tab improvements --- .../cloud-foundry-cell-base.component.ts | 10 +-- .../cloud-foundry-cell-summary.component.html | 39 ++++++--- .../cloud-foundry-cell-summary.component.scss | 16 ++++ .../cloud-foundry-cell-summary.component.ts | 19 +---- .../cloud-foundry-cell.service.ts | 83 ++++++++++++++++--- .../app/store/actions/metrics.actions.ts | 10 +++ src/frontend/app/store/types/metric.types.ts | 1 + 7 files changed, 135 insertions(+), 43 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts index 70d38728a1..789c6d927c 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts @@ -43,11 +43,11 @@ export class CloudFoundryCellBaseComponent { cfCellService: CloudFoundryCellService ) { - this.waitForEntityId = cfCellService.healthyAction.metricId; - this.name$ = cfCellService.healthy$.pipe( - map(entity => { - console.log(entity); - return entity.data.result[0].metric.bosh_job_name; + this.waitForEntityId = cfCellService.healthyMetricId; + this.name$ = cfCellService.cellMetric$.pipe( + map(metric => { + console.log(metric); + return metric.bosh_job_name; }) ); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html index 08663e7431..56944f3e28 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html @@ -1,16 +1,35 @@ - -
Summary
-
- - - - +
- + + + + + + + - --> + diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss index c149aedff7..76efc96bd6 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss @@ -1,4 +1,20 @@ .metric-chart-wrapper { height: 500px; +} + +mat-card { margin-bottom: 20px; } + +.app-metadata { + display: flex; + flex-direction: row; + + &__two-cols { + flex: 1; + + app-metadata-item:first-child { + margin-top: 0; + } + } +} diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts index 20c32cc447..557975668b 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts @@ -19,25 +19,12 @@ export class CloudFoundryCellSummaryComponent { ) { this.status$ = cfCellService.healthy$.pipe( - map(entity => { - if (!entity.data || !entity.data.result) { + map(health => { + if (health === undefined) { return CardStatus.NONE; } - // TODO: RC - const health = entity.data.result[0]; - return health.value[1] === '0' ? CardStatus.OK : CardStatus.ERROR; + return health === '0' ? CardStatus.OK : CardStatus.ERROR; }) - // map(entityInfo => entityInfo.entity), - // filter(metrics => !!metrics && !!metrics.data && !!metrics.data.result), - // map(metrics => metrics.data.result) ); - // this.status$.subscribe(status => console.log(status)); - - // this.status$ = cellHealth$.pipe( - // startWith(CardStatus.NONE) - // ); - - - } } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index 0ef1c6e401..4574be6df4 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -1,45 +1,104 @@ import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; -import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; +import { FetchCFCellMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; import { entityFactory, metricSchemaKey } from '../../../../../store/helpers/entity-factory'; import { IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../store/types/metric.types'; import { ActiveRouteCfCell } from '../../../cf-page.types'; +export const enum CellMetrics { + HEALTHY = 'firehose_value_metric_rep_unhealthy_cell', + REMAINING_CONTAINERS = 'firehose_value_metric_rep_capacity_remaining_containers', + REMAINING_DISK = 'firehose_value_metric_rep_capacity_remaining_disk', + REMAINING_MEMORY = 'firehose_value_metric_rep_capacity_remaining_memory', + TOTAL_CONTAINERS = 'firehose_value_metric_rep_capacity_total_containers', + TOTAL_DISK = 'firehose_value_metric_rep_capacity_total_disk', + TOTAL_MEMORY = 'firehose_value_metric_rep_capacity_total_memory', + CPUS = 'firehose_value_metric_rep_num_cpus' +} + @Injectable() export class CloudFoundryCellService { cfGuid: string; cellId: string; - healthyAction: FetchCFMetricsAction; - healthy$: Observable>>; + cellMetric$: Observable; + + healthy$: Observable; + healthyMetricId: string; + cpus$: Observable; + + usageContainers$: Observable; + remainingContainers$: Observable; + totalContainers$: Observable; + + usageDisk$: Observable; + remainingDisk$: Observable; + totalDisk$: Observable; + + usageMemory$: Observable; + remainingMemory$: Observable; + totalMemory$: Observable; constructor( activeRouteCfCell: ActiveRouteCfCell, - entityServiceFactory: EntityServiceFactory) { + private entityServiceFactory: EntityServiceFactory) { this.cellId = activeRouteCfCell.cellId; this.cfGuid = activeRouteCfCell.cfGuid; - // TODO: RC limit by cell - this.healthyAction = new FetchCFMetricsAction( + this.healthy$ = this.generate(CellMetrics.HEALTHY); + this.remainingContainers$ = this.generate(CellMetrics.REMAINING_CONTAINERS); + this.totalContainers$ = this.generate(CellMetrics.TOTAL_CONTAINERS); + this.remainingDisk$ = this.generate(CellMetrics.REMAINING_DISK); + this.totalDisk$ = this.generate(CellMetrics.TOTAL_DISK); + this.remainingMemory$ = this.generate(CellMetrics.REMAINING_MEMORY); + this.totalMemory$ = this.generate(CellMetrics.TOTAL_MEMORY); + this.cpus$ = this.generate(CellMetrics.CPUS); + + this.usageContainers$ = this.generateUsage(this.remainingContainers$, this.totalContainers$); + this.usageDisk$ = this.generateUsage(this.remainingDisk$, this.totalDisk$); + this.usageMemory$ = this.generateUsage(this.remainingMemory$, this.totalMemory$); + + this.cellMetric$ = this.generate(CellMetrics.HEALTHY, true); + + } + + + private generate(metric: CellMetrics, isMetric = false): Observable { + const action = new FetchCFCellMetricsAction( this.cfGuid, - new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}), + this.cellId, + new MetricQueryConfig(metric, { bosh_job_id: this.cellId }), MetricQueryType.QUERY ); - this.healthy$ = entityServiceFactory.create>>( + if (metric === CellMetrics.HEALTHY) { + this.healthyMetricId = action.metricId; + } + return this.entityServiceFactory.create>>( metricSchemaKey, entityFactory(metricSchemaKey), - this.healthyAction.metricId, - this.healthyAction, + action.metricId, + action, false ).waitForEntity$.pipe( - map(entityInfo => entityInfo.entity) + map(entityInfo => entityInfo.entity), + map(entity => { + if (!entity.data || !entity.data.result) { + return undefined; + } + return isMetric ? entity.data.result[0].metric : entity.data.result[0].value[1]; + }) ); } + private generateUsage(remaining$: Observable, total$: Observable): Observable { + return combineLatest([remaining$, total$]).pipe( + map(([remaining, total]) => Number(total) - Number(remaining)) + ); + } } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 638ffc5312..3dba3715f4 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -53,6 +53,7 @@ export abstract class MetricsAction implements IRequestAction { // Builds the key that is used to store the metric in the app state. static buildMetricKey(guid: string, query: MetricQueryConfig) { + // TODO: RC add params return `${guid}:${query.metric}`; } } @@ -66,6 +67,15 @@ export class FetchCFMetricsAction extends MetricsAction { } } +export class FetchCFCellMetricsAction extends MetricsAction { + public cfGuid: string; + constructor(cfGuid: string, cellId: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { + super(cfGuid + '-' + cellId, query, queryType); + this.cfGuid = cfGuid; + this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; + } +} + export class FetchCFMetricsPaginatedAction extends FetchCFMetricsAction implements PaginatedAction { constructor(cfGuid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { super(cfGuid, query, queryType); diff --git a/src/frontend/app/store/types/metric.types.ts b/src/frontend/app/store/types/metric.types.ts index 368e93b659..097ebfb441 100644 --- a/src/frontend/app/store/types/metric.types.ts +++ b/src/frontend/app/store/types/metric.types.ts @@ -16,6 +16,7 @@ export interface IMetricCell { bosh_deployment: string; bosh_job_id: string; bosh_job_name: string; + environment: string; instance: string; job: string; origin: string; From 416accbb17ac0df82dbeb745a9078722beb8ea32 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Thu, 27 Sep 2018 17:32:00 +0100 Subject: [PATCH 036/114] Wire in manager to new parent time selector --- .../home/home/home-page.component.html | 7 ++- .../features/home/home/home-page.component.ts | 43 +++++++++++++-- ...trics-parent-range-selector.component.html | 44 ++++++++++++++- ...metrics-parent-range-selector.component.ts | 55 +++++++++++++++++-- .../metrics-range-selector.component.ts | 2 - src/frontend/app/shared/shared.module.ts | 1 + .../app/store/actions/metrics.actions.ts | 32 ++++++----- .../app/store/effects/metrics.effects.ts | 5 +- 8 files changed, 156 insertions(+), 33 deletions(-) diff --git a/src/frontend/app/features/home/home/home-page.component.html b/src/frontend/app/features/home/home/home-page.component.html index f11b6c5970..669a8ce5d0 100644 --- a/src/frontend/app/features/home/home/home-page.component.html +++ b/src/frontend/app/features/home/home/home-page.component.html @@ -1,4 +1,9 @@

Dashboard

- \ No newline at end of file + + + + + + \ No newline at end of file diff --git a/src/frontend/app/features/home/home/home-page.component.ts b/src/frontend/app/features/home/home/home-page.component.ts index 69eda66492..315787196b 100644 --- a/src/frontend/app/features/home/home/home-page.component.ts +++ b/src/frontend/app/features/home/home/home-page.component.ts @@ -1,7 +1,13 @@ +import { MetricsAction } from './../../../store/actions/metrics.actions'; import { Component, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { FetchApplicationMetricsAction, MetricQueryConfig } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; +import { MetricsChartHelpers } from '../../../shared/components/metrics-chart/metrics.component.helpers'; +import { MetricsConfig } from '../../../shared/components/metrics-chart/metrics-chart.component'; +import { IMetricMatrixResult } from '../../../store/types/base-metric.types'; +import { IMetricApplication } from '../../../store/types/metric.types'; +import { MetricsLineChartConfig } from '../../../shared/components/metrics-chart/metrics-chart.types'; @Component({ selector: 'app-home-page', @@ -9,12 +15,37 @@ import { AppState } from '../../../store/app-state'; styleUrls: ['./home-page.component.scss'] }) export class HomePageComponent implements OnInit { + instanceChartConfig: MetricsLineChartConfig; + + constructor(private store: Store) { + this.instanceChartConfig = this.buildChartConfig(); + } + + instanceMetricConfig: MetricsConfig> = { + getSeriesName: result => `Instance ${result.metric.instance_index}`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + sort: MetricsChartHelpers.sortBySeriesName, + mapSeriesItemValue: this.mapSeriesItemValue(), + metricsAction: new FetchApplicationMetricsAction( + 'fbb2e26f-491f-468c-8d5b-ed02028f7106', + 'rqljU7j5TF-v8_nyozXsd6kDUeU', + new MetricQueryConfig('firehose_container_metric_cpu_percentage') + ) + }; + + private mapSeriesItemValue() { + return (bytes) => (bytes / 1000000).toFixed(2); + } + + + private buildChartConfig() { + const lineChartConfig = new MetricsLineChartConfig(); + lineChartConfig.xAxisLabel = 'Time'; + lineChartConfig.yAxisLabel = 'test'; + return lineChartConfig; + } + + - constructor(private store: Store) { } - public metricsAction = new FetchApplicationMetricsAction( - 'fbb2e26f-491f-468c-8d5b-ed02028f7106', - 'rqljU7j5TF-v8_nyozXsd6kDUeU', - new MetricQueryConfig('firehose_container_metric_cpu_percentage') - ); ngOnInit() { } } diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html index fb198a6c8e..e359678be2 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html @@ -1,3 +1,41 @@ -

- metrics-parent-range-selector works! -

+
+
+
+
+

Choose time window

+ +
+ + +
+
+
+
+ + + + {{time.label}} + + + +
+
+
{{ rangeSelectorManager.committedStartEnd[0] | + amDateFormat:'Do MMM YYYY, + HH:mm' }} +
+
to
+
{{ rangeSelectorManager.committedStartEnd[1] | + amDateFormat:'Do MMM YYYY, + HH:mm' }} +
+
+ No dates selected + Edit +
+
+ +
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts index 7201e2d884..bb64ecfd6b 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts @@ -1,13 +1,60 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, QueryList, ViewChildren, AfterViewInit } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; +import { MetricsRangeSelectorManagerService } from '../../services/metrics-range-selector-manager.service'; +import { MetricsChartComponent } from '../metrics-chart/metrics-chart.component'; +import { MetricQueryType } from '../../services/metrics-range-selector.types'; +import { metricSchemaKey, entityFactory } from '../../../store/helpers/entity-factory'; +import { IMetrics } from '../../../store/types/base-metric.types'; @Component({ selector: 'app-metrics-parent-range-selector', templateUrl: './metrics-parent-range-selector.component.html', - styleUrls: ['./metrics-parent-range-selector.component.scss'] + styleUrls: ['./metrics-parent-range-selector.component.scss'], + providers: [ + MetricsRangeSelectorManagerService + ] }) -export class MetricsParentRangeSelectorComponent implements OnInit { +export class MetricsParentRangeSelectorComponent implements OnInit, AfterViewInit { + private actionSub: Subscription; + + @ViewChildren(MetricsChartComponent) + private metricsCharts: QueryList; + + public rangeTypes = MetricQueryType; + + constructor( + private entityMonitorFactory: EntityMonitorFactory, + public rangeSelectorManager: MetricsRangeSelectorManagerService + ) { + + } + + ngAfterViewInit() { + const action = this.metricsCharts.first.metricsConfig.metricsAction; + const metricsMonitor = this.entityMonitorFactory.create( + action.metricId, + metricSchemaKey, + entityFactory(metricSchemaKey) + ); + this.rangeSelectorManager.init(metricsMonitor, action); + this.actionSub = this.rangeSelectorManager.metricsAction$.subscribe(newAction => { + if (newAction) { + this.metricsCharts.forEach(chart => { + const oldAction = chart.metricsConfig.metricsAction; + chart.metricsAction = { + ...oldAction, + query: { + ...oldAction.query, + params: newAction.query.params + }, + queryType: newAction.queryType + }; + }); + } + }); + } - constructor() { } ngOnInit() { } diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts index 7a64890520..5c0799fb7a 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts @@ -25,9 +25,7 @@ export class MetricsRangeSelectorComponent implements OnDestroy { public rangeSelectorManager: MetricsRangeSelectorManagerService ) { this.rangeSelectorSub = this.rangeSelectorManager.timeWindow$.subscribe(selectedTimeRangeValue => { - if (selectedTimeRangeValue.queryType === MetricQueryType.RANGE_QUERY) { - console.log(selectedTimeRangeValue) if (!this.rangeSelectorManager.startEnd[0] || !this.rangeSelectorManager.startEnd[1]) { this.showOverlay = true; } diff --git a/src/frontend/app/shared/shared.module.ts b/src/frontend/app/shared/shared.module.ts index cbf8b93fc3..6bd44c01d7 100644 --- a/src/frontend/app/shared/shared.module.ts +++ b/src/frontend/app/shared/shared.module.ts @@ -339,6 +339,7 @@ import { MetricsRangeSelectorService } from './services/metrics-range-selector.s DateTimeComponent, StartEndDateComponent, MetricsRangeSelectorComponent, + MetricsParentRangeSelectorComponent ], entryComponents: [ AppEventDetailDialogComponentComponent, diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index b9b38b5409..a33410bc07 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -12,26 +12,28 @@ export interface IMetricQueryConfigParams { window?: string; [key: string]: string | number; } + +function joinParams(queryConfig: MetricQueryConfig) { + const { + window = '', + ...params + } = queryConfig.params; + const windowString = window ? `{}[${window}]` : ''; + const paramString = Object.keys(params).reduce((accum, key) => { + return accum + `&${key}=${params[key]}`; + }, ''); + return windowString + paramString || ''; +} + +export function getFullMetricQueryQuery(queryConfig: MetricQueryConfig) { + return queryConfig.metric + joinParams(queryConfig); +} + export class MetricQueryConfig { constructor( public metric: string, public params?: IMetricQueryConfigParams ) { } - public getFullQuery() { - return this.metric + this.joinParams(); - } - - public joinParams() { - const { - window = '', - ...params - } = this.params; - const windowString = window ? `{}[${window}]` : ''; - const paramString = Object.keys(params).reduce((accum, key) => { - return accum + `&${key}=${params[key]}`; - }, ''); - return windowString + paramString || ''; - } } export class MetricsAction implements Action { diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index ea580ee0f5..e7c5207cb4 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -1,3 +1,4 @@ +import { query } from '@angular/animations'; import { catchError, mergeMap, map } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; @@ -5,7 +6,7 @@ import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; import { Store } from '@ngrx/store'; -import { METRICS_START, MetricsAction } from '../actions/metrics.actions'; +import { METRICS_START, MetricsAction, getFullMetricQueryQuery } from '../actions/metrics.actions'; import { AppState } from '../app-state'; import { metricSchemaKey } from '../helpers/entity-factory'; import { IMetricsResponse } from '../types/base-metric.types'; @@ -63,7 +64,7 @@ export class MetricsEffect { })); private buildFullUrl(action: MetricsAction) { - return `${action.url}/${action.queryType}?query=${action.query.getFullQuery()}`; + return `${action.url}/${action.queryType}?query=${getFullMetricQueryQuery(action.query)}`; } } From 989b3da0f5d53bfef379024593a9779f98ed511f Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 27 Sep 2018 17:42:17 +0100 Subject: [PATCH 037/114] Fix after update --- .../cf-cell-summary-chart.component.html | 4 +- .../cf-cell-summary-chart.component.ts | 5 +- .../cloud-foundry-cell.service.ts | 10 ++-- .../metrics-range-selector.component.ts | 48 +++++++++---------- .../app/store/actions/metrics.actions.ts | 24 ++++++++-- 5 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html index 2d35f7f03a..4680d74f91 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html @@ -1 +1,3 @@ - + + + diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts index 919945a453..1c431f048b 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts @@ -4,7 +4,7 @@ import { MetricsConfig } from '../../../../../shared/components/metrics-chart/me import { IMetricMatrixResult } from '../../../../../store/types/base-metric.types'; import { IMetricApplication } from '../../../../../store/types/metric.types'; import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; -import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; +import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType, FetchCFCellMetricsAction } from '../../../../../store/actions/metrics.actions'; @Component({ @@ -56,8 +56,9 @@ export class CfCellSummaryChartComponent implements OnInit { mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, sort: MetricsChartHelpers.sortBySeriesName, // mapSeriesItemValue: this.mapSeriesItemValue(), - metricsAction: new FetchCFMetricsAction( + metricsAction: new FetchCFCellMetricsAction( this.endpointGuid, + this.cellId, new MetricQueryConfig(this.queryString), // TODO: RC MetricQueryType.RANGE_QUERY causes failure // this.queryRange ? MetricQueryType.RANGE_QUERY : MetricQueryType.QUERY diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index 4574be6df4..dcccb27467 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -3,7 +3,7 @@ import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; -import { FetchCFCellMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; +import { FetchCFCellMetricAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; import { entityFactory, metricSchemaKey } from '../../../../../store/helpers/entity-factory'; import { IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../store/types/metric.types'; @@ -69,7 +69,7 @@ export class CloudFoundryCellService { private generate(metric: CellMetrics, isMetric = false): Observable { - const action = new FetchCFCellMetricsAction( + const action = new FetchCFCellMetricAction( this.cfGuid, this.cellId, new MetricQueryConfig(metric, { bosh_job_id: this.cellId }), @@ -90,7 +90,11 @@ export class CloudFoundryCellService { if (!entity.data || !entity.data.result) { return undefined; } - return isMetric ? entity.data.result[0].metric : entity.data.result[0].value[1]; + if (isMetric) { + return entity.data.result[0].metric; + } + console.log(metric, entity.data.result[0]); + return entity.data.result[0].value[1]; }) ); } diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts index 5a12eeb8d6..d12c5d1775 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts @@ -1,12 +1,13 @@ -import { Component, OnInit, Output, EventEmitter, OnDestroy, Input, ViewChild, TemplateRef } from '@angular/core'; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import * as moment from 'moment'; -import { FetchApplicationMetricsAction, MetricQueryConfig, MetricsAction, MetricQueryType } from '../../../store/actions/metrics.actions'; -import { EntityMonitor } from '../../monitors/entity-monitor'; +import { Subscription } from 'rxjs'; +import { debounceTime, takeWhile, tap } from 'rxjs/operators'; + +import { MetricQueryConfig, MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; +import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { IMetrics } from '../../../store/types/base-metric.types'; -import { debounceTime, tap, takeWhile } from 'rxjs/operators'; +import { EntityMonitor } from '../../monitors/entity-monitor'; import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; -import { metricSchemaKey, entityFactory } from '../../../store/helpers/entity-factory'; -import { Subscription } from 'rxjs'; enum RangeType { ROLLING_WINDOW = 'ROLLING_WINDOW', @@ -39,7 +40,7 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { private initSub: Subscription; @Output() - public metricsAction = new EventEmitter(); + public metricsAction = new EventEmitter(); private baseActionValue: MetricsAction; @@ -101,6 +102,14 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { public showOverlayValue = false; + private newMetricsAction(action: MetricsAction, newQuery: MetricQueryConfig, queryType: MetricQueryType): MetricsAction { + return { + ...action, + query: newQuery, + queryType + }; + } + private commitDate(date: moment.Moment, type: 'start' | 'end') { const index = type === 'start' ? this.startIndex : this.endIndex; const oldDate = this.startEnd[index]; @@ -113,16 +122,11 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { const startUnix = start.unix(); const endUnix = end.unix(); const oldAction = this.baseAction; - const action = new FetchApplicationMetricsAction( - oldAction.guid, - oldAction.cfGuid, - new MetricQueryConfig(this.baseAction.query.metric, { - start: startUnix, - end: end.unix(), - step: Math.max((endUnix - startUnix) / 200, 0) - }), - MetricQueryType.RANGE_QUERY - ); + const action = this.newMetricsAction(oldAction, new MetricQueryConfig(this.baseAction.query.metric, { + start: startUnix, + end: end.unix(), + step: Math.max((endUnix - startUnix) / 200, 0) + }), MetricQueryType.RANGE_QUERY); this.commit = () => { this.committedStartEnd = [ @@ -190,13 +194,9 @@ export class MetricsRangeSelectorComponent implements OnInit, OnDestroy { this.committedStartEnd = [null, null]; this.startEnd = [null, null]; const oldAction = this.baseAction; - const action = new FetchApplicationMetricsAction( - oldAction.guid, - oldAction.cfGuid, - new MetricQueryConfig(this.baseAction.query.metric, { - window: window.value - }) - ); + const action = this.newMetricsAction(oldAction, new MetricQueryConfig(this.baseAction.query.metric, { + window: window.value + }), MetricQueryType.QUERY); this.commitAction(action); } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 3dba3715f4..83b3d04b20 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -10,7 +10,9 @@ const { proxyAPIVersion } = environment; export enum MetricQueryType { QUERY = 'query', - RANGE_QUERY = 'query_range' + RANGE_QUERY = 'query_range', + // Response contains a single value instead of a series of values + VALUE = 'value' } export interface IMetricQueryConfigParams { window?: string; @@ -40,7 +42,7 @@ export class MetricQueryConfig { export abstract class MetricsAction implements IRequestAction { constructor(public guid: string, public query: MetricQueryConfig, public queryType: MetricQueryType = MetricQueryType.QUERY) { - this.metricId = MetricsAction.buildMetricKey(guid, query); + this.metricId = MetricsAction.buildMetricKey(guid, query, queryType); } entityKey = metricSchemaKey; type = METRICS_START; @@ -52,9 +54,10 @@ export abstract class MetricsAction implements IRequestAction { } // Builds the key that is used to store the metric in the app state. - static buildMetricKey(guid: string, query: MetricQueryConfig) { - // TODO: RC add params - return `${guid}:${query.metric}`; + static buildMetricKey(guid: string, query: MetricQueryConfig, queryType: MetricQueryType) { + // TODO: RC add params? + const valueOrSeries = queryType === MetricQueryType.VALUE ? 'value' : 'series'; + return `${guid}:${query.metric}:${valueOrSeries}`; } } @@ -67,9 +70,20 @@ export class FetchCFMetricsAction extends MetricsAction { } } +export class FetchCFCellMetricAction extends MetricsAction { + public cfGuid: string; + constructor(cfGuid: string, cellId: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { + // TODO: RC metric id needs to be unique per cell... and if it's a series or single value + super(cfGuid + '-' + cellId + '-value', query, queryType); + this.cfGuid = cfGuid; + this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; + } +} + export class FetchCFCellMetricsAction extends MetricsAction { public cfGuid: string; constructor(cfGuid: string, cellId: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { + // TODO: RC metric id needs to be unique per cell... and if it's a series or single value super(cfGuid + '-' + cellId, query, queryType); this.cfGuid = cfGuid; this.url = `${MetricsAction.getBaseMetricsURL()}/cf`; From 14ec642499b2f01cfcaabd9320e3aa2a070d4c36 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 28 Sep 2018 10:58:38 +0100 Subject: [PATCH 038/114] Optionally remove legend, more cell summary info & better icons --- .../cf-cell-summary-chart.component.ts | 11 ++++++++--- .../cloud-foundry-cell-charts.component.html | 14 ++++++++++---- .../cloud-foundry-cell-summary.component.html | 15 +++++++++------ .../metrics-chart/metrics-chart.component.html | 2 +- .../metrics-chart/metrics-chart.component.ts | 10 ++-------- .../metrics-chart/metrics-chart.types.ts | 4 ++++ 6 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts index 1c431f048b..2364796e03 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts @@ -1,10 +1,11 @@ import { Component, Input, OnInit } from '@angular/core'; -import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types'; + import { MetricsConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.component'; +import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types'; +import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; +import { FetchCFCellMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; import { IMetricMatrixResult } from '../../../../../store/types/base-metric.types'; import { IMetricApplication } from '../../../../../store/types/metric.types'; -import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; -import { FetchCFMetricsAction, MetricQueryConfig, MetricQueryType, FetchCFCellMetricsAction } from '../../../../../store/actions/metrics.actions'; @Component({ @@ -23,6 +24,9 @@ export class CfCellSummaryChartComponent implements OnInit { @Input() private yAxisLabel: string; + @Input() + private showLegend = true; + // Prometheus query string @Input() private queryString: string; @@ -46,6 +50,7 @@ export class CfCellSummaryChartComponent implements OnInit { const lineChartConfig = new MetricsLineChartConfig(); lineChartConfig.xAxisLabel = 'Time'; lineChartConfig.yAxisLabel = this.yAxisLabel; + lineChartConfig.showLegend = this.showLegend; return lineChartConfig; } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html index d6876436d9..5228d3865d 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html @@ -1,12 +1,18 @@ - + - + - + - + \ No newline at end of file diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html index 56944f3e28..49c5e7ecc4 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html @@ -2,12 +2,15 @@ @@ -23,13 +26,13 @@ - - + - + - + \ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index cd6739acb2..205dab6b85 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -7,7 +7,7 @@

{{title}}

mode="indeterminate">
- diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 6b20ac1485..b4fc5f84ce 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -10,7 +10,7 @@ import { EntityMonitor } from '../../monitors/entity-monitor'; import { MetricsRangeSelectorComponent } from '../metrics-range-selector/metrics-range-selector.component'; import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types/base-metric.types'; import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.service'; -import { MetricsChartTypes } from './metrics-chart.types'; +import { MetricsChartTypes, IMetricsChartConfig } from './metrics-chart.types'; import { MetricsChartManager } from './metrics.component.manager'; export interface MetricsConfig { @@ -20,12 +20,6 @@ export interface MetricsConfig { mapSeriesItemValue?: (value) => any; sort?: (a: ChartSeries, b: ChartSeries) => number; } -export interface MetricsChartConfig { - // Make an enum for this. - chartType: MetricsChartTypes; - xAxisLabel?: string; - yAxisLabel?: string; -} @Component({ selector: 'app-metrics-chart', @@ -36,7 +30,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni @Input() public metricsConfig: MetricsConfig; @Input() - public chartConfig: MetricsChartConfig; + public chartConfig: IMetricsChartConfig; @Input() public title: string; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.types.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.types.ts index 395d7ae85f..e41b534eae 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.types.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.types.ts @@ -13,10 +13,14 @@ export enum MetricsChartTypes { export interface IMetricsChartConfig { chartType: MetricsChartTypes; + showLegend?: boolean; + xAxisLabel?: string; + yAxisLabel?: string; } export class MetricsLineChartConfig implements IMetricsChartConfig { chartType = MetricsChartTypes.LINE; + showLegend = true; xAxisLabel?: string; yAxisLabel?: string; } From 00e91527e91543d21e63a97ffb938fe03e0bf54b Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 28 Sep 2018 12:07:23 +0100 Subject: [PATCH 039/114] Metrics: Add Prometheus Job Information --- .../features/endpoints/endpoint-helpers.ts | 25 +++++- .../metrics/metrics/metrics.component.html | 60 ++++++++----- .../metrics/metrics/metrics.component.scss | 39 ++++---- .../metrics/metrics/metrics.component.ts | 90 ++++++++++++++++--- .../app/store/actions/metrics.actions.ts | 9 ++ .../app/store/effects/metrics.effects.ts | 2 +- .../app/store/types/endpoint.types.ts | 3 +- 7 files changed, 176 insertions(+), 52 deletions(-) diff --git a/src/frontend/app/features/endpoints/endpoint-helpers.ts b/src/frontend/app/features/endpoints/endpoint-helpers.ts index 43d460173d..5ea24a3390 100644 --- a/src/frontend/app/features/endpoints/endpoint-helpers.ts +++ b/src/frontend/app/features/endpoints/endpoint-helpers.ts @@ -16,13 +16,22 @@ export interface EndpointTypeHelper { label: string; urlValidation?: string; allowTokenSharing?: boolean; + icon?: string; + iconFont?: string; +} + +export interface EndpointIcon { + name: string; + font: string; } const endpointTypes: EndpointTypeHelper[] = [ { value: 'cf', label: 'Cloud Foundry', - urlValidation: urlValidationExpression + urlValidation: urlValidationExpression, + icon: 'cloud_foundry', + iconFont: 'stratos-icons' }, { value: 'metrics', @@ -49,3 +58,17 @@ export function getCanShareTokenForEndpointType(type: string): boolean { export function getEndpointTypes() { return endpointTypes; } + +export function getIconForEndpoint(type: string): EndpointIcon { + const icon = { + name: 'endpoint', + font: '' + }; + + const ep = endpointTypesMap[type]; + if (ep && ep.icon) { + icon.name = ep.icon; + icon.font = ep.iconFont; + } + return icon; +} diff --git a/src/frontend/app/features/metrics/metrics/metrics.component.html b/src/frontend/app/features/metrics/metrics/metrics.component.html index afa410d989..fc774421fa 100644 --- a/src/frontend/app/features/metrics/metrics/metrics.component.html +++ b/src/frontend/app/features/metrics/metrics/metrics.component.html @@ -1,30 +1,44 @@ -{{ (metricsEndpoint$ | async)?.provider.name }} +{{ (metricsEndpoint$ | async)?.entity.provider.name }} - - -
- equalizer -
-
{{ ep.provider.name }}
-

- {{ ep.provider.token_endpoint }} -

+
+ + +
+ equalizer +
+
{{ ep.entity.provider.name }}
+

+ {{ ep.entity.provider.token_endpoint }} +

+
-
-
-

Provides metrics for the following endpoints:

-
-
+ + + +
+

Provides metrics for the following endpoints:

+
+ +
+
+ +
- settings_ethernet -
+ {{ ep.metadata[svc.guid].icon.name }} +
-
-
+ +
- - +
+
\ No newline at end of file diff --git a/src/frontend/app/features/metrics/metrics/metrics.component.scss b/src/frontend/app/features/metrics/metrics/metrics.component.scss index ef30d5658c..c52defe4f0 100644 --- a/src/frontend/app/features/metrics/metrics/metrics.component.scss +++ b/src/frontend/app/features/metrics/metrics/metrics.component.scss @@ -1,18 +1,25 @@ -.metrics-url { - font-size: 14px; -} - -.metrics-info { - align-items: center; - display: flex; - mat-icon { - font-size: 48px; - height: 48px; - margin-right: 8px; - width: 48px; +.metrics { + &-url { + font-size: 14px; + } + &-endpoint { + padding-bottom: 24px; + } + &-info { + display: flex; + mat-icon { + font-size: 48px; + height: 48px; + margin-right: 8px; + width: 48px; + } + } + &-metadata { + div { + padding: 4px 0; + } + .metrics-extra { + padding: 0; + } } -} - -.metrics-detail { - margin-left: 56px; } diff --git a/src/frontend/app/features/metrics/metrics/metrics.component.ts b/src/frontend/app/features/metrics/metrics/metrics.component.ts index b85bc1310b..31d60db4a3 100644 --- a/src/frontend/app/features/metrics/metrics/metrics.component.ts +++ b/src/frontend/app/features/metrics/metrics/metrics.component.ts @@ -1,12 +1,41 @@ import { Component, OnInit } from '@angular/core'; import { MetricsService, MetricsEndpointProvider } from '../services/metrics-service'; -import { getNameForEndpointType } from '../../endpoints/endpoint-helpers'; +import { getNameForEndpointType, getIconForEndpoint, EndpointIcon } from '../../endpoints/endpoint-helpers'; import { Observable } from 'rxjs'; import { ActivatedRoute } from '@angular/router'; import { getIdFromRoute } from '../../cloud-foundry/cf.helpers'; -import { map, first } from 'rxjs/operators'; +import { map, filter, first } from 'rxjs/operators'; import { IHeaderBreadcrumb } from '../../../shared/components/page-header/page-header.types'; +import { Store } from '@ngrx/store'; +import { AppState } from '../../../store/app-state'; +import { FetchMetricsAction } from '../../../store/actions/metrics.actions'; +import { EntityMonitorFactory } from '../../../shared/monitors/entity-monitor.factory.service'; +import { IMetrics } from '../../../store/types/base-metric.types'; +import { metricSchemaKey, entityFactory } from '../../../store/helpers/entity-factory'; + +interface EndpointMetadata { + type: string; + icon: EndpointIcon; +} +interface MetricsInfo { + entity: MetricsEndpointProvider; + metadata: { + [guid: string]: EndpointMetadata; + }; +} + +interface PrometheusJobDetail { + name: string; + health: string; + lastError: string; + lastScrape: string; +} + +interface PrometheusJobs { + [guid: string]: PrometheusJobDetail; +} + @Component({ selector: 'app-metrics', templateUrl: './metrics.component.html', @@ -16,19 +45,40 @@ export class MetricsComponent { getNameForEndpointType = getNameForEndpointType; - public metricsEndpoint$: Observable; + public metricsEndpoint$: Observable; public breadcrumbs$: Observable; + public jobDetails$: Observable; - constructor(private activatedRoute: ActivatedRoute, private metricsService: MetricsService) { + constructor( + private activatedRoute: ActivatedRoute, + private metricsService: MetricsService, + private store: Store, + private entityMonitorFactory: EntityMonitorFactory + ) { - const metricsGuid = getIdFromRoute(activatedRoute, 'metricsId'); + const metricsGuid = getIdFromRoute(this.activatedRoute, 'metricsId'); + const metricsAction = new FetchMetricsAction(metricsGuid, 'targets'); + this.store.dispatch(metricsAction); - this.metricsEndpoint$ = metricsService.metricsEndpoints$.pipe( - map((ep) => ep.find((item) => item.provider.guid === metricsGuid)) - ); + this.metricsEndpoint$ = this.metricsService.metricsEndpoints$.pipe( + map((ep) => ep.find((item) => item.provider.guid === metricsGuid)), + map((ep) => { + const metadata = {}; + ep.endpoints.forEach(endpoint => { + metadata[endpoint.guid] = { + type: getNameForEndpointType(endpoint.cnsi_type), + icon: getIconForEndpoint(endpoint.cnsi_type) + }; + }); + return { + entity: ep, + metadata: metadata + }; + } + )); this.breadcrumbs$ = this.metricsEndpoint$.pipe( - map(endpoint => ([ + map(() => ([ { breadcrumbs: [ { @@ -40,6 +90,26 @@ export class MetricsComponent { ])), first() ); - } + const metricsMonitor = this.entityMonitorFactory.create( + metricsAction.metricId, + metricSchemaKey, + entityFactory(metricSchemaKey) + ); + + this.jobDetails$ = metricsMonitor.entity$.pipe( + filter(a => !!a), + map((targetsData: any) => { + const mapped = {}; + if (targetsData.activeTargets) { + targetsData.activeTargets.forEach(t => { + if (t.labels && t.labels.job) { + mapped[t.labels.job] = t; + } + }); + } + return mapped; + }) + ); + } } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index c919c26444..0ff70f3387 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -17,6 +17,7 @@ export abstract class MetricsAction implements Action { guid: string; cfGuid: string; metricId: string; + directApi = false; static getBaseMetricsURL() { return `/pp/${proxyAPIVersion}/metrics`; } @@ -27,6 +28,14 @@ export abstract class MetricsAction implements Action { } } +export class FetchMetricsAction extends MetricsAction { + constructor(public guid: string, public query: string) { + super(guid, query); + this.url = `/pp/${proxyAPIVersion}/proxy/api/v1/` + query; + this.directApi = true; + this.cfGuid = guid; + } +} export class FetchCFMetricsAction extends MetricsAction { public cfGuid: string; constructor(public guid: string, public query: string) { diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index 36dba5da8c..703894a411 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -23,7 +23,7 @@ export class MetricsEffect { @Effect() metrics$ = this.actions$.ofType(METRICS_START).pipe( mergeMap(action => { - const fullUrl = this.buildFullUrl(action); + const fullUrl = action.directApi ? action.url : this.buildFullUrl(action); const apiAction = { guid: action.guid, entityKey: metricSchemaKey diff --git a/src/frontend/app/store/types/endpoint.types.ts b/src/frontend/app/store/types/endpoint.types.ts index 64510a6ff7..feb05f1ca9 100644 --- a/src/frontend/app/store/types/endpoint.types.ts +++ b/src/frontend/app/store/types/endpoint.types.ts @@ -41,7 +41,8 @@ export interface EndpointModel { token_endpoint?: string; user?: EndpointUser; metadata?: { - metrics: string + metrics?: string; + metrics_job?: string; }; system_shared_token: boolean; sso_allowed: boolean; From 34640de03a7a014a26ea46bf7b3db047848cd8b1 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 28 Sep 2018 13:26:47 +0100 Subject: [PATCH 040/114] Tidy ups --- .../cf-cell-summary-chart.component.html | 4 ++-- .../cf-cell-summary-chart.component.ts | 19 ++++----------- .../cloud-foundry-cell-base.component.ts | 5 +--- .../cloud-foundry-cell-summary.component.html | 1 - .../cloud-foundry-cell.service.ts | 6 +---- .../table-cell-cf-cell.component.ts | 6 ++--- .../cf-cells/cf-cells-data-source.ts | 6 ++--- .../cf-cells/cf-cells-list-config.service.ts | 23 +++++++++---------- .../app/store/actions/metrics.actions.ts | 1 - src/frontend/app/store/types/metric.types.ts | 6 ++--- 10 files changed, 29 insertions(+), 48 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html index 4680d74f91..524bf7983c 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html @@ -1,3 +1,3 @@ - + - + \ No newline at end of file diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts index 2364796e03..6e2ab961c7 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts @@ -5,7 +5,7 @@ import { MetricsLineChartConfig } from '../../../../../shared/components/metrics import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; import { FetchCFCellMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; import { IMetricMatrixResult } from '../../../../../store/types/base-metric.types'; -import { IMetricApplication } from '../../../../../store/types/metric.types'; +import { IMetricCell } from '../../../../../store/types/metric.types'; @Component({ @@ -40,9 +40,9 @@ export class CfCellSummaryChartComponent implements OnInit { @Input() public title: string; - public instanceChartConfig: MetricsLineChartConfig; + public chartConfig: MetricsLineChartConfig; - public instanceMetricConfig: MetricsConfig>; + public metricConfig: MetricsConfig>; constructor() { } @@ -55,8 +55,8 @@ export class CfCellSummaryChartComponent implements OnInit { } ngOnInit() { - this.instanceChartConfig = this.buildChartConfig(); - this.instanceMetricConfig = { + this.chartConfig = this.buildChartConfig(); + this.metricConfig = { getSeriesName: result => `Cell ${result.metric.bosh_job_id}`, mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, sort: MetricsChartHelpers.sortBySeriesName, @@ -72,13 +72,4 @@ export class CfCellSummaryChartComponent implements OnInit { }; } - private mapSeriesItemValue() { - switch (this.seriesTranslation) { - case 'mb': - return (bytes) => (bytes / 1000000).toFixed(2); - default: - return undefined; - } - } - } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts index 789c6d927c..c73b8ed78a 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts @@ -45,10 +45,7 @@ export class CloudFoundryCellBaseComponent { this.waitForEntityId = cfCellService.healthyMetricId; this.name$ = cfCellService.cellMetric$.pipe( - map(metric => { - console.log(metric); - return metric.bosh_job_name; - }) + map(metric => metric.bosh_job_name) ); this.breadcrumbs$ = cfEndpointService.endpoint$.pipe( diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html index 49c5e7ecc4..54261bf1d5 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html @@ -30,7 +30,6 @@ - diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index dcccb27467..6719d5c878 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -90,11 +90,7 @@ export class CloudFoundryCellService { if (!entity.data || !entity.data.result) { return undefined; } - if (isMetric) { - return entity.data.result[0].metric; - } - console.log(metric, entity.data.result[0]); - return entity.data.result[0].value[1]; + return isMetric ? entity.data.result[0].metric : entity.data.result[0].value[1]; }) ); } diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts index c767de7cb4..e9fc2f589c 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts @@ -3,7 +3,7 @@ import { Observable } from 'rxjs'; import { filter, map, tap } from 'rxjs/operators'; import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; -import { IMetricApplication } from '../../../../../../store/types/metric.types'; +import { IMetricCell } from '../../../../../../store/types/metric.types'; import { TableCellCustom } from '../../../list.types'; import { ListAppInstance } from '../app-instance-types'; @@ -14,12 +14,12 @@ import { ListAppInstance } from '../app-instance-types'; }) export class TableCellCfCellComponent extends TableCellCustom { - cellMetric$: Observable; + cellMetric$: Observable; cellLink: string; @Input('config') set config(config: { - metricResults$: Observable[]> + metricResults$: Observable[]> cfGuid: string }) { if (!config) { diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts index 8b23af7c3e..3f90cff2d6 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts @@ -9,19 +9,19 @@ import { import { AppState } from '../../../../../store/app-state'; import { entityFactory } from '../../../../../store/helpers/entity-factory'; import { IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; -import { IMetricApplication } from '../../../../../store/types/metric.types'; +import { IMetricCell } from '../../../../../store/types/metric.types'; import { ListDataSource } from '../../data-sources-controllers/list-data-source'; import { IListConfig } from '../../list.component.types'; export class CfCellsDataSource - extends ListDataSource, IMetrics>> { + extends ListDataSource, IMetrics>> { static cellIdPath = 'metric.bosh_job_id'; static cellNamePath = 'metric.bosh_job_name'; static cellHealthyPath = 'value.1'; static cellDeploymentPath = 'metric.bosh_deployment'; - constructor(store: Store, cfGuid: string, listConfig: IListConfig>) { + constructor(store: Store, cfGuid: string, listConfig: IListConfig>) { const action = new FetchCFMetricsPaginatedAction( cfGuid, new MetricQueryConfig('firehose_value_metric_rep_unhealthy_cell', {}), diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts index 16d86b078b..587ed6245d 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts @@ -1,24 +1,23 @@ import { Injectable } from '@angular/core'; -import { Router } from '@angular/router'; import { Store } from '@ngrx/store'; -import { ActiveRouteCfOrgSpace } from '../../../../../features/cloud-foundry/cf-page.types'; +import { ActiveRouteCfCell } from '../../../../../features/cloud-foundry/cf-page.types'; import { ListView } from '../../../../../store/actions/list.actions'; import { AppState } from '../../../../../store/app-state'; import { IMetricVectorResult } from '../../../../../store/types/base-metric.types'; -import { IMetricApplication } from '../../../../../store/types/metric.types'; +import { IMetricCell } from '../../../../../store/types/metric.types'; import { getIntegerFieldSortFunction } from '../../data-sources-controllers/local-filtering-sorting'; import { TableCellBooleanIndicatorComponent, TableCellBooleanIndicatorComponentConfig, } from '../../list-table/table-cell-boolean-indicator/table-cell-boolean-indicator.component'; import { ITableColumn } from '../../list-table/table.types'; -import { IListAction, ListViewTypes } from '../../list.component.types'; +import { ListViewTypes } from '../../list.component.types'; import { BaseCfListConfig } from '../base-cf/base-cf-list-config'; import { CfCellsDataSource } from './cf-cells-data-source'; @Injectable() -export class CfCellsListConfigService extends BaseCfListConfig> { +export class CfCellsListConfigService extends BaseCfListConfig> { dataSource: CfCellsDataSource; defaultView = 'table' as ListView; @@ -30,15 +29,15 @@ export class CfCellsListConfigService extends BaseCfListConfig> = { + private boolIndicatorConfig: TableCellBooleanIndicatorComponentConfig> = { // "0 signifies healthy, and 1 signifies unhealthy" - isEnabled: (row: IMetricVectorResult) => row ? row.value[1] === '0' : false, + isEnabled: (row: IMetricVectorResult) => row ? row.value[1] === '0' : false, type: 'enabled-disabled', subtle: false, showText: false }; - columns: Array>> = [ + columns: Array>> = [ { columnId: 'id', headerCell: () => 'ID', @@ -54,8 +53,8 @@ export class CfCellsListConfigService extends BaseCfListConfig 'Name', cellDefinition: { valuePath: CfCellsDataSource.cellNamePath, - getLink: (row: IMetricVectorResult) => - `/cloud-foundry/${this.activeRouteCfOrgSpace.cfGuid}/cells/${row.metric.bosh_job_id}/summary` + getLink: (row: IMetricVectorResult) => + `/cloud-foundry/${this.activeRouteCfCell.cfGuid}/cells/${row.metric.bosh_job_id}/summary` }, cellFlex: '1', sort: { @@ -91,9 +90,9 @@ export class CfCellsListConfigService extends BaseCfListConfig, private activeRouteCfOrgSpace: ActiveRouteCfOrgSpace) { + constructor(store: Store, private activeRouteCfCell: ActiveRouteCfCell) { super(); - this.dataSource = new CfCellsDataSource(store, activeRouteCfOrgSpace.cfGuid, this); + this.dataSource = new CfCellsDataSource(store, activeRouteCfCell.cfGuid, this); } getColumns = () => this.columns; diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 83b3d04b20..e708b19460 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -97,7 +97,6 @@ export class FetchCFMetricsPaginatedAction extends FetchCFMetricsAction implemen } actions = []; paginationKey: string; - // TODO: RC Move this to DataSource initialParams = { 'order-direction': 'desc', 'order-direction-field': 'id', diff --git a/src/frontend/app/store/types/metric.types.ts b/src/frontend/app/store/types/metric.types.ts index 097ebfb441..44df01daba 100644 --- a/src/frontend/app/store/types/metric.types.ts +++ b/src/frontend/app/store/types/metric.types.ts @@ -1,16 +1,16 @@ export interface IMetricApplication { __name__: string; - application_id?: string; + application_id: string; bosh_deployment: string; bosh_job_id: string; bosh_job_ip?: string; bosh_job_name: string; instance: string; - instance_index?: string; + instance_index: string; job: string; origin: string; } -// TODO: RC search and replace for all cell stuff + export interface IMetricCell { __name__: string; bosh_deployment: string; From 89840498c6e7f659a56557e04632cc5abd41b4ee Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 28 Sep 2018 13:26:59 +0100 Subject: [PATCH 041/114] Fix unit tests --- .../cf-cell-summary-chart.component.spec.ts | 6 +++ .../cloud-foundry-cell-base.component.spec.ts | 8 +++- ...loud-foundry-cell-charts.component.spec.ts | 14 +++++-- ...oud-foundry-cell-summary.component.spec.ts | 41 +++++++++++++++++-- .../cloud-foundry-cells.component.spec.ts | 12 ++++-- 5 files changed, 71 insertions(+), 10 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts index b8879f2f6b..8cf8aef6de 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts @@ -13,12 +13,18 @@ describe('CfCellSummaryChartComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ + declarations: [ + CfCellSummaryChartComponent + ], imports: [ createBasicStoreModule(), RouterTestingModule, CoreModule, SharedModule, NoopAnimationsModule + ], + providers: [ + ] }) .compileComponents(); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.spec.ts index cee3688755..10dafc063b 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.spec.ts @@ -2,6 +2,8 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; import { ActiveRouteCfOrgSpace } from '../../../../cf-page.types'; +import { CloudFoundryEndpointService } from '../../../../services/cloud-foundry-endpoint.service'; +import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; import { CloudFoundryCellBaseComponent } from './cloud-foundry-cell-base.component'; @@ -13,7 +15,11 @@ describe('CloudFoundryCellBaseComponent', () => { TestBed.configureTestingModule({ declarations: [CloudFoundryCellBaseComponent], imports: [...BaseTestModules], - providers: [ActiveRouteCfOrgSpace] + providers: [ + CloudFoundryEndpointService, + CloudFoundryCellService, + ActiveRouteCfOrgSpace + ] }) .compileComponents(); })); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts index b45acf5239..c51f36d678 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts @@ -1,7 +1,9 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; -import { ActiveRouteCfOrgSpace } from '../../../../cf-page.types'; +import { ActiveRouteCfCell } from '../../../../cf-page.types'; +import { CfCellSummaryChartComponent } from '../../cf-cell-summary-chart/cf-cell-summary-chart.component'; +import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; import { CloudFoundryCellChartsComponent } from './cloud-foundry-cell-charts.component'; describe('CloudFoundryCellChartsComponent', () => { @@ -10,9 +12,15 @@ describe('CloudFoundryCellChartsComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [CloudFoundryCellChartsComponent], + declarations: [ + CloudFoundryCellChartsComponent, + CfCellSummaryChartComponent + ], imports: [...BaseTestModules], - providers: [ActiveRouteCfOrgSpace] + providers: [ + CloudFoundryCellService, + ActiveRouteCfCell + ] }) .compileComponents(); })); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts index b97e7b4517..db80996f64 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts @@ -1,7 +1,33 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { CloudFoundryCellSummaryComponent } from './cloud-foundry-cell-summary.component'; import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { ActiveRouteCfCell } from '../../../../cf-page.types'; +import { CfCellSummaryChartComponent } from '../../cf-cell-summary-chart/cf-cell-summary-chart.component'; +import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; +import { CloudFoundryCellSummaryComponent } from './cloud-foundry-cell-summary.component'; +import { of as observableOf } from 'rxjs'; + +class MockCloudFoundryCellService { + cfGuid = 'cfGuid'; + cellId = 'cellId'; + cellMetric$ = observableOf({}); + + healthy$ = observableOf(null); + healthyMetricId = observableOf(null); + cpus$ = observableOf(null); + + usageContainers$ = observableOf(null); + remainingContainers$ = observableOf(null); + totalContainers$ = observableOf(null); + + usageDisk$ = observableOf(null); + remainingDisk$ = observableOf(null); + totalDisk$ = observableOf(null); + + usageMemory$ = observableOf(null); + remainingMemory$ = observableOf(null); + totalMemory$ = observableOf(null); +} describe('CloudFoundryCellSummaryComponent', () => { let component: CloudFoundryCellSummaryComponent; @@ -9,9 +35,18 @@ describe('CloudFoundryCellSummaryComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [CloudFoundryCellSummaryComponent], + declarations: [ + CloudFoundryCellSummaryComponent, + CfCellSummaryChartComponent + ], imports: [...BaseTestModules], - providers: [ActiveRouteCfOrgSpace] + providers: [ + { + provide: CloudFoundryCellService, + useValue: new MockCloudFoundryCellService() + }, + ActiveRouteCfCell + ] }) .compileComponents(); })); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts index 94eae4a6f6..20d084ec43 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts @@ -1,8 +1,11 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { CloudFoundryCellsComponent } from './cloud-foundry-cells.component'; +import { + CfCellsListConfigService, +} from '../../../../shared/components/list/list-types/cf-cells/cf-cells-list-config.service'; import { BaseTestModules } from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; -import { ActiveRouteCfOrgSpace } from '../../cf-page.types'; +import { ActiveRouteCfCell } from '../../cf-page.types'; +import { CloudFoundryCellsComponent } from './cloud-foundry-cells.component'; describe('CloudFoundryCellsComponent', () => { let component: CloudFoundryCellsComponent; @@ -12,7 +15,10 @@ describe('CloudFoundryCellsComponent', () => { TestBed.configureTestingModule({ declarations: [CloudFoundryCellsComponent], imports: [...BaseTestModules], - providers: [ActiveRouteCfOrgSpace] + providers: [ + CfCellsListConfigService, + ActiveRouteCfCell + ] }) .compileComponents(); })); From 3214528d02c25a295326b2f8e74bee22ca8cfc1f Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 28 Sep 2018 13:33:05 +0100 Subject: [PATCH 042/114] Fixes for parent time range selector --- .../home/home/home-page.component.html | 4 +- .../features/home/home/home-page.component.ts | 50 +++++++++++++------ .../metrics-chart/metrics-chart.component.ts | 6 ++- ...trics-parent-range-selector.component.html | 17 +++---- ...trics-parent-range-selector.component.scss | 4 ++ ...metrics-parent-range-selector.component.ts | 8 +-- 6 files changed, 56 insertions(+), 33 deletions(-) diff --git a/src/frontend/app/features/home/home/home-page.component.html b/src/frontend/app/features/home/home/home-page.component.html index 669a8ce5d0..f60a79871a 100644 --- a/src/frontend/app/features/home/home/home-page.component.html +++ b/src/frontend/app/features/home/home/home-page.component.html @@ -2,8 +2,6 @@

Dashboard

- - - + \ No newline at end of file diff --git a/src/frontend/app/features/home/home/home-page.component.ts b/src/frontend/app/features/home/home/home-page.component.ts index 315787196b..d880b1e340 100644 --- a/src/frontend/app/features/home/home/home-page.component.ts +++ b/src/frontend/app/features/home/home/home-page.component.ts @@ -21,21 +21,41 @@ export class HomePageComponent implements OnInit { this.instanceChartConfig = this.buildChartConfig(); } - instanceMetricConfig: MetricsConfig> = { - getSeriesName: result => `Instance ${result.metric.instance_index}`, - mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, - sort: MetricsChartHelpers.sortBySeriesName, - mapSeriesItemValue: this.mapSeriesItemValue(), - metricsAction: new FetchApplicationMetricsAction( - 'fbb2e26f-491f-468c-8d5b-ed02028f7106', - 'rqljU7j5TF-v8_nyozXsd6kDUeU', - new MetricQueryConfig('firehose_container_metric_cpu_percentage') - ) - }; - - private mapSeriesItemValue() { - return (bytes) => (bytes / 1000000).toFixed(2); - } + instanceMetricConfig: MetricsConfig>[] = [ + { + getSeriesName: result => `Instance ${result.metric.instance_index}`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + sort: MetricsChartHelpers.sortBySeriesName, + // mapSeriesItemValue: (bytes) => (bytes / 1000000).toFixed(2); + metricsAction: new FetchApplicationMetricsAction( + '23b29b1a-1411-422d-9089-0dd51b1fe57a', + 'rqljU7j5TF-v8_nyozXsd6kDUeU', + new MetricQueryConfig('firehose_container_metric_cpu_percentage') + ) + }, + { + getSeriesName: result => `Instance ${result.metric.instance_index}`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + sort: MetricsChartHelpers.sortBySeriesName, + mapSeriesItemValue: (bytes) => (bytes / 1000000).toFixed(2), + metricsAction: new FetchApplicationMetricsAction( + '23b29b1a-1411-422d-9089-0dd51b1fe57a', + 'rqljU7j5TF-v8_nyozXsd6kDUeU', + new MetricQueryConfig('firehose_container_metric_memory_bytes') + ) + }, + { + getSeriesName: result => `Instance ${result.metric.instance_index}`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + sort: MetricsChartHelpers.sortBySeriesName, + mapSeriesItemValue: (bytes) => (bytes / 1000000).toFixed(2), + metricsAction: new FetchApplicationMetricsAction( + '23b29b1a-1411-422d-9089-0dd51b1fe57a', + 'rqljU7j5TF-v8_nyozXsd6kDUeU', + new MetricQueryConfig('firehose_container_metric_disk_bytes') + ) + } + ]; private buildChartConfig() { diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 80f93845b2..ddd92c585a 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,7 +1,7 @@ import { AfterContentInit, Component, ContentChild, Input, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { Subscription } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, distinctUntilChanged } from 'rxjs/operators'; import { MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; @@ -97,6 +97,9 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni ); this.results$ = this.metricsMonitor.entity$.pipe( + distinctUntilChanged((oldMetrics, newMetrics) => { + return oldMetrics && oldMetrics.data === newMetrics.data; + }), map(metrics => { const metricsArray = this.mapMetricsToChartData(metrics, this.metricsConfig); if (!metricsArray.length) { @@ -143,6 +146,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni private mapMetricsToChartData(metrics: IMetrics, metricsConfig: MetricsConfig) { if (metrics && metrics.data) { + console.log(metrics.data); switch (metrics.data.resultType) { case MetricResultTypes.MATRIX: return MetricsChartManager.mapMatrix(metrics.data, metricsConfig); diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html index e359678be2..f25f319e90 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html @@ -1,16 +1,13 @@
-
-
-

Choose time window

- -
- - -
+ +
+ +
diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss index e69de29bb2..5bae97daa5 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss @@ -0,0 +1,4 @@ +app-metrics-chart { + display: block; + height: 300px; +} diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts index bb64ecfd6b..8db5fdd9be 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, QueryList, ViewChildren, AfterViewInit } from '@angular/core'; +import { Component, OnInit, QueryList, ViewChildren, AfterViewInit, AfterContentInit, ContentChildren } from '@angular/core'; import { Subscription } from 'rxjs'; import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; import { MetricsRangeSelectorManagerService } from '../../services/metrics-range-selector-manager.service'; @@ -15,10 +15,10 @@ import { IMetrics } from '../../../store/types/base-metric.types'; MetricsRangeSelectorManagerService ] }) -export class MetricsParentRangeSelectorComponent implements OnInit, AfterViewInit { +export class MetricsParentRangeSelectorComponent implements OnInit, AfterContentInit { private actionSub: Subscription; - @ViewChildren(MetricsChartComponent) + @ContentChildren(MetricsChartComponent) private metricsCharts: QueryList; public rangeTypes = MetricQueryType; @@ -30,7 +30,7 @@ export class MetricsParentRangeSelectorComponent implements OnInit, AfterViewIni } - ngAfterViewInit() { + ngAfterContentInit() { const action = this.metricsCharts.first.metricsConfig.metricsAction; const metricsMonitor = this.entityMonitorFactory.create( action.metricId, From 783b3b39917beda3c066c5e75b8ff3e67fb0f461 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 28 Sep 2018 14:29:38 +0100 Subject: [PATCH 043/114] Fixing layout for parent selector --- .../metrics-chart.component.html | 34 ++++++++------- .../metrics-chart.component.scss | 4 ++ .../metrics-chart/metrics-chart.component.ts | 3 ++ ...trics-parent-range-selector.component.html | 42 ++++++++++--------- ...trics-parent-range-selector.component.scss | 36 ++++++++++++++-- 5 files changed, 80 insertions(+), 39 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index cd6739acb2..1d69bf8c86 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -1,17 +1,19 @@ -
-
-

{{title}}

- + +
+
+

{{title}}

+ +
+ + +
+ + + {{ chartConfig.chartType }} chart type not found +
+ No results found
- - -
- - - {{ chartConfig.chartType }} chart type not found -
- No results found -
\ No newline at end of file + \ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index f5ac0a85a2..831fb14f0d 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -8,6 +8,10 @@ align-items: center; display: flex; } + &__card { + height: 300px; + margin-bottom: 15px; + } &__outer { display: flex; flex-direction: column; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index ddd92c585a..a90874ae0b 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -48,6 +48,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni this.commitAction(action); } + public hasMultipleInstances = false; + public chartTypes = MetricsChartTypes; private pollSub: Subscription; @@ -105,6 +107,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni if (!metricsArray.length) { return null; } + this.hasMultipleInstances = metricsArray.length > 1; return this.postFetchMiddleware(metricsArray); }) ); diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html index f25f319e90..ad3a547083 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html @@ -1,16 +1,5 @@ -
-
-
- -
- - -
-
-
+
+
@@ -18,21 +7,34 @@ -
-
-
{{ rangeSelectorManager.committedStartEnd[0] | + +
+
+
{{ rangeSelectorManager.committedStartEnd[0] | amDateFormat:'Do MMM YYYY, HH:mm' }}
-
to
-
{{ rangeSelectorManager.committedStartEnd[1] | +
to
+
{{ rangeSelectorManager.committedStartEnd[1] | amDateFormat:'Do MMM YYYY, HH:mm' }}
- No dates selected - Edit + No date range set +
+
+ +
+ +
+
+
+ +
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss index 5bae97daa5..78f8654206 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss @@ -1,4 +1,34 @@ -app-metrics-chart { - display: block; - height: 300px; +.metrics-range-parent-selector { + &__date-range { + align-items: center; + display: flex; + &-buttons { + margin-left: 10px; + } + } + &__charts { + & > * { + background-color: white; + height: 500px; + } + } + &__dropdown-wrapper { + display: flex; + } + &__dropdown-wrapper { + align-items: center; + display: flex; + } + &__selected-range { + margin-left: 10px; + &-dates { + display: flex; + } + &-to { + margin: 0 5px; + } + &-date { + font-weight: bold; + } + } } From c93b7fe861663e25389d819969bb146b1c27fc76 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 28 Sep 2018 16:41:42 +0100 Subject: [PATCH 044/114] Fixed value vs series querying, other small fixes --- .../cf-cell-summary-chart.component.ts | 2 +- .../cloud-foundry-cell-charts.component.html | 16 +++++++--------- .../cloud-foundry-cell-summary.component.html | 6 +++--- .../cloud-foundry-cell.service.ts | 8 ++++---- .../app/store/actions/metrics.actions.ts | 13 +------------ .../app/store/effects/metrics.effects.ts | 12 ++++++++++-- 6 files changed, 26 insertions(+), 31 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts index 6e2ab961c7..0e99721e95 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts @@ -57,7 +57,7 @@ export class CfCellSummaryChartComponent implements OnInit { ngOnInit() { this.chartConfig = this.buildChartConfig(); this.metricConfig = { - getSeriesName: result => `Cell ${result.metric.bosh_job_id}`, + getSeriesName: result => `Cell ${result.metric.bosh_job_name} (${result.metric.bosh_job_id})`, mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, sort: MetricsChartHelpers.sortBySeriesName, // mapSeriesItemValue: this.mapSeriesItemValue(), diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html index 5228d3865d..521289b63f 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html @@ -1,18 +1,16 @@ - + + - + - + \ No newline at end of file diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html index 54261bf1d5..18261f0701 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html @@ -3,9 +3,9 @@ No dates selected - Edit + Change
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts index 5c0799fb7a..c0495ff254 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.ts @@ -26,7 +26,7 @@ export class MetricsRangeSelectorComponent implements OnDestroy { ) { this.rangeSelectorSub = this.rangeSelectorManager.timeWindow$.subscribe(selectedTimeRangeValue => { if (selectedTimeRangeValue.queryType === MetricQueryType.RANGE_QUERY) { - if (!this.rangeSelectorManager.startEnd[0] || !this.rangeSelectorManager.startEnd[1]) { + if (!this.rangeSelectorManager.committedStartEnd[0] || !this.rangeSelectorManager.committedStartEnd[1]) { this.showOverlay = true; } } diff --git a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts index 4ad811cb9f..2e94321ac3 100644 --- a/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts +++ b/src/frontend/app/shared/components/start-end-date/start-end-date.component.ts @@ -44,7 +44,11 @@ export class StartEndDateComponent { @Input() set start(start: moment.Moment) { this.valid = true; - if (start && start.isValid()) { + if (!start) { + this.startValue = start; + return; + } + if (start.isValid()) { if (!this.isStartEndValid(start, this.end)) { this.valid = false; return; @@ -64,6 +68,10 @@ export class StartEndDateComponent { @Input() set end(end: moment.Moment) { this.valid = true; + if (!end) { + this.endValue = end; + return; + } if (end && end.isValid()) { if (!this.isStartEndValid(this.start, end)) { this.valid = false; diff --git a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts index 09f81492bb..c2682b3fcf 100644 --- a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts @@ -45,7 +45,11 @@ export class MetricsRangeSelectorManagerService { private commitDate(date: moment.Moment, type: 'start' | 'end') { const index = type === 'start' ? this.startIndex : this.endIndex; const oldDate = this.startEnd[index]; - if (!date.isValid() || date.isSame(oldDate)) { + if (oldDate && !date) { + this.startEnd[index] = date; + return; + } + if (!date || !date.isValid() || date.isSame(oldDate)) { return; } this.startEnd[index] = date; @@ -57,6 +61,8 @@ export class MetricsRangeSelectorManagerService { this.startEnd[0], this.startEnd[1] ]; + this.start = null; + this.end = null; this.commitAction(action); }; } @@ -73,8 +79,6 @@ export class MetricsRangeSelectorManagerService { if (timeRange.queryType === MetricQueryType.RANGE_QUERY) { const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); if (isDifferent) { - this.start = start; - this.end = end; this.committedStartEnd = [start, end]; } } @@ -107,9 +111,7 @@ export class MetricsRangeSelectorManagerService { } set start(start: moment.Moment) { - if (start) { - this.commitDate(start, 'start'); - } + this.commitDate(start, 'start'); } get start() { @@ -117,9 +119,7 @@ export class MetricsRangeSelectorManagerService { } set end(end: moment.Moment) { - if (end) { - this.commitDate(end, 'end'); - } + this.commitDate(end, 'end'); } get end() { From b410a838d6e04997bc63b8a39bd85ee9a4dfe37f Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 28 Sep 2018 17:32:00 +0100 Subject: [PATCH 046/114] Fix cells view --- .../tabs/cloud-foundry-cells/cloud-foundry-cells.component.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.ts index 1b5c66d55d..69ffe4bd49 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.ts @@ -4,6 +4,7 @@ import { CfCellsListConfigService, } from '../../../../shared/components/list/list-types/cf-cells/cf-cells-list-config.service'; import { ListConfig } from '../../../../shared/components/list/list.component.types'; +import { getActiveRouteCfCellProvider } from '../../cf.helpers'; @Component({ selector: 'app-cloud-foundry-cells', @@ -13,7 +14,8 @@ import { ListConfig } from '../../../../shared/components/list/list.component.ty { provide: ListConfig, useClass: CfCellsListConfigService - } + }, + getActiveRouteCfCellProvider, ] }) export class CloudFoundryCellsComponent { } From 983cc156560e2999d321900344df04f4ee16fa5b Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 28 Sep 2018 20:57:27 +0100 Subject: [PATCH 047/114] Add parent time window selector to applciation metrics --- .../metrics-tab/metrics-tab.component.html | 19 ++---- .../tabs/metrics-tab/metrics-tab.component.ts | 64 ++++++++++++++++++- .../date-time/date-time.component.html | 1 - .../metrics-chart.component.html | 22 ++++--- .../metrics-chart.component.scss | 3 + .../metrics-chart/metrics-chart.component.ts | 28 ++++++-- .../app/store/effects/metrics.effects.ts | 11 ++-- 7 files changed, 112 insertions(+), 36 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html index 55b9398234..2d25e39888 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.html @@ -1,15 +1,4 @@ - - - - - - - - - - - - \ No newline at end of file + + + + \ No newline at end of file diff --git a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.ts b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.ts index 172e998650..0698f4259d 100644 --- a/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.ts +++ b/src/frontend/app/features/applications/application/application-tabs-base/tabs/metrics-tab/metrics-tab.component.ts @@ -1,5 +1,11 @@ import { Component } from '@angular/core'; import { ApplicationService } from '../../../../application.service'; +import { MetricsConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.component'; +import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; +import { IMetricApplication } from '../../../../../../store/types/metric.types'; +import { MetricsChartHelpers } from '../../../../../../shared/components/metrics-chart/metrics.component.helpers'; +import { FetchApplicationMetricsAction, MetricQueryConfig } from '../../../../../../store/actions/metrics.actions'; +import { MetricsLineChartConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.types'; @Component({ selector: 'app-metrics-tab', @@ -7,5 +13,61 @@ import { ApplicationService } from '../../../../application.service'; styleUrls: ['./metrics-tab.component.scss'] }) export class MetricsTabComponent { - constructor(public applicationService: ApplicationService) { } + public instanceMetricConfigs: [ + MetricsConfig>, + MetricsLineChartConfig + ][]; + private buildChartConfig(yLabel: string) { + const lineChartConfig = new MetricsLineChartConfig(); + lineChartConfig.xAxisLabel = 'Time'; + lineChartConfig.yAxisLabel = yLabel; + return lineChartConfig; + } + constructor(public applicationService: ApplicationService) { + + this.instanceMetricConfigs = [ + [ + { + getSeriesName: result => `Instance ${result.metric.instance_index}`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + sort: MetricsChartHelpers.sortBySeriesName, + metricsAction: new FetchApplicationMetricsAction( + applicationService.appGuid, + applicationService.cfGuid, + new MetricQueryConfig('firehose_container_metric_cpu_percentage') + ) + }, + this.buildChartConfig('CPU Usage (%)') + ], + [ + { + getSeriesName: result => `Instance ${result.metric.instance_index}`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + sort: MetricsChartHelpers.sortBySeriesName, + mapSeriesItemValue: (bytes) => (bytes / 1000000).toFixed(2), + metricsAction: new FetchApplicationMetricsAction( + applicationService.appGuid, + applicationService.cfGuid, + new MetricQueryConfig('firehose_container_metric_memory_bytes') + ) + }, + this.buildChartConfig('Memory Usage (MB)') + ], + [ + { + getSeriesName: result => `Instance ${result.metric.instance_index}`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + sort: MetricsChartHelpers.sortBySeriesName, + mapSeriesItemValue: (bytes) => (bytes / 1000000).toFixed(2), + metricsAction: new FetchApplicationMetricsAction( + applicationService.appGuid, + applicationService.cfGuid, + new MetricQueryConfig('firehose_container_metric_disk_bytes') + ) + }, + this.buildChartConfig('Disk Usage (MB)') + ] + ]; + + } } diff --git a/src/frontend/app/shared/components/date-time/date-time.component.html b/src/frontend/app/shared/components/date-time/date-time.component.html index 283b03197e..29a6e2bf4f 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.html +++ b/src/frontend/app/shared/components/date-time/date-time.component.html @@ -5,7 +5,6 @@ - access_time \ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 1d69bf8c86..35f4986179 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -4,16 +4,20 @@

{{title}}

- + + -
- - - {{ chartConfig.chartType }} chart type not found +
+
+ + + {{ chartConfig.chartType }} chart type not found +
+ +
No results found
+
- No results found
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index 831fb14f0d..6dd20bfc02 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -27,6 +27,9 @@ min-width: 100px; width: 10%; } + &__refreshing { + position: absolute; + } &__overlay { background-color: rgba(0, 0, 0, 0); bottom: 0; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index a90874ae0b..da29d4999a 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,7 +1,7 @@ import { AfterContentInit, Component, ContentChild, Input, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; -import { Subscription } from 'rxjs'; -import { map, distinctUntilChanged } from 'rxjs/operators'; +import { Subscription, Observable, combineLatest, timer } from 'rxjs'; +import { map, distinctUntilChanged, startWith, debounce } from 'rxjs/operators'; import { MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; @@ -62,6 +62,9 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni private committedAction: MetricsAction; + public isRefreshing$: Observable; + public isFetching$: Observable; + constructor( private store: Store, private entityMonitorFactory: EntityMonitorFactory @@ -98,6 +101,8 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni entityFactory(metricSchemaKey) ); + + this.results$ = this.metricsMonitor.entity$.pipe( distinctUntilChanged((oldMetrics, newMetrics) => { return oldMetrics && oldMetrics.data === newMetrics.data; @@ -105,12 +110,28 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni map(metrics => { const metricsArray = this.mapMetricsToChartData(metrics, this.metricsConfig); if (!metricsArray.length) { - return null; + return []; } this.hasMultipleInstances = metricsArray.length > 1; return this.postFetchMiddleware(metricsArray); }) ); + this.isRefreshing$ = combineLatest( + this.results$, + this.metricsMonitor.isFetchingEntity$ + ).pipe( + debounce(([results, fetching]) => { + return !fetching ? timer(800) : timer(0); + }), + map(([results, fetching]) => results && fetching) + ); + + this.isFetching$ = combineLatest( + this.results$.pipe(startWith(null)), + this.metricsMonitor.isFetchingEntity$ + ).pipe( + map(([results, fetching]) => !results && fetching) + ); } ngAfterContentInit() { @@ -149,7 +170,6 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni private mapMetricsToChartData(metrics: IMetrics, metricsConfig: MetricsConfig) { if (metrics && metrics.data) { - console.log(metrics.data); switch (metrics.data.resultType) { case MetricResultTypes.MATRIX: return MetricsChartManager.mapMatrix(metrics.data, metricsConfig); diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index e7c5207cb4..14bf442a04 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -1,16 +1,15 @@ -import { query } from '@angular/animations'; - -import { catchError, mergeMap, map } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; import { Store } from '@ngrx/store'; - -import { METRICS_START, MetricsAction, getFullMetricQueryQuery } from '../actions/metrics.actions'; +import { catchError, map, mergeMap } from 'rxjs/operators'; +import { getFullMetricQueryQuery, MetricsAction, METRICS_START } from '../actions/metrics.actions'; import { AppState } from '../app-state'; import { metricSchemaKey } from '../helpers/entity-factory'; import { IMetricsResponse } from '../types/base-metric.types'; -import { IRequestAction, WrapperRequestActionFailed, WrapperRequestActionSuccess, StartRequestAction } from './../types/request.types'; +import { IRequestAction, StartRequestAction, WrapperRequestActionFailed, WrapperRequestActionSuccess } from './../types/request.types'; + + @Injectable() From 7e4a52fbbcfbb33474bd1bf9ff22e2eca6db3d6b Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Fri, 28 Sep 2018 21:04:14 +0100 Subject: [PATCH 048/114] Allow calender text to float --- .../shared/components/date-time/date-time.component.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/app/shared/components/date-time/date-time.component.html b/src/frontend/app/shared/components/date-time/date-time.component.html index 29a6e2bf4f..55bb99ba86 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.html +++ b/src/frontend/app/shared/components/date-time/date-time.component.html @@ -1,10 +1,10 @@
- - + + - + - + \ No newline at end of file From 538493b97cfb6376889940f7df413e7e4412cc8c Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 1 Oct 2018 12:28:35 +0100 Subject: [PATCH 049/114] Fixes following merge --- .../cloud-foundry/cloud-foundry.module.ts | 8 +- .../cf-cell-summary-chart.component.html | 4 +- .../cf-cell-summary-chart.component.spec.ts | 72 ++++----- .../cf-cell-summary-chart.component.ts | 151 +++++++++--------- .../cloud-foundry-cell-charts.component.html | 20 +-- .../cloud-foundry-cell-charts.component.ts | 24 +++ .../cloud-foundry-cell-summary.component.html | 9 +- .../cloud-foundry-cell-summary.component.ts | 11 ++ .../cloud-foundry-cell.service.ts | 33 +++- .../cf-app-instances-config.service.ts | 7 +- .../cf-cells/cf-cells-data-source.ts | 7 +- .../metrics-chart.component.html | 6 +- .../metrics-chart/metrics-chart.component.ts | 3 +- .../metrics-range-selector.service.ts | 36 ++--- .../services/metrics-range-selector.types.ts | 4 +- 15 files changed, 220 insertions(+), 175 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts index 8f29c75e0d..ad1f59c1cc 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts @@ -28,9 +28,9 @@ import { EditSpaceComponent } from './edit-space/edit-space.component'; import { CloudFoundryEndpointService } from './services/cloud-foundry-endpoint.service'; import { CloudFoundryOrganizationService } from './services/cloud-foundry-organization.service'; import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; -import { - CfCellSummaryChartComponent, -} from './tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component'; +// import { +// CfCellSummaryChartComponent, +// } from './tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component'; import { CloudFoundryCellBaseComponent, } from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component'; @@ -108,7 +108,7 @@ import { UsersRolesComponent } from './users/manage-users/manage-users.component CloudFoundryCellBaseComponent, CloudFoundryCellSummaryComponent, CloudFoundryCellChartsComponent, - CfCellSummaryChartComponent, + // CfCellSummaryChartComponent, CloudFoundryBuildPacksComponent, CloudFoundryStacksComponent, CloudFoundrySecurityGroupsComponent, diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html index 524bf7983c..b9c68a70be 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts index 8cf8aef6de..eaf2ad7815 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts @@ -1,42 +1,42 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { RouterTestingModule } from '@angular/router/testing'; +// import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +// import { RouterTestingModule } from '@angular/router/testing'; -import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -import { CfCellSummaryChartComponent } from './cf-cell-summary-chart.component'; -import { createBasicStoreModule } from '../../../../../test-framework/store-test-helper'; -import { CoreModule } from '../../../../../core/core.module'; -import { SharedModule } from '../../../../../shared/shared.module'; +// import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +// import { CfCellSummaryChartComponent } from './cf-cell-summary-chart.component'; +// import { createBasicStoreModule } from '../../../../../test-framework/store-test-helper'; +// import { CoreModule } from '../../../../../core/core.module'; +// import { SharedModule } from '../../../../../shared/shared.module'; -describe('CfCellSummaryChartComponent', () => { - let component: CfCellSummaryChartComponent; - let fixture: ComponentFixture; +// describe('CfCellSummaryChartComponent', () => { +// let component: CfCellSummaryChartComponent; +// let fixture: ComponentFixture; - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ - CfCellSummaryChartComponent - ], - imports: [ - createBasicStoreModule(), - RouterTestingModule, - CoreModule, - SharedModule, - NoopAnimationsModule - ], - providers: [ +// beforeEach(async(() => { +// TestBed.configureTestingModule({ +// declarations: [ +// CfCellSummaryChartComponent +// ], +// imports: [ +// createBasicStoreModule(), +// RouterTestingModule, +// CoreModule, +// SharedModule, +// NoopAnimationsModule +// ], +// providers: [ - ] - }) - .compileComponents(); - })); +// ] +// }) +// .compileComponents(); +// })); - beforeEach(() => { - fixture = TestBed.createComponent(CfCellSummaryChartComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); +// beforeEach(() => { +// fixture = TestBed.createComponent(CfCellSummaryChartComponent); +// component = fixture.componentInstance; +// fixture.detectChanges(); +// }); - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); +// it('should create', () => { +// expect(component).toBeTruthy(); +// }); +// }); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts index 0e99721e95..361b9555d3 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts @@ -1,75 +1,76 @@ -import { Component, Input, OnInit } from '@angular/core'; - -import { MetricsConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.component'; -import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types'; -import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; -import { FetchCFCellMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; -import { IMetricMatrixResult } from '../../../../../store/types/base-metric.types'; -import { IMetricCell } from '../../../../../store/types/metric.types'; - - -@Component({ - selector: 'app-cf-cell-summary-chart', - templateUrl: './cf-cell-summary-chart.component.html', - styleUrls: ['./cf-cell-summary-chart.component.scss'] -}) -export class CfCellSummaryChartComponent implements OnInit { - - @Input() - private cellId: string; - - @Input() - private endpointGuid: string; - - @Input() - private yAxisLabel: string; - - @Input() - private showLegend = true; - - // Prometheus query string - @Input() - private queryString: string; - - @Input() - private seriesTranslation: string; - - @Input() - private queryRange = false; - - @Input() - public title: string; - - public chartConfig: MetricsLineChartConfig; - - public metricConfig: MetricsConfig>; - - constructor() { } - - private buildChartConfig() { - const lineChartConfig = new MetricsLineChartConfig(); - lineChartConfig.xAxisLabel = 'Time'; - lineChartConfig.yAxisLabel = this.yAxisLabel; - lineChartConfig.showLegend = this.showLegend; - return lineChartConfig; - } - - ngOnInit() { - this.chartConfig = this.buildChartConfig(); - this.metricConfig = { - getSeriesName: result => `Cell ${result.metric.bosh_job_name} (${result.metric.bosh_job_id})`, - mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, - sort: MetricsChartHelpers.sortBySeriesName, - // mapSeriesItemValue: this.mapSeriesItemValue(), - metricsAction: new FetchCFCellMetricsAction( - this.endpointGuid, - this.cellId, - new MetricQueryConfig(this.queryString), - // TODO: RC MetricQueryType.RANGE_QUERY causes failure - // this.queryRange ? MetricQueryType.RANGE_QUERY : MetricQueryType.QUERY - MetricQueryType.QUERY - ), - }; - } - -} +// import { Component, Input, OnInit } from '@angular/core'; + +// import { MetricsConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.component'; +// import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types'; +// import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; +// import { MetricQueryType } from '../../../../../shared/services/metrics-range-selector.types'; +// import { FetchCFCellMetricsAction, MetricQueryConfig } from '../../../../../store/actions/metrics.actions'; +// import { IMetricMatrixResult } from '../../../../../store/types/base-metric.types'; +// import { IMetricCell } from '../../../../../store/types/metric.types'; + + +// @Component({ +// selector: 'app-cf-cell-summary-chart', +// templateUrl: './cf-cell-summary-chart.component.html', +// styleUrls: ['./cf-cell-summary-chart.component.scss'] +// }) +// export class CfCellSummaryChartComponent implements OnInit { + +// @Input() +// private cellId: string; + +// @Input() +// private endpointGuid: string; + +// @Input() +// private yAxisLabel: string; + +// @Input() +// private showLegend = true; + +// // Prometheus query string +// @Input() +// private queryString: string; + +// @Input() +// private seriesTranslation: string; + +// @Input() +// private queryRange = false; + +// @Input() +// public title: string; + +// public chartConfig: MetricsLineChartConfig; + +// public metricConfig: MetricsConfig>; + +// constructor() { } + +// private buildChartConfig() { +// const lineChartConfig = new MetricsLineChartConfig(); +// lineChartConfig.xAxisLabel = 'Time'; +// lineChartConfig.yAxisLabel = this.yAxisLabel; +// lineChartConfig.showLegend = this.showLegend; +// return lineChartConfig; +// } + +// ngOnInit() { +// this.chartConfig = this.buildChartConfig(); +// this.metricConfig = { +// getSeriesName: result => `Cell ${result.metric.bosh_job_name} (${result.metric.bosh_job_id})`, +// mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, +// sort: MetricsChartHelpers.sortBySeriesName, +// // mapSeriesItemValue: this.mapSeriesItemValue(), +// metricsAction: new FetchCFCellMetricsAction( +// this.endpointGuid, +// this.cellId, +// new MetricQueryConfig(this.queryString), +// // TODO: RC MetricQueryType.RANGE_QUERY causes failure +// this.queryRange ? MetricQueryType.RANGE_QUERY : MetricQueryType.QUERY +// // MetricQueryType.QUERY +// ), +// }; +// } + +// } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html index 521289b63f..2ecb907850 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.html @@ -1,16 +1,4 @@ - - - - - - - - - - - - - \ No newline at end of file + + + + \ No newline at end of file diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts index eef8223e8a..ac83cf4d4f 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts @@ -1,5 +1,10 @@ import { Component } from '@angular/core'; +import { MetricsConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.component'; +import { MetricsLineChartConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.types'; +import { MetricQueryType } from '../../../../../../shared/services/metrics-range-selector.types'; +import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; +import { IMetricCell } from '../../../../../../store/types/metric.types'; import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; @Component({ @@ -9,8 +14,27 @@ import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; }) export class CloudFoundryCellChartsComponent { + public metricConfigs: [ + MetricsConfig>, + MetricsLineChartConfig + ][]; constructor(public cfCellService: CloudFoundryCellService) { + this.metricConfigs = [ + [ + this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_containers', MetricQueryType.QUERY), + this.cfCellService.buildChartConfig('Available') + ], + [ + this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_total_disk', MetricQueryType.QUERY), + this.cfCellService.buildChartConfig('Memory Available (MB)') + ], + [ + this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_total_memory', MetricQueryType.QUERY), + this.cfCellService.buildChartConfig('Disk Available (MB)') + ], + ]; + } } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html index 18261f0701..4be5c9caf7 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html @@ -30,8 +30,7 @@ - - - - \ No newline at end of file + + + + \ No newline at end of file diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts index 557975668b..bbde7d0a70 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts @@ -3,6 +3,11 @@ import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { CardStatus } from '../../../../../../shared/components/application-state/application-state.service'; +import { MetricsConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.component'; +import { MetricsLineChartConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.types'; +import { MetricQueryType } from '../../../../../../shared/services/metrics-range-selector.types'; +import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; +import { IMetricCell } from '../../../../../../store/types/metric.types'; import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; @Component({ @@ -13,11 +18,17 @@ import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; export class CloudFoundryCellSummaryComponent { public status$: Observable; + public metricsConfig: MetricsConfig>; + public chartConfig: MetricsLineChartConfig; + constructor( public cfCellService: CloudFoundryCellService ) { + this.metricsConfig = this.cfCellService.buildMetricConfig('firehose_value_metric_rep_unhealthy_cell', MetricQueryType.QUERY); + this.chartConfig = this.cfCellService.buildChartConfig('0 = Healthy, 1 = Unhealthy'); + this.status$ = cfCellService.healthy$.pipe( map(health => { if (health === undefined) { diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index c6265d4de5..8f536b87be 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -3,9 +3,13 @@ import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; -import { FetchCFCellMetricsAction, MetricQueryConfig, MetricQueryType } from '../../../../../store/actions/metrics.actions'; +import { MetricsConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.component'; +import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types'; +import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; +import { MetricQueryType } from '../../../../../shared/services/metrics-range-selector.types'; +import { FetchCFCellMetricsAction, MetricQueryConfig } from '../../../../../store/actions/metrics.actions'; import { entityFactory, metricSchemaKey } from '../../../../../store/helpers/entity-factory'; -import { IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; +import { IMetricMatrixResult, IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../store/types/metric.types'; import { ActiveRouteCfCell } from '../../../cf-page.types'; @@ -67,6 +71,31 @@ export class CloudFoundryCellService { } + public buildMetricConfig(queryString: string, queryRange: MetricQueryType): MetricsConfig> { + return { + getSeriesName: result => `Cell ${result.metric.bosh_job_name} (${result.metric.bosh_job_id})`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + sort: MetricsChartHelpers.sortBySeriesName, + // mapSeriesItemValue: this.mapSeriesItemValue(), + metricsAction: new FetchCFCellMetricsAction( + this.cfGuid, + this.cellId, + new MetricQueryConfig(queryString), + // TODO: RC MetricQueryType.RANGE_QUERY causes failure + queryRange + // MetricQueryType.QUERY + ), + }; + } + + public buildChartConfig(yAxisLabel: string): MetricsLineChartConfig { + const lineChartConfig = new MetricsLineChartConfig(); + lineChartConfig.xAxisLabel = 'Time'; + lineChartConfig.yAxisLabel = yAxisLabel; + lineChartConfig.showLegend = false; + return lineChartConfig; + } + private generate(metric: CellMetrics, isMetric = false): Observable { const action = new FetchCFCellMetricsAction( 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 41e4cbb8ae..24226c0446 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 @@ -9,15 +9,12 @@ import { EntityServiceFactory } from '../../../../../core/entity-service-factory import { UtilsService } from '../../../../../core/utils.service'; import { ApplicationService } from '../../../../../features/applications/application.service'; import { DeleteApplicationInstance } from '../../../../../store/actions/application.actions'; -import { - FetchApplicationMetricsAction, - MetricQueryConfig, - MetricQueryType, -} from '../../../../../store/actions/metrics.actions'; +import { FetchApplicationMetricsAction, MetricQueryConfig } from '../../../../../store/actions/metrics.actions'; import { AppState } from '../../../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../../../store/helpers/entity-factory'; import { IMetricMatrixResult, IMetrics } from '../../../../../store/types/base-metric.types'; import { IMetricApplication } from '../../../../../store/types/metric.types'; +import { MetricQueryType } from '../../../../services/metrics-range-selector.types'; import { ConfirmationDialogConfig } from '../../../confirmation-dialog.config'; import { ConfirmationDialogService } from '../../../confirmation-dialog.service'; import { getIntegerFieldSortFunction } from '../../data-sources-controllers/local-filtering-sorting'; diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts index 3f90cff2d6..1e65bd637a 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts @@ -1,15 +1,12 @@ import { Store } from '@ngrx/store'; import { map } from 'rxjs/operators'; -import { - FetchCFMetricsPaginatedAction, - MetricQueryConfig, - MetricQueryType, -} from '../../../../../store/actions/metrics.actions'; +import { FetchCFMetricsPaginatedAction, MetricQueryConfig } from '../../../../../store/actions/metrics.actions'; import { AppState } from '../../../../../store/app-state'; import { entityFactory } from '../../../../../store/helpers/entity-factory'; import { IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../store/types/metric.types'; +import { MetricQueryType } from '../../../../services/metrics-range-selector.types'; import { ListDataSource } from '../../data-sources-controllers/list-data-source'; import { IListConfig } from '../../list.component.types'; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 35f8425219..2cbffc8b55 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -15,9 +15,9 @@

{{title}}

{{ chartConfig.chartType }} chart type not found
- -
No results found
-
+ +
No results found
+
\ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index 69a4e99f5e..eaa799dcce 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -3,10 +3,11 @@ import { Store } from '@ngrx/store'; import { combineLatest, Observable, Subscription, timer } from 'rxjs'; import { debounce, distinctUntilChanged, map, startWith } from 'rxjs/operators'; -import { MetricQueryType, MetricsAction } from '../../../store/actions/metrics.actions'; +import { MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { EntityMonitor } from '../../monitors/entity-monitor'; +import { MetricQueryType } from '../../services/metrics-range-selector.types'; import { MetricsRangeSelectorComponent } from '../metrics-range-selector/metrics-range-selector.component'; import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types/base-metric.types'; import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.service'; diff --git a/src/frontend/app/shared/services/metrics-range-selector.service.ts b/src/frontend/app/shared/services/metrics-range-selector.service.ts index dfaa32911c..c97abd4ba5 100644 --- a/src/frontend/app/shared/services/metrics-range-selector.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector.service.ts @@ -31,32 +31,28 @@ export class MetricsRangeSelectorService { } ]; + private newMetricsAction(action: MetricsAction, newQuery: MetricQueryConfig, queryType: MetricQueryType): MetricsAction { + return { + ...action, + query: newQuery, + queryType + }; + } + public getNewDateRangeAction(action: MetricsAction, start: moment.Moment, end: moment.Moment) { const startUnix = start.unix(); const endUnix = end.unix(); - return new MetricsAction( - action.guid, - action.endpointGuid, - new MetricQueryConfig(action.query.metric, { - start: startUnix, - end: end.unix(), - step: Math.max((endUnix - startUnix) / 200, 0) - }), - action.url, - MetricQueryType.RANGE_QUERY - ); + return this.newMetricsAction(action, new MetricQueryConfig(action.query.metric, { + start: startUnix, + end: end.unix(), + step: Math.max((endUnix - startUnix) / 200, 0) + }), MetricQueryType.RANGE_QUERY); } public getNewTimeWindowAction(action: MetricsAction, window: ITimeRange) { - return new MetricsAction( - action.guid, - action.endpointGuid, - new MetricQueryConfig(action.query.metric, { - window: window.value - }), - action.url, - MetricQueryType.QUERY - ); + return this.newMetricsAction(action, new MetricQueryConfig(action.query.metric, { + window: window.value + }), MetricQueryType.QUERY); } public getDateFromStoreMetric(metrics: IMetrics, times = this.times): StoreMetricTimeRange { diff --git a/src/frontend/app/shared/services/metrics-range-selector.types.ts b/src/frontend/app/shared/services/metrics-range-selector.types.ts index d62623f9f2..106fd1dbd1 100644 --- a/src/frontend/app/shared/services/metrics-range-selector.types.ts +++ b/src/frontend/app/shared/services/metrics-range-selector.types.ts @@ -14,5 +14,7 @@ export interface StoreMetricTimeRange { export enum MetricQueryType { QUERY = 'query', - RANGE_QUERY = 'query_range' + RANGE_QUERY = 'query_range', + // Response contains a single value instead of a series of values + VALUE = 'value' } From 4de01b28a4477eb682bad0a018f033f1623f007b Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 1 Oct 2018 14:36:44 +0100 Subject: [PATCH 050/114] Improvements and more post merge fixes --- .../application-instance-chart.component.ts | 1 + .../cloud-foundry-cell-base.component.ts | 2 +- .../cloud-foundry-cell.service.ts | 8 +++++--- .../table-cell-cf-cell.component.html | 6 ++---- .../cf-cells/cf-cells-list-config.service.ts | 10 +++++----- .../services/metrics-range-selector.service.ts | 14 ++++++++++++++ src/frontend/app/store/actions/metrics.actions.ts | 5 +++-- 7 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts index 0f65b1f030..e253a7a96d 100644 --- a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts +++ b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts @@ -7,6 +7,7 @@ import { MetricsChartHelpers } from '../../../../shared/components/metrics-chart import { IMetricApplication } from '../../../../store/types/metric.types'; import { MetricQueryType } from '../../../../shared/services/metrics-range-selector.types'; +// TODO: RC Remove in upstream PR @Component({ selector: 'app-application-instance-chart', templateUrl: './application-instance-chart.component.html', diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts index c73b8ed78a..56ce1c3485 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts @@ -45,7 +45,7 @@ export class CloudFoundryCellBaseComponent { this.waitForEntityId = cfCellService.healthyMetricId; this.name$ = cfCellService.cellMetric$.pipe( - map(metric => metric.bosh_job_name) + map(metric => `${metric.bosh_job_id} (${metric.bosh_deployment})`) ); this.breadcrumbs$ = cfEndpointService.endpoint$.pipe( diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index 8f536b87be..e62911797f 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -73,14 +73,15 @@ export class CloudFoundryCellService { public buildMetricConfig(queryString: string, queryRange: MetricQueryType): MetricsConfig> { return { - getSeriesName: result => `Cell ${result.metric.bosh_job_name} (${result.metric.bosh_job_id})`, + getSeriesName: (result: IMetricMatrixResult) => `Cell ${result.metric.bosh_job_id} (${result.metric.bosh_deployment})`, mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, sort: MetricsChartHelpers.sortBySeriesName, // mapSeriesItemValue: this.mapSeriesItemValue(), metricsAction: new FetchCFCellMetricsAction( this.cfGuid, this.cellId, - new MetricQueryConfig(queryString), + new MetricQueryConfig(queryString + `{bosh_job_id="${this.cellId}"}`, {}), + // new MetricQueryConfig(queryString, { bosh_job_id: this.cellId }), // TODO: RC Fix - this does not work // TODO: RC MetricQueryType.RANGE_QUERY causes failure queryRange // MetricQueryType.QUERY @@ -101,7 +102,8 @@ export class CloudFoundryCellService { const action = new FetchCFCellMetricsAction( this.cfGuid, this.cellId, - new MetricQueryConfig(metric, { bosh_job_id: this.cellId }), + new MetricQueryConfig(metric, {}), + // new MetricQueryConfig(metric, { bosh_job_id: this.cellId }), MetricQueryType.VALUE ); if (metric === CellMetrics.HEALTHY) { diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html index 2675dc5630..3ecab238f6 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html @@ -1,5 +1,3 @@ - Deployment: {{cellMetric.bosh_deployment}}
- Name: {{cellMetric.bosh_job_name}}
- Id: {{cellMetric.bosh_job_id}} -
+ {{cellMetric.bosh_job_id}} ({{cellMetric.bosh_deployment}}) + \ No newline at end of file diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts index 587ed6245d..62198af74f 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts @@ -42,19 +42,19 @@ export class CfCellsListConfigService extends BaseCfListConfig 'ID', cellDefinition: { - valuePath: CfCellsDataSource.cellIdPath + valuePath: CfCellsDataSource.cellIdPath, + getLink: (row: IMetricVectorResult) => + `/cloud-foundry/${this.activeRouteCfCell.cfGuid}/cells/${row.metric.bosh_job_id}/summary` }, class: 'table-column-select', - cellFlex: '0 0 100px', + cellFlex: '1', sort: getIntegerFieldSortFunction(CfCellsDataSource.cellIdPath) }, { columnId: 'name', headerCell: () => 'Name', cellDefinition: { - valuePath: CfCellsDataSource.cellNamePath, - getLink: (row: IMetricVectorResult) => - `/cloud-foundry/${this.activeRouteCfCell.cfGuid}/cells/${row.metric.bosh_job_id}/summary` + valuePath: CfCellsDataSource.cellNamePath }, cellFlex: '1', sort: { diff --git a/src/frontend/app/shared/services/metrics-range-selector.service.ts b/src/frontend/app/shared/services/metrics-range-selector.service.ts index c97abd4ba5..d1f7b815ff 100644 --- a/src/frontend/app/shared/services/metrics-range-selector.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector.service.ts @@ -42,7 +42,13 @@ export class MetricsRangeSelectorService { public getNewDateRangeAction(action: MetricsAction, start: moment.Moment, end: moment.Moment) { const startUnix = start.unix(); const endUnix = end.unix(); + const { + window, + ...params + } = action.query.params || { window: undefined }; + return this.newMetricsAction(action, new MetricQueryConfig(action.query.metric, { + ...params, start: startUnix, end: end.unix(), step: Math.max((endUnix - startUnix) / 200, 0) @@ -50,7 +56,15 @@ export class MetricsRangeSelectorService { } public getNewTimeWindowAction(action: MetricsAction, window: ITimeRange) { + const { + start, + end, + step, + ...params + } = action.query.params || { start: undefined, end: undefined, step: undefined }; + return this.newMetricsAction(action, new MetricQueryConfig(action.query.metric, { + ...params, window: window.value }), MetricQueryType.QUERY); } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index ae7d8cc555..90e256d43c 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -18,8 +18,9 @@ function joinParams(queryConfig: MetricQueryConfig) { const { window = '', ...params - } = queryConfig.params; - const windowString = window ? `{}[${window}]` : ''; + } = queryConfig.params || {}; + const hasSquiggly = queryConfig.metric.endsWith('}'); + const windowString = window ? `${(hasSquiggly ? '' : '{}')}[${window}]` : ''; const paramString = Object.keys(params).reduce((accum, key) => { return accum + `&${key}=${params[key]}`; }, ''); From 7dcbd06e3f2665ce2db0659c2703364c0512600c Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 1 Oct 2018 15:23:10 +0100 Subject: [PATCH 051/114] Cells fixes --- .../cloud-foundry-tabs-base.component.ts | 10 +++++----- .../cloud-foundry-cell-summary.component.html | 6 ++++-- .../cloud-foundry-cell/cloud-foundry-cell.service.ts | 8 ++++++-- .../table-cell-cf-cell.component.html | 2 +- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts index 63a22a0888..b703a2cc11 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry-tabs-base/cloud-foundry-tabs-base.component.ts @@ -53,16 +53,16 @@ export class CloudFoundryTabsBaseComponent implements OnInit { this.tabLinks = [ { link: 'summary', label: 'Summary' }, { link: 'organizations', label: 'Organizations' }, - { - link: CloudFoundryTabsBaseComponent.users, - label: 'Users', - hidden: usersHidden$ - }, { link: CloudFoundryTabsBaseComponent.cells, label: 'Cells', hidden: cellsHidden$ }, + { + link: CloudFoundryTabsBaseComponent.users, + label: 'Users', + hidden: usersHidden$ + }, { link: CloudFoundryTabsBaseComponent.firehose, label: 'Firehose', diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html index 4be5c9caf7..e90c91fb01 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html @@ -3,11 +3,13 @@ diff --git a/src/frontend/app/features/metrics/metrics/metrics.component.ts b/src/frontend/app/features/metrics/metrics/metrics.component.ts index 72921b718b..8928eb9f4f 100644 --- a/src/frontend/app/features/metrics/metrics/metrics.component.ts +++ b/src/frontend/app/features/metrics/metrics/metrics.component.ts @@ -101,8 +101,8 @@ export class MetricsComponent { filter(a => !!a), map((targetsData: any) => { const mapped = {}; - if (targetsData.activeTargets) { - targetsData.activeTargets.forEach(t => { + if (targetsData.data.activeTargets) { + targetsData.data.activeTargets.forEach(t => { if (t.labels && t.labels.job) { mapped[t.labels.job] = t; } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 2fe64e0447..1446e844b9 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -64,11 +64,9 @@ export class MetricsAction implements IRequestAction { } export class FetchMetricsAction extends MetricsAction { - constructor(public guid: string, public query: string) { - super(guid, query); - this.url = `/pp/${proxyAPIVersion}/proxy/api/v1/` + query; + constructor(guid: string, query: string) { + super(guid, guid, new MetricQueryConfig(query), `/pp/${proxyAPIVersion}/proxy/api/v1/` + query); this.directApi = true; - this.cfGuid = guid; } } export class FetchCFMetricsAction extends MetricsAction { From 0d162388ffd49f7ba1372423fd33adaa832af615 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 1 Oct 2018 15:24:10 +0100 Subject: [PATCH 053/114] Metric chart tweaks --- ...trics-parent-range-selector.component.html | 59 ++++++++++--------- ...trics-parent-range-selector.component.scss | 17 +++++- .../metrics-range-selector.component.html | 3 +- .../metrics-range-selector.service.ts | 2 +- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html index ad3a547083..3039bd8f28 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html @@ -1,39 +1,40 @@ -
-
- - - - {{time.label}} - - - + +
+
+ + + + {{time.label}} + + + -
-
-
{{ rangeSelectorManager.committedStartEnd[0] | - amDateFormat:'Do MMM YYYY, - HH:mm' }} -
-
to
-
{{ rangeSelectorManager.committedStartEnd[1] | - amDateFormat:'Do MMM YYYY, - HH:mm' }} +
+
+
{{ rangeSelectorManager.committedStartEnd[0] | + amDateFormat:'Do MMM YYYY, + HH:mm' }} +
+
to
+
{{ rangeSelectorManager.committedStartEnd[1] | + amDateFormat:'Do MMM YYYY, + HH:mm' }} +
+ No date range set
- No date range set
-
-
- -
- +
+ +
+ +
-
+
diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss index 78f8654206..f11911f13b 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss @@ -1,34 +1,49 @@ .metrics-range-parent-selector { + &_card { + padding-bottom: 0; + padding-top: 0; + } + &__date-range { align-items: center; display: flex; + &-buttons { margin-left: 10px; } } + &__charts { + padding-top: 20px; + & > * { background-color: white; height: 500px; } } + &__dropdown-wrapper { display: flex; } + &__dropdown-wrapper { align-items: center; display: flex; } + &__selected-range { margin-left: 10px; + &-dates { display: flex; } + &-to { margin: 0 5px; } + &-date { font-weight: bold; } - } + } } diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html index cc154038e7..e655b5cac5 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html @@ -3,8 +3,7 @@ class="metrics-range-selector__overlay">

Choose time window

- +
diff --git a/src/frontend/app/shared/services/metrics-range-selector.service.ts b/src/frontend/app/shared/services/metrics-range-selector.service.ts index d1f7b815ff..03ce4824f0 100644 --- a/src/frontend/app/shared/services/metrics-range-selector.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector.service.ts @@ -26,7 +26,7 @@ export class MetricsRangeSelectorService { queryType: MetricQueryType.QUERY }, { - label: 'Set time window', + label: 'Custom time window', queryType: MetricQueryType.RANGE_QUERY } ]; From 5f89c0eeaaf572a49813acd2b99b05d2ef76fb18 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 1 Oct 2018 15:37:54 +0100 Subject: [PATCH 054/114] Improve cells table post name-->id update --- .../list/list-types/cf-cells/cf-cells-data-source.ts | 2 +- .../list-types/cf-cells/cf-cells-list-config.service.ts | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts index 1e65bd637a..2a1387dfd2 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-data-source.ts @@ -32,7 +32,7 @@ export class CfCellsDataSource getRowUniqueId: (row) => row.metric.bosh_job_id, paginationKey: action.paginationKey, isLocal: true, - transformEntities: [{ type: 'filter', field: CfCellsDataSource.cellNamePath }], + transformEntities: [{ type: 'filter', field: CfCellsDataSource.cellIdPath }], transformEntity: map((response) => { if (!response || response.length === 0) { return []; diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts index 62198af74f..8d6318a41b 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts @@ -25,7 +25,7 @@ export class CfCellsListConfigService extends BaseCfListConfig Date: Mon, 1 Oct 2018 17:39:28 +0100 Subject: [PATCH 055/114] Fix app instance cell link - ensure we associated correct cell with instance by instance_index - if cell info is missing for instance poll until we have it --- .../cf-app-instances-config.service.ts | 10 +--- .../table-cell-cf-cell.component.ts | 46 ++++++++++++++----- src/frontend/app/store/types/metric.types.ts | 1 + 3 files changed, 38 insertions(+), 19 deletions(-) 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 24226c0446..3d9c3e4df5 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 @@ -190,7 +190,7 @@ export class CfAppInstancesConfigService implements IListConfig if (hasMetrics) { this.columns.splice(1, 0, this.cfCellColumn); this.cfCellColumn.cellConfig = { - metricResults$: this.createMetricsResults(entityServiceFactory), + metricEntityService: this.createMetricsResults(entityServiceFactory), cfGuid: this.appService.cfGuid }; } @@ -218,9 +218,7 @@ export class CfAppInstancesConfigService implements IListConfig const metricsAction = new FetchApplicationMetricsAction( this.appService.appGuid, this.appService.cfGuid, - new MetricQueryConfig('firehose_container_metric_cpu_percentage', { - window: '5m' - }), + new MetricQueryConfig('firehose_container_metric_cpu_percentage'), MetricQueryType.QUERY ); return entityServiceFactory.create>>( @@ -229,10 +227,6 @@ export class CfAppInstancesConfigService implements IListConfig metricsAction.metricId, metricsAction, false - ).entityObs$.pipe( - map(entityInfo => entityInfo.entity), - filter(metrics => !!metrics && !!metrics.data && !!metrics.data.result), - map(metrics => metrics.data.result) ); } diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts index e9fc2f589c..b89eeedf12 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts @@ -1,8 +1,9 @@ -import { Component, Input } from '@angular/core'; -import { Observable } from 'rxjs'; +import { Component, Input, OnDestroy } from '@angular/core'; +import { Observable, Subscription } from 'rxjs'; import { filter, map, tap } from 'rxjs/operators'; -import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; +import { EntityService } from '../../../../../../core/entity-service'; +import { IMetricMatrixResult, IMetrics } from '../../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../../store/types/metric.types'; import { TableCellCustom } from '../../../list.types'; import { ListAppInstance } from '../app-instance-types'; @@ -12,27 +13,50 @@ import { ListAppInstance } from '../app-instance-types'; templateUrl: './table-cell-cf-cell.component.html', styleUrls: ['./table-cell-cf-cell.component.scss'] }) -export class TableCellCfCellComponent extends TableCellCustom { +export class TableCellCfCellComponent extends TableCellCustom implements OnDestroy { cellMetric$: Observable; cellLink: string; + fetchMetricsSub: Subscription; @Input('config') set config(config: { - metricResults$: Observable[]> + metricEntityService: EntityService>> cfGuid: string }) { if (!config) { return; } - const { metricResults$, cfGuid } = config; - - this.cellMetric$ = metricResults$.pipe( - filter(metricResults => !!metricResults[this.row.index]), - map(metricResults => metricResults[this.row.index].metric), - tap(metric => this.cellLink = `/cloud-foundry/${cfGuid}/cells/${metric.bosh_job_id}/summary`) + const { metricEntityService, cfGuid } = config; + + this.cellMetric$ = metricEntityService.waitForEntity$.pipe( + filter(entityInfo => !!entityInfo.entity.data && !!entityInfo.entity.data.result), + map((entityInfo) => { + const metricResult = entityInfo.entity.data.result.find(res => res.metric.instance_index === this.row.index.toString()); + return metricResult ? metricResult.metric : null; + }), + tap(metric => { + // No metric? It should exists so start polling to ensure we fetch it. It could be missing if the instance was just created + // and cf hasn't yet emitted metrics for it + if (!metric && !this.fetchMetricsSub) { + this.fetchMetricsSub = metricEntityService.poll(5000).subscribe(); + } + }), + filter(metric => !!metric), + tap(metric => { + this.cellLink = `/cloud-foundry/${cfGuid}/cells/${metric.bosh_job_id}/summary`; + // If we're polled to get metric then make sure to unsub + if (this.fetchMetricsSub) { + this.fetchMetricsSub.unsubscribe(); + } + }) ); + } + ngOnDestroy() { + if (this.fetchMetricsSub) { + this.fetchMetricsSub.unsubscribe(); + } } } diff --git a/src/frontend/app/store/types/metric.types.ts b/src/frontend/app/store/types/metric.types.ts index 44df01daba..3041f3f369 100644 --- a/src/frontend/app/store/types/metric.types.ts +++ b/src/frontend/app/store/types/metric.types.ts @@ -18,6 +18,7 @@ export interface IMetricCell { bosh_job_name: string; environment: string; instance: string; + instance_index: string; job: string; origin: string; } From 3f4dcfac3ec4ef6753368ee1b26e145c91b0fa2e Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Mon, 1 Oct 2018 17:55:38 +0100 Subject: [PATCH 056/114] Separate out instace scaling tests --- .../application-deploy-e2e.spec.ts | 91 +++++++++++-------- 1 file changed, 53 insertions(+), 38 deletions(-) diff --git a/src/test-e2e/application/application-deploy-e2e.spec.ts b/src/test-e2e/application/application-deploy-e2e.spec.ts index 544d3e2863..a689cfba9e 100644 --- a/src/test-e2e/application/application-deploy-e2e.spec.ts +++ b/src/test-e2e/application/application-deploy-e2e.spec.ts @@ -353,49 +353,64 @@ describe('Application Deploy -', function () { expect(appEvents.list.table.getCell(2, 2).getText()).toBe(`person\n${currentUser}`); }); - it('Instance scaling', () => { - const appInstances = new ApplicationPageInstancesTab(appDetails.cfGuid, appDetails.appGuid); - appInstances.goToInstancesTab(); + describe('Instance scaling', () => { + let appInstances; - // Initial state - appInstances.cardStatus.getStatus().then(res => { - expect(res.status).toBe('Deployed'); - expect(res.subStatus).toBe('Online'); + beforeAll(() => { + appInstances = new ApplicationPageInstancesTab(appDetails.cfGuid, appDetails.appGuid); + appInstances.goToInstancesTab(); }); - appInstances.cardInstances.waitForRunningInstancesText('1 / 1'); - expect(appInstances.list.table.getCell(0, 1).getText()).toBe('RUNNING'); - expect(appInstances.cardInstances.editCountButton().isDisplayed()).toBeTruthy(); - expect(appInstances.cardInstances.decreaseCountButton().isDisplayed()).toBeTruthy(); - expect(appInstances.cardInstances.increaseCountButton().isDisplayed()).toBeTruthy(); - - // Scale using edit count form - appInstances.cardInstances.editInstanceCount(2); - appInstances.cardInstances.waitForRunningInstancesText('2 / 2'); - expect(appInstances.list.getTotalResults()).toBe(2); - expect(appInstances.list.table.getCell(0, 1).getText()).toBe('RUNNING'); - expect(appInstances.list.table.getCell(1, 1).getText()).toBe('RUNNING'); - appInstances.cardInstances.editInstanceCount(1); - appInstances.cardInstances.waitForRunningInstancesText('1 / 1'); - expect(appInstances.list.getTotalResults()).toBe(1); - expect(appInstances.list.table.getCell(0, 1).getText()).toBe('RUNNING'); + it('Should show correct initial state', () => { + // Initial state + appInstances.cardStatus.getStatus().then(res => { + expect(res.status).toBe('Deployed'); + expect(res.subStatus).toBe('Online'); + }); + appInstances.cardInstances.waitForRunningInstancesText('1 / 1'); + expect(appInstances.list.table.getCell(0, 1).getText()).toBe('RUNNING'); + expect(appInstances.cardInstances.editCountButton().isDisplayed()).toBeTruthy(); + expect(appInstances.cardInstances.decreaseCountButton().isDisplayed()).toBeTruthy(); + expect(appInstances.cardInstances.increaseCountButton().isDisplayed()).toBeTruthy(); + }); - // Scale using +/- buttons - expect(appInstances.cardInstances.decreaseCountButton().isDisplayed()).toBeTruthy(); - appInstances.cardInstances.decreaseCountButton().click(); - const confirm = new ConfirmDialogComponent(); - confirm.waitUntilShown(); - expect(confirm.getMessage()).toBe('Are you sure you want to set the instance count to 0?'); - confirm.confirm(); - appInstances.cardInstances.waitForRunningInstancesText('0 / 0'); - // Content of empty instance table is tested elsewhere - expect(appInstances.list.getTotalResults()).toBe(0); + it('Should scale up using edit form', () => { + // Scale using edit count form + appInstances.cardInstances.editInstanceCount(2); + appInstances.cardInstances.waitForRunningInstancesText('2 / 2'); + expect(appInstances.list.getTotalResults()).toBe(2); + expect(appInstances.list.table.getCell(0, 1).getText()).toBe('RUNNING'); + expect(appInstances.list.table.getCell(1, 1).getText()).toBe('RUNNING'); + }); - expect(appInstances.cardInstances.decreaseCountButton().isDisplayed()).toBeTruthy(); - appInstances.cardInstances.increaseCountButton().click(); - appInstances.cardInstances.waitForRunningInstancesText('1 / 1'); - expect(appInstances.list.getTotalResults()).toBe(1); - expect(appInstances.list.table.getCell(0, 1).getText()).toBe('RUNNING'); + it('Should scale down using edit form', () => { + // Scale using edit count form + appInstances.cardInstances.editInstanceCount(1); + appInstances.cardInstances.waitForRunningInstancesText('1 / 1'); + expect(appInstances.list.getTotalResults()).toBe(1); + expect(appInstances.list.table.getCell(0, 1).getText()).toBe('RUNNING'); + }); + + it('Should scale to zero using - button', () => { + // Scale using +/- buttons + expect(appInstances.cardInstances.decreaseCountButton().isDisplayed()).toBeTruthy(); + appInstances.cardInstances.decreaseCountButton().click(); + const confirm = new ConfirmDialogComponent(); + confirm.waitUntilShown(); + expect(confirm.getMessage()).toBe('Are you sure you want to set the instance count to 0?'); + confirm.confirm(); + appInstances.cardInstances.waitForRunningInstancesText('0 / 0'); + // Content of empty instance table is tested elsewhere + expect(appInstances.list.getTotalResults()).toBe(0); + }); + + it('Should scale to 1 using + button', () => { + expect(appInstances.cardInstances.increaseCountButton().isDisplayed()).toBeTruthy(); + appInstances.cardInstances.increaseCountButton().click(); + appInstances.cardInstances.waitForRunningInstancesText('1 / 1'); + expect(appInstances.list.getTotalResults()).toBe(1); + expect(appInstances.list.table.getCell(0, 1).getText()).toBe('RUNNING'); + }); }); it('Instance termination', () => { From 5119ed456152a10c9c0e94d258d2ab7977e245ff Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 10:22:23 +0100 Subject: [PATCH 057/114] Fix ordering of check and close --- src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts b/src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts index 71b1dbb644..10f37189ad 100644 --- a/src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts +++ b/src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts @@ -11,7 +11,7 @@ import { SnackBarComponent } from '../po/snackbar.po'; import { ConnectDialogComponent } from './connect-dialog.po'; import { EndpointMetadata, EndpointsPage } from './endpoints.po'; -describe('Endpoints', () => { +fdescribe('Endpoints', () => { const endpointsPage = new EndpointsPage(); describe('Connect/Disconnect endpoints -', () => { @@ -31,8 +31,8 @@ describe('Endpoints', () => { // expect(endpointsPage.isActivePage()).toBeTruthy(); // Close the snack bar telling us that there are no connected endpoints + connectDialog.snackBar.waitForMessage('There are no connected endpoints, connect with your personal credentials to get started.'); connectDialog.snackBar.safeClose(); - connectDialog.snackBar.waitUntilShown('No connected endpoints snackbar'); // Get the row in the table for this endpoint endpointsPage.table.getRowForEndpoint(toConnect.name).then(row => { From fecc5ddcead2766c366e66c660ee84013fd82d18 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 10:22:55 +0100 Subject: [PATCH 058/114] Remove fdescribe --- src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts b/src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts index 10f37189ad..8a6b4bb290 100644 --- a/src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts +++ b/src/test-e2e/endpoints/endpoints-connect-e2e.spec.ts @@ -11,7 +11,7 @@ import { SnackBarComponent } from '../po/snackbar.po'; import { ConnectDialogComponent } from './connect-dialog.po'; import { EndpointMetadata, EndpointsPage } from './endpoints.po'; -fdescribe('Endpoints', () => { +describe('Endpoints', () => { const endpointsPage = new EndpointsPage(); describe('Connect/Disconnect endpoints -', () => { From 03f6ce2d8248085e102a9d3c7fcf3f7fa8d36652 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 2 Oct 2018 10:30:31 +0100 Subject: [PATCH 059/114] Tidy up home component --- .../home/home/home-page.component.html | 6 +- .../features/home/home/home-page.component.ts | 65 +------------------ 2 files changed, 4 insertions(+), 67 deletions(-) diff --git a/src/frontend/app/features/home/home/home-page.component.html b/src/frontend/app/features/home/home/home-page.component.html index f60a79871a..96acc6b904 100644 --- a/src/frontend/app/features/home/home/home-page.component.html +++ b/src/frontend/app/features/home/home/home-page.component.html @@ -1,7 +1,3 @@

Dashboard

-
- - - - \ No newline at end of file + \ No newline at end of file diff --git a/src/frontend/app/features/home/home/home-page.component.ts b/src/frontend/app/features/home/home/home-page.component.ts index d880b1e340..228ab7af34 100644 --- a/src/frontend/app/features/home/home/home-page.component.ts +++ b/src/frontend/app/features/home/home/home-page.component.ts @@ -1,71 +1,12 @@ -import { MetricsAction } from './../../../store/actions/metrics.actions'; -import { Component, OnInit } from '@angular/core'; -import { Store } from '@ngrx/store'; -import { FetchApplicationMetricsAction, MetricQueryConfig } from '../../../store/actions/metrics.actions'; -import { AppState } from '../../../store/app-state'; -import { MetricsChartHelpers } from '../../../shared/components/metrics-chart/metrics.component.helpers'; -import { MetricsConfig } from '../../../shared/components/metrics-chart/metrics-chart.component'; -import { IMetricMatrixResult } from '../../../store/types/base-metric.types'; -import { IMetricApplication } from '../../../store/types/metric.types'; -import { MetricsLineChartConfig } from '../../../shared/components/metrics-chart/metrics-chart.types'; +import { Component } from '@angular/core'; @Component({ selector: 'app-home-page', templateUrl: './home-page.component.html', styleUrls: ['./home-page.component.scss'] }) -export class HomePageComponent implements OnInit { - instanceChartConfig: MetricsLineChartConfig; +export class HomePageComponent { - constructor(private store: Store) { - this.instanceChartConfig = this.buildChartConfig(); + constructor() { } - - instanceMetricConfig: MetricsConfig>[] = [ - { - getSeriesName: result => `Instance ${result.metric.instance_index}`, - mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, - sort: MetricsChartHelpers.sortBySeriesName, - // mapSeriesItemValue: (bytes) => (bytes / 1000000).toFixed(2); - metricsAction: new FetchApplicationMetricsAction( - '23b29b1a-1411-422d-9089-0dd51b1fe57a', - 'rqljU7j5TF-v8_nyozXsd6kDUeU', - new MetricQueryConfig('firehose_container_metric_cpu_percentage') - ) - }, - { - getSeriesName: result => `Instance ${result.metric.instance_index}`, - mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, - sort: MetricsChartHelpers.sortBySeriesName, - mapSeriesItemValue: (bytes) => (bytes / 1000000).toFixed(2), - metricsAction: new FetchApplicationMetricsAction( - '23b29b1a-1411-422d-9089-0dd51b1fe57a', - 'rqljU7j5TF-v8_nyozXsd6kDUeU', - new MetricQueryConfig('firehose_container_metric_memory_bytes') - ) - }, - { - getSeriesName: result => `Instance ${result.metric.instance_index}`, - mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, - sort: MetricsChartHelpers.sortBySeriesName, - mapSeriesItemValue: (bytes) => (bytes / 1000000).toFixed(2), - metricsAction: new FetchApplicationMetricsAction( - '23b29b1a-1411-422d-9089-0dd51b1fe57a', - 'rqljU7j5TF-v8_nyozXsd6kDUeU', - new MetricQueryConfig('firehose_container_metric_disk_bytes') - ) - } - ]; - - - private buildChartConfig() { - const lineChartConfig = new MetricsLineChartConfig(); - lineChartConfig.xAxisLabel = 'Time'; - lineChartConfig.yAxisLabel = 'test'; - return lineChartConfig; - } - - - - ngOnInit() { } } From 04f75d6400ee8850cff9b976bb045a5d615fdf78 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 2 Oct 2018 10:30:53 +0100 Subject: [PATCH 060/114] Fix loading vs refreshing in chart componnt --- .../metrics-chart/metrics-chart.component.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index da29d4999a..d84133f44e 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,7 +1,7 @@ import { AfterContentInit, Component, ContentChild, Input, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; import { Subscription, Observable, combineLatest, timer } from 'rxjs'; -import { map, distinctUntilChanged, startWith, debounce } from 'rxjs/operators'; +import { map, distinctUntilChanged, startWith, debounce, tap } from 'rxjs/operators'; import { MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; @@ -101,12 +101,13 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni entityFactory(metricSchemaKey) ); - - - this.results$ = this.metricsMonitor.entity$.pipe( + const baseResults$ = this.metricsMonitor.entity$.pipe( distinctUntilChanged((oldMetrics, newMetrics) => { return oldMetrics && oldMetrics.data === newMetrics.data; - }), + }) + ); + + this.results$ = baseResults$.pipe( map(metrics => { const metricsArray = this.mapMetricsToChartData(metrics, this.metricsConfig); if (!metricsArray.length) { @@ -116,18 +117,20 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni return this.postFetchMiddleware(metricsArray); }) ); + this.isRefreshing$ = combineLatest( - this.results$, + baseResults$, this.metricsMonitor.isFetchingEntity$ ).pipe( debounce(([results, fetching]) => { return !fetching ? timer(800) : timer(0); }), + tap(console.log), map(([results, fetching]) => results && fetching) ); this.isFetching$ = combineLatest( - this.results$.pipe(startWith(null)), + baseResults$.pipe(startWith(null)), this.metricsMonitor.isFetchingEntity$ ).pipe( map(([results, fetching]) => !results && fetching) From ea59014c07cb936d7e2d58ba974e2e6268529fd7 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 2 Oct 2018 10:49:21 +0100 Subject: [PATCH 061/114] Remove disabled attr from calender input --- .../app/shared/components/date-time/date-time.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/app/shared/components/date-time/date-time.component.html b/src/frontend/app/shared/components/date-time/date-time.component.html index 55bb99ba86..db147af028 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.html +++ b/src/frontend/app/shared/components/date-time/date-time.component.html @@ -1,6 +1,6 @@
- + From 50b3013e02ea4a324615f699f396f035021d6c3f Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Tue, 2 Oct 2018 10:49:46 +0100 Subject: [PATCH 062/114] Improve no results message --- .../components/metrics-chart/metrics-chart.component.html | 6 +++++- .../components/metrics-chart/metrics-chart.component.scss | 7 +++++++ .../components/metrics-chart/metrics-chart.component.ts | 8 +++----- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 35f4986179..43722521d1 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -16,7 +16,11 @@

{{title}}

{{ chartConfig.chartType }} chart type not found
-
No results found
+
No results found +
+ {{ chartConfig.yAxisLabel }} +
+
diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index 6dd20bfc02..563b641348 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -100,4 +100,11 @@ cursor: pointer; margin-left: 10px; } + .no-content { + padding: 20px 15px; + &__name { + font-size: 12px; + padding-top: 5px; + } + } } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index d84133f44e..5797be1a67 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -1,17 +1,17 @@ import { AfterContentInit, Component, ContentChild, Input, OnDestroy, OnInit } from '@angular/core'; import { Store } from '@ngrx/store'; -import { Subscription, Observable, combineLatest, timer } from 'rxjs'; -import { map, distinctUntilChanged, startWith, debounce, tap } from 'rxjs/operators'; +import { combineLatest, Observable, Subscription, timer } from 'rxjs'; +import { debounce, distinctUntilChanged, map, startWith } from 'rxjs/operators'; import { MetricsAction } from '../../../store/actions/metrics.actions'; import { AppState } from '../../../store/app-state'; import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; import { EntityMonitor } from '../../monitors/entity-monitor'; +import { MetricQueryType } from '../../services/metrics-range-selector.types'; import { MetricsRangeSelectorComponent } from '../metrics-range-selector/metrics-range-selector.component'; import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types/base-metric.types'; import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.service'; import { MetricsChartTypes } from './metrics-chart.types'; import { MetricsChartManager } from './metrics.component.manager'; -import { MetricQueryType } from '../../services/metrics-range-selector.types'; export interface MetricsConfig { metricsAction: MetricsAction; @@ -21,7 +21,6 @@ export interface MetricsConfig { sort?: (a: ChartSeries, b: ChartSeries) => number; } export interface MetricsChartConfig { - // Make an enum for this. chartType: MetricsChartTypes; xAxisLabel?: string; yAxisLabel?: string; @@ -125,7 +124,6 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni debounce(([results, fetching]) => { return !fetching ? timer(800) : timer(0); }), - tap(console.log), map(([results, fetching]) => results && fetching) ); From 32d0dc145331f9d2ded6e449f210f3d29728aa90 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Mon, 1 Oct 2018 15:24:10 +0100 Subject: [PATCH 063/114] Metric chart tweaks --- ...trics-parent-range-selector.component.html | 59 ++++++++++--------- ...trics-parent-range-selector.component.scss | 17 +++++- .../metrics-range-selector.component.html | 3 +- .../metrics-range-selector.service.ts | 2 +- 4 files changed, 48 insertions(+), 33 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html index ad3a547083..3039bd8f28 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.html @@ -1,39 +1,40 @@ -
-
- - - - {{time.label}} - - - + +
+
+ + + + {{time.label}} + + + -
-
-
{{ rangeSelectorManager.committedStartEnd[0] | - amDateFormat:'Do MMM YYYY, - HH:mm' }} -
-
to
-
{{ rangeSelectorManager.committedStartEnd[1] | - amDateFormat:'Do MMM YYYY, - HH:mm' }} +
+
+
{{ rangeSelectorManager.committedStartEnd[0] | + amDateFormat:'Do MMM YYYY, + HH:mm' }} +
+
to
+
{{ rangeSelectorManager.committedStartEnd[1] | + amDateFormat:'Do MMM YYYY, + HH:mm' }} +
+ No date range set
- No date range set
-
-
- -
- +
+ +
+ +
-
+
diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss index 78f8654206..f11911f13b 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.scss @@ -1,34 +1,49 @@ .metrics-range-parent-selector { + &_card { + padding-bottom: 0; + padding-top: 0; + } + &__date-range { align-items: center; display: flex; + &-buttons { margin-left: 10px; } } + &__charts { + padding-top: 20px; + & > * { background-color: white; height: 500px; } } + &__dropdown-wrapper { display: flex; } + &__dropdown-wrapper { align-items: center; display: flex; } + &__selected-range { margin-left: 10px; + &-dates { display: flex; } + &-to { margin: 0 5px; } + &-date { font-weight: bold; } - } + } } diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html index cc154038e7..e655b5cac5 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.html @@ -3,8 +3,7 @@ class="metrics-range-selector__overlay">

Choose time window

- +
diff --git a/src/frontend/app/shared/services/metrics-range-selector.service.ts b/src/frontend/app/shared/services/metrics-range-selector.service.ts index dfaa32911c..7782d80e38 100644 --- a/src/frontend/app/shared/services/metrics-range-selector.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector.service.ts @@ -26,7 +26,7 @@ export class MetricsRangeSelectorService { queryType: MetricQueryType.QUERY }, { - label: 'Set time window', + label: 'Custom time window', queryType: MetricQueryType.RANGE_QUERY } ]; From f4e0a7dc0ae206d104aece50997ba2cfa711b031 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 2 Oct 2018 11:02:19 +0100 Subject: [PATCH 064/114] General fixes - Removes `other_content` missing warning - When creating actions on time window/range change use original action - Fixed issued where using `{x="y"}` notation would fail for window based ranges --- .../metrics-chart.component.html | 18 +++---- .../metrics-range-selector.service.ts | 50 +++++++++++-------- .../app/store/actions/metrics.actions.ts | 5 +- 3 files changed, 42 insertions(+), 31 deletions(-) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 43722521d1..426c533c6e 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -7,21 +7,21 @@

{{title}}

+ +
No results found +
+ {{ chartConfig.yAxisLabel }} +
+
+
+ [yAxisLabel]="chartConfig.yAxisLabel" [xAxisLabel]="chartConfig.xAxisLabel" [showXAxisLabel]="true" [showYAxisLabel]="true" + [xAxis]="true" [yAxis]="true" [autoScale]="true"> {{ chartConfig.chartType }} chart type not found
- -
No results found -
- {{ chartConfig.yAxisLabel }} -
-
-
\ No newline at end of file diff --git a/src/frontend/app/shared/services/metrics-range-selector.service.ts b/src/frontend/app/shared/services/metrics-range-selector.service.ts index 7782d80e38..03ce4824f0 100644 --- a/src/frontend/app/shared/services/metrics-range-selector.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector.service.ts @@ -31,32 +31,42 @@ export class MetricsRangeSelectorService { } ]; + private newMetricsAction(action: MetricsAction, newQuery: MetricQueryConfig, queryType: MetricQueryType): MetricsAction { + return { + ...action, + query: newQuery, + queryType + }; + } + public getNewDateRangeAction(action: MetricsAction, start: moment.Moment, end: moment.Moment) { const startUnix = start.unix(); const endUnix = end.unix(); - return new MetricsAction( - action.guid, - action.endpointGuid, - new MetricQueryConfig(action.query.metric, { - start: startUnix, - end: end.unix(), - step: Math.max((endUnix - startUnix) / 200, 0) - }), - action.url, - MetricQueryType.RANGE_QUERY - ); + const { + window, + ...params + } = action.query.params || { window: undefined }; + + return this.newMetricsAction(action, new MetricQueryConfig(action.query.metric, { + ...params, + start: startUnix, + end: end.unix(), + step: Math.max((endUnix - startUnix) / 200, 0) + }), MetricQueryType.RANGE_QUERY); } public getNewTimeWindowAction(action: MetricsAction, window: ITimeRange) { - return new MetricsAction( - action.guid, - action.endpointGuid, - new MetricQueryConfig(action.query.metric, { - window: window.value - }), - action.url, - MetricQueryType.QUERY - ); + const { + start, + end, + step, + ...params + } = action.query.params || { start: undefined, end: undefined, step: undefined }; + + return this.newMetricsAction(action, new MetricQueryConfig(action.query.metric, { + ...params, + window: window.value + }), MetricQueryType.QUERY); } public getDateFromStoreMetric(metrics: IMetrics, times = this.times): StoreMetricTimeRange { diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index a33410bc07..d255d519c9 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -17,8 +17,9 @@ function joinParams(queryConfig: MetricQueryConfig) { const { window = '', ...params - } = queryConfig.params; - const windowString = window ? `{}[${window}]` : ''; + } = queryConfig.params || {}; + const hasSquiggly = queryConfig.metric.endsWith('}'); + const windowString = window ? `${(hasSquiggly ? '' : '{}')}[${window}]` : ''; const paramString = Object.keys(params).reduce((accum, key) => { return accum + `&${key}=${params[key]}`; }, ''); From 27a67003b0cc43a228736efa442e5af7a0346938 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 2 Oct 2018 12:10:47 +0100 Subject: [PATCH 065/114] Bump FetchMetricsAction into type of it's own, store data in EndpointModel --- .../metrics/metrics/metrics.component.ts | 50 +++++++------------ .../app/store/actions/metrics-api.actions.ts | 39 +++++++++++++++ .../app/store/actions/metrics.actions.ts | 13 ++--- .../app/store/effects/metrics.effects.ts | 31 ++++++++++-- .../app/store/effects/system.effects.ts | 22 ++++---- .../reducers/system-endpoints.reducer.ts | 21 ++++++++ .../app/store/types/endpoint.types.ts | 8 +-- 7 files changed, 121 insertions(+), 63 deletions(-) create mode 100644 src/frontend/app/store/actions/metrics-api.actions.ts diff --git a/src/frontend/app/features/metrics/metrics/metrics.component.ts b/src/frontend/app/features/metrics/metrics/metrics.component.ts index 31d60db4a3..3d23f899ef 100644 --- a/src/frontend/app/features/metrics/metrics/metrics.component.ts +++ b/src/frontend/app/features/metrics/metrics/metrics.component.ts @@ -1,18 +1,15 @@ -import { Component, OnInit } from '@angular/core'; -import { MetricsService, MetricsEndpointProvider } from '../services/metrics-service'; - -import { getNameForEndpointType, getIconForEndpoint, EndpointIcon } from '../../endpoints/endpoint-helpers'; -import { Observable } from 'rxjs'; +import { Component } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { getIdFromRoute } from '../../cloud-foundry/cf.helpers'; -import { map, filter, first } from 'rxjs/operators'; -import { IHeaderBreadcrumb } from '../../../shared/components/page-header/page-header.types'; import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { filter, first, map } from 'rxjs/operators'; + +import { IHeaderBreadcrumb } from '../../../shared/components/page-header/page-header.types'; +import { MetricsAPIAction, MetricsAPITargets } from '../../../store/actions/metrics-api.actions'; import { AppState } from '../../../store/app-state'; -import { FetchMetricsAction } from '../../../store/actions/metrics.actions'; -import { EntityMonitorFactory } from '../../../shared/monitors/entity-monitor.factory.service'; -import { IMetrics } from '../../../store/types/base-metric.types'; -import { metricSchemaKey, entityFactory } from '../../../store/helpers/entity-factory'; +import { getIdFromRoute } from '../../cloud-foundry/cf.helpers'; +import { EndpointIcon, getIconForEndpoint, getNameForEndpointType } from '../../endpoints/endpoint-helpers'; +import { MetricsEndpointProvider, MetricsService } from '../services/metrics-service'; interface EndpointMetadata { type: string; @@ -53,11 +50,10 @@ export class MetricsComponent { private activatedRoute: ActivatedRoute, private metricsService: MetricsService, private store: Store, - private entityMonitorFactory: EntityMonitorFactory ) { const metricsGuid = getIdFromRoute(this.activatedRoute, 'metricsId'); - const metricsAction = new FetchMetricsAction(metricsGuid, 'targets'); + const metricsAction = new MetricsAPIAction(metricsGuid, 'targets'); this.store.dispatch(metricsAction); this.metricsEndpoint$ = this.metricsService.metricsEndpoints$.pipe( @@ -75,7 +71,7 @@ export class MetricsComponent { metadata: metadata }; } - )); + )); this.breadcrumbs$ = this.metricsEndpoint$.pipe( map(() => ([ @@ -91,25 +87,15 @@ export class MetricsComponent { first() ); - const metricsMonitor = this.entityMonitorFactory.create( - metricsAction.metricId, - metricSchemaKey, - entityFactory(metricSchemaKey) - ); - - this.jobDetails$ = metricsMonitor.entity$.pipe( - filter(a => !!a), - map((targetsData: any) => { - const mapped = {}; - if (targetsData.activeTargets) { - targetsData.activeTargets.forEach(t => { - if (t.labels && t.labels.job) { - mapped[t.labels.job] = t; - } - }); + this.jobDetails$ = this.metricsEndpoint$.pipe( + filter(mi => !!mi && !!mi.entity.provider && !!mi.entity.provider.metadata && !!mi.entity.provider.metadata.metrics_targets), + map(mi => mi.entity.provider.metadata.metrics_targets), + map((targetsData: MetricsAPITargets) => targetsData.activeTargets.reduce((mapped, t) => { + if (t.labels && t.labels.job) { + mapped[t.labels.job] = t; } return mapped; - }) + }, {})) ); } } diff --git a/src/frontend/app/store/actions/metrics-api.actions.ts b/src/frontend/app/store/actions/metrics-api.actions.ts new file mode 100644 index 0000000000..93eaa9c4c3 --- /dev/null +++ b/src/frontend/app/store/actions/metrics-api.actions.ts @@ -0,0 +1,39 @@ +import { Action } from '@ngrx/store'; +import { environment } from './../../../environments/environment.prod'; + +export const METRIC_API_START = '[Metrics] API Start'; +export const METRIC_API_SUCCESS = '[Metrics] API Success'; +export const METRIC_API_FAILED = '[Metrics] API Failed'; + +const { proxyAPIVersion } = environment; + +export const MetricAPIQueryTypes = { + TARGETS: 'targets' +}; + +export interface MetricAPIResponse { + data: any; + status: string; +} + +export interface MetricsAPITargets { + activeTargets: [{ + labels: { + job: string + } + }]; + droppedTargets: []; +} + +export class MetricsAPIAction implements Action { + public url; + constructor(public endpointGuid: string, public query: string, public queryType = MetricAPIQueryTypes.TARGETS) { + this.url = `/pp/${proxyAPIVersion}/proxy/api/v1/` + query; + } + type = METRIC_API_START; +} + +export class MetricsAPIActionSuccess implements Action { + constructor(public endpointGuid: string, public data: MetricAPIResponse, public queryType = MetricAPIQueryTypes.TARGETS) { } + type = METRIC_API_SUCCESS; +} diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 0ff70f3387..057ff26c90 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -1,10 +1,11 @@ -import { environment } from './../../../environments/environment.prod'; -import { schema } from 'normalizr'; import { Action } from '@ngrx/store'; +import { environment } from './../../../environments/environment.prod'; + export const METRICS_START = '[Metrics] Start'; export const METRICS_START_SUCCESS = '[Metrics] Start succeeded'; export const METRICS_START_FAILED = '[Metrics] Start failed'; + const { proxyAPIVersion } = environment; export abstract class MetricsAction implements Action { @@ -28,14 +29,6 @@ export abstract class MetricsAction implements Action { } } -export class FetchMetricsAction extends MetricsAction { - constructor(public guid: string, public query: string) { - super(guid, query); - this.url = `/pp/${proxyAPIVersion}/proxy/api/v1/` + query; - this.directApi = true; - this.cfGuid = guid; - } -} export class FetchCFMetricsAction extends MetricsAction { public cfGuid: string; constructor(public guid: string, public query: string) { diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index 703894a411..e279bc28ee 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -1,12 +1,15 @@ - -import {catchError, mergeMap, map } from 'rxjs/operators'; import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; -import { Store } from '@ngrx/store'; +import { catchError, map, mergeMap } from 'rxjs/operators'; +import { + METRIC_API_FAILED, + METRIC_API_START, + MetricsAPIAction, + MetricsAPIActionSuccess, +} from '../actions/metrics-api.actions'; import { METRICS_START, MetricsAction } from '../actions/metrics.actions'; -import { AppState } from '../app-state'; import { metricSchemaKey } from '../helpers/entity-factory'; import { IMetricsResponse } from '../types/base-metric.types'; import { IRequestAction, WrapperRequestActionFailed, WrapperRequestActionSuccess } from './../types/request.types'; @@ -17,7 +20,6 @@ export class MetricsEffect { constructor( private actions$: Actions, - private store: Store, private httpClient: HttpClient, ) { } @@ -58,6 +60,25 @@ export class MetricsEffect { })); })); + @Effect() metricsAPI$ = this.actions$.ofType(METRIC_API_START).pipe( + mergeMap(action => { + return this.httpClient.get<{ [cfguid: string]: IMetricsResponse }>(action.url, { + headers: { 'x-cap-cnsi-list': action.endpointGuid } + }).pipe( + map(metrics => { + const metric = metrics[action.endpointGuid]; + return new MetricsAPIActionSuccess(action.endpointGuid, metric); + }) + ).pipe(catchError(errObservable => { + return [ + { + type: METRIC_API_FAILED, + error: errObservable.message + } + ]; + })); + })); + private buildFullUrl(action: MetricsAction) { return `${action.url}/query?query=${action.query}`; } diff --git a/src/frontend/app/store/effects/system.effects.ts b/src/frontend/app/store/effects/system.effects.ts index 661821153e..442d2a2e38 100644 --- a/src/frontend/app/store/effects/system.effects.ts +++ b/src/frontend/app/store/effects/system.effects.ts @@ -1,18 +1,14 @@ - -import {catchError, mergeMap} from 'rxjs/operators'; -import { StartRequestAction, WrapperRequestActionSuccess, WrapperRequestActionFailed } from './../types/request.types'; -import { - IRequestAction -} from '../types/request.types'; -import { endpointStoreNames } from '../types/endpoint.types'; -import { SystemInfo, systemStoreNames } from './../types/system.types'; import { HttpClient } from '@angular/common/http'; -import { GET_SYSTEM_INFO, GetSystemInfo, GetSystemSuccess, GetSystemFailed } from './../actions/system.actions'; +import { Injectable } from '@angular/core'; +import { Actions, Effect } from '@ngrx/effects'; import { Store } from '@ngrx/store'; +import { catchError, mergeMap } from 'rxjs/operators'; + import { AppState } from '../app-state'; -import { Actions, Effect } from '@ngrx/effects'; -import { Injectable } from '@angular/core'; -import { GetAllEndpoints } from '../actions/endpoint.actions'; +import { IRequestAction } from '../types/request.types'; +import { GET_SYSTEM_INFO, GetSystemFailed, GetSystemInfo, GetSystemSuccess } from './../actions/system.actions'; +import { StartRequestAction, WrapperRequestActionFailed, WrapperRequestActionSuccess } from './../types/request.types'; +import { SystemInfo, systemStoreNames } from './../types/system.types'; @Injectable() export class SystemEffects { @@ -47,6 +43,6 @@ export class SystemEffects { new WrapperRequestActionFailed('Could not get system endpoints', associatedAction), new WrapperRequestActionFailed('Could not fetch system info', apiAction) ]; - }), ); + })); })); } diff --git a/src/frontend/app/store/reducers/system-endpoints.reducer.ts b/src/frontend/app/store/reducers/system-endpoints.reducer.ts index 0fd84e4148..15bb022acf 100644 --- a/src/frontend/app/store/reducers/system-endpoints.reducer.ts +++ b/src/frontend/app/store/reducers/system-endpoints.reducer.ts @@ -10,6 +10,7 @@ import { import { IRequestEntityTypeState } from '../app-state'; import { endpointConnectionStatus, EndpointModel } from '../types/endpoint.types'; import { GET_SYSTEM_INFO, GET_SYSTEM_INFO_SUCCESS } from './../actions/system.actions'; +import { MetricsAPIActionSuccess, MetricAPIQueryTypes, METRIC_API_SUCCESS } from '../actions/metrics-api.actions'; export function systemEndpointsReducer(state: IRequestEntityTypeState, action) { switch (action.type) { @@ -28,6 +29,8 @@ export function systemEndpointsReducer(state: IRequestEntityTypeState, action: MetricsAPIActionSuccess) { + if (action.queryType === MetricAPIQueryTypes.TARGETS) { + const existingEndpoint = state[action.endpointGuid]; + return { + ...state, + [action.endpointGuid]: { + ...existingEndpoint, + metadata: { + ...existingEndpoint.metadata, + metrics_targets: action.data.data + } + }, + }; + } + return state; + +} diff --git a/src/frontend/app/store/types/endpoint.types.ts b/src/frontend/app/store/types/endpoint.types.ts index feb05f1ca9..aba9294f03 100644 --- a/src/frontend/app/store/types/endpoint.types.ts +++ b/src/frontend/app/store/types/endpoint.types.ts @@ -1,4 +1,5 @@ import { ScopeStrings } from '../../core/current-user-permissions.config'; +import { MetricsAPITargets } from '../actions/metrics-api.actions'; import { endpointSchemaKey } from '../helpers/entity-factory'; import { RequestSectionKeys, TRequestTypeKeys } from '../reducers/api-request-reducer/types'; @@ -14,9 +15,9 @@ export const endpointStoreNames: { section: TRequestTypeKeys, type: string } = { - section: RequestSectionKeys.Other, - type: endpointSchemaKey - }; + section: RequestSectionKeys.Other, + type: endpointSchemaKey +}; export interface IApiEndpointInfo { ForceQuery: boolean; @@ -43,6 +44,7 @@ export interface EndpointModel { metadata?: { metrics?: string; metrics_job?: string; + metrics_targets?: MetricsAPITargets; }; system_shared_token: boolean; sso_allowed: boolean; From da3945e718980a5f95e9d41103f44c4bff74469c Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 2 Oct 2018 12:12:32 +0100 Subject: [PATCH 066/114] Typo and import fixes --- .../app/store/reducers/system-endpoints.reducer.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/frontend/app/store/reducers/system-endpoints.reducer.ts b/src/frontend/app/store/reducers/system-endpoints.reducer.ts index 15bb022acf..0b296b7de7 100644 --- a/src/frontend/app/store/reducers/system-endpoints.reducer.ts +++ b/src/frontend/app/store/reducers/system-endpoints.reducer.ts @@ -7,10 +7,10 @@ import { DISCONNECT_ENDPOINTS_FAILED, DISCONNECT_ENDPOINTS_SUCCESS, } from '../actions/endpoint.actions'; +import { METRIC_API_SUCCESS, MetricAPIQueryTypes, MetricsAPIActionSuccess } from '../actions/metrics-api.actions'; import { IRequestEntityTypeState } from '../app-state'; import { endpointConnectionStatus, EndpointModel } from '../types/endpoint.types'; import { GET_SYSTEM_INFO, GET_SYSTEM_INFO_SUCCESS } from './../actions/system.actions'; -import { MetricsAPIActionSuccess, MetricAPIQueryTypes, METRIC_API_SUCCESS } from '../actions/metrics-api.actions'; export function systemEndpointsReducer(state: IRequestEntityTypeState, action) { switch (action.type) { @@ -39,7 +39,7 @@ export function systemEndpointsReducer(state: IRequestEntityTypeState { + getAllEndpointIds(fetchingState).forEach(guid => { // Only set checking flag if we don't have a status if (!fetchingState[guid].connectionStatus) { modified = true; @@ -56,7 +56,7 @@ function succeedEndpointInfo(state, action) { const newState = { ...state }; const payload = action.type === GET_SYSTEM_INFO_SUCCESS ? action.payload : action.sessionData; Object.keys(payload.endpoints).forEach(type => { - getAllEnpointIds(newState[type], payload.endpoints[type]).forEach(guid => { + getAllEndpointIds(newState[type], payload.endpoints[type]).forEach(guid => { const endpointInfo = payload.endpoints[type][guid] as EndpointModel; newState[guid] = { ...newState[guid], @@ -90,7 +90,7 @@ function changeEndpointConnectionStatus(state: IRequestEntityTypeState Date: Tue, 2 Oct 2018 13:06:47 +0100 Subject: [PATCH 067/114] Minor typing fix --- src/frontend/app/store/actions/metrics-api.actions.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/frontend/app/store/actions/metrics-api.actions.ts b/src/frontend/app/store/actions/metrics-api.actions.ts index 93eaa9c4c3..93dfa8a293 100644 --- a/src/frontend/app/store/actions/metrics-api.actions.ts +++ b/src/frontend/app/store/actions/metrics-api.actions.ts @@ -17,12 +17,16 @@ export interface MetricAPIResponse { } export interface MetricsAPITargets { - activeTargets: [{ + activeTargets: { labels: { job: string } - }]; - droppedTargets: []; + }[]; + droppedTargets: { + discoveredLabels: { + job: string + } + }[]; } export class MetricsAPIAction implements Action { From 4573bbbdf316ec0f5defa5fa5fa11e084dc1f068 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 13:42:46 +0100 Subject: [PATCH 068/114] Increase test time --- .../cloud-foundry/manage-users-stepper-e2e.spec.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test-e2e/cloud-foundry/manage-users-stepper-e2e.spec.ts b/src/test-e2e/cloud-foundry/manage-users-stepper-e2e.spec.ts index 1d22397416..7cbc0f896c 100644 --- a/src/test-e2e/cloud-foundry/manage-users-stepper-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/manage-users-stepper-e2e.spec.ts @@ -19,6 +19,17 @@ describe('Manage Users Stepper', () => { let manageUsersPage: ManagerUsersPage, cfHelper: CFHelpers, cfGuid, userGuid; + let originalTimeout = 40000; + + beforeEach(function() { + originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL; + jasmine.DEFAULT_TIMEOUT_INTERVAL = 90000; + }); + + afterEach(function() { + jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout; + }); + beforeAll(() => { setUpTestOrgSpaceE2eTest(orgName, spaceName, userName, true).then(res => { cfHelper = res.cfHelper; From aa6e1af35f8018310616c5eeffb6913a80095569 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 13:46:38 +0100 Subject: [PATCH 069/114] Add support for using a dep cache download --- deploy/ci/travis/depcache.sh | 25 +++++++++++++++++++++ deploy/ci/travis/fetch-depcache.sh | 32 +++++++++++++++++++++++++++ deploy/ci/travis/generate-depcache.sh | 8 +++++++ 3 files changed, 65 insertions(+) create mode 100755 deploy/ci/travis/depcache.sh create mode 100755 deploy/ci/travis/fetch-depcache.sh create mode 100755 deploy/ci/travis/generate-depcache.sh diff --git a/deploy/ci/travis/depcache.sh b/deploy/ci/travis/depcache.sh new file mode 100755 index 0000000000..1608f4950b --- /dev/null +++ b/deploy/ci/travis/depcache.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +echo "=============================" +echo "Generate Dep Cache for Travis" +echo "=============================" + +# Install go and dep +apt-get update +apt-get install -y curl +apt-get install -y git +curl -sL -o ./gimme https://raw.githubusercontent.com/travis-ci/gimme/master/gimme +chmod +x ./gimme +eval "$(./gimme 1.9.7)" +mkdir -p /root/go/bin +curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh +export PATH=/root/go/bin:$PATH +export DEPPROJECTROOT=./stratos +cd /stratos +dep ensure -v --vendor-only + +SUM=$(cat Gopkg.toml Gopkg.lock | md5sum | awk '{ print $1 }') +tar -czf travis-vendor-${SUM}.tgz ./vendor +echo $SUM +echo "Completed" + diff --git a/deploy/ci/travis/fetch-depcache.sh b/deploy/ci/travis/fetch-depcache.sh new file mode 100755 index 0000000000..07cd3002a0 --- /dev/null +++ b/deploy/ci/travis/fetch-depcache.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +# Download the go dep cache if configured +DIRPATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../../.. && pwd)" +GODEPCACHEURL=${STRATOS_GODEP_CACHE:false} + +unamestr=`uname` + +if [ "${GODEPCACHEURL}" != "false" ]; then + pushd "$DIRPATH" > /dev/null + if [ "$unamestr" == "Darwin" ]; then + CACHEMD5=$(cat Gopkg.toml Gopkg.lock | md5 -q) + else + CACHEMD5=$(cat Gopkg.toml Gopkg.lock | md5sum | awk '{ print $1 }') + fi + popd > /dev/null + DEPFILE="travis-vendor-${CACHEMD5}.tgz" + echo "Looking for vendor cache file : $DEPFILE" + set +e + + echo "${GODEPCACHEURL}/${DEPFILE}" + curl -L -s -f "${GODEPCACHEURL}/${DEPFILE}" -o depcache.tgz + if [ $? -eq 0 ]; then + echo "Unpacking depcache" + tar -xvf depcache.tgz -C "${DIRPATH}" + else + echo "Could not find vendor cache to download" + fi + set -e +fi + diff --git a/deploy/ci/travis/generate-depcache.sh b/deploy/ci/travis/generate-depcache.sh new file mode 100755 index 0000000000..4c6c481232 --- /dev/null +++ b/deploy/ci/travis/generate-depcache.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Create a vendor cache package for use in Travis + +DIRPATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && cd ../../.. && pwd)" + +echo $DIRPATH +docker run -v ${DIRPATH}:/stratos ubuntu:trusty /stratos/deploy/ci/travis/depcache.sh \ No newline at end of file From dfe84725c8945e360a294aef81363712c4a9c9de Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 13:47:10 +0100 Subject: [PATCH 070/114] Update travis file to use vendor cache download --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 9334298e37..d4889e213a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,6 +67,7 @@ jobs: - sudo apt-get -qq update - sudo apt-get install -y ffmpeg script: + - "./deploy/ci/travis/fetch-depcache.sh" - "./deploy/ci/travis/run-e2e-tests.sh video" - "./deploy/ci/travis/upload-e2e-test-report.sh" notifications: From 3300ff02d0d36ee9ff6234ae10bb31e45d6ea7fd Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 14:17:17 +0100 Subject: [PATCH 071/114] Test for the org/space create failure --- .../organizations-spaces-e2e.spec.ts | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts b/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts index 4558218a58..2a7ac39fd7 100644 --- a/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts @@ -161,5 +161,43 @@ describe('CF - Manage Organizations and Spaces', () => { }); }); }); -}); + it('Should create an org and a space', () => { + const cardView = cloudFoundry.goToOrgView(); + + // Click the add button to add an organization + cloudFoundry.header.clickIconButton('add'); + const modal = new StepperComponent(); + modal.getStepperForm().fill({ + 'orgname': testOrgName + }); + expect(modal.canNext()).toBeTruthy(); + modal.next(); + + cardView.cards.waitUntilShown(); + + // Go to the org and create a space + cardView.cards.findCardByTitle(testOrgName).then(org => { + org.click(); + + cloudFoundry.subHeader.clickItem('Spaces'); + cardView.cards.waitUntilShown(); + const list = new ListComponent(); + list.refresh(); + + // Add space + // Click the add button to add a space + cloudFoundry.header.clickIconButton('add'); + + modal.getStepperForm().fill({ + 'spacename': testSpaceName + }); + expect(modal.canNext()).toBeTruthy(); + modal.next(); + + cloudFoundry.subHeader.clickItem('Spaces'); + cardView.cards.waitUntilShown(); + }); + + }); +}); From 05c55b0cb4d5bee61cab1b3cc80ce96d0aa87766 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 14:47:54 +0100 Subject: [PATCH 072/114] More resilient test --- .../cloud-foundry/manage-users-stepper-e2e.spec.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/test-e2e/cloud-foundry/manage-users-stepper-e2e.spec.ts b/src/test-e2e/cloud-foundry/manage-users-stepper-e2e.spec.ts index 7cbc0f896c..3f1965d7c8 100644 --- a/src/test-e2e/cloud-foundry/manage-users-stepper-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/manage-users-stepper-e2e.spec.ts @@ -1,5 +1,4 @@ -import { protractor } from 'protractor'; - +import { protractor, by, element, browser } from 'protractor'; import { e2e } from '../e2e'; import { E2EHelpers } from '../helpers/e2e-helpers'; import { ManagerUsersPage } from './manage-users-page.po'; @@ -157,6 +156,14 @@ describe('Manage Users Stepper', () => { stpr.next(); expect(stpr.getActiveStepName()).toBe('Confirm'); + // Wait until all of the spinners have gone + const spinners = element.all(by.tagName('mat-progress-spinner')); + browser.wait(function() { + return spinners.isPresent().then(function(present) { + return !present; + }); + }); + // ... action table state after submit expect(confirmStep.actionTable.table.getTableData()).toEqual(createActionTableDate(orgTarget, spaceTarget, 'done')); expect(stpr.canPrevious()).toBeFalsy(); From 3a64c1c558255144d830d328365cf52b13a05bb9 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 14:50:37 +0100 Subject: [PATCH 073/114] Omit logging from the vendor tar file unpacking --- deploy/ci/travis/fetch-depcache.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deploy/ci/travis/fetch-depcache.sh b/deploy/ci/travis/fetch-depcache.sh index 07cd3002a0..8d92ec4815 100755 --- a/deploy/ci/travis/fetch-depcache.sh +++ b/deploy/ci/travis/fetch-depcache.sh @@ -23,7 +23,7 @@ if [ "${GODEPCACHEURL}" != "false" ]; then curl -L -s -f "${GODEPCACHEURL}/${DEPFILE}" -o depcache.tgz if [ $? -eq 0 ]; then echo "Unpacking depcache" - tar -xvf depcache.tgz -C "${DIRPATH}" + tar -xvf depcache.tgz -C "${DIRPATH}" > /dev/null else echo "Could not find vendor cache to download" fi From 9612f9caa6a6d3ef23d9ca6f43cd8e17d598f57e Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 2 Oct 2018 15:32:26 +0100 Subject: [PATCH 074/114] WIP --- .../diagnostics-page.component.ts | 4 +- .../application-instance-chart.component.ts | 1 - .../cf-cell-summary-chart.component.html | 3 - .../cf-cell-summary-chart.component.scss | 0 .../cf-cell-summary-chart.component.spec.ts | 42 ---------- .../cf-cell-summary-chart.component.ts | 76 ------------------- .../cloud-foundry-cell-charts.component.ts | 39 +++++++--- .../cloud-foundry-cell-summary.component.html | 8 +- .../cloud-foundry-cell-summary.component.ts | 3 +- .../cloud-foundry-cell.service.ts | 35 +++++++-- .../cloud-foundry-firehose.component.ts | 13 ++-- .../card-app-usage.component.ts | 24 +++--- ...metrics-parent-range-selector.component.ts | 9 ++- .../metrics-range-selector-manager.service.ts | 21 ++--- .../metrics-range-selector.service.ts | 38 +++++----- .../app/store/actions/metrics.actions.ts | 3 +- 16 files changed, 122 insertions(+), 197 deletions(-) delete mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html delete mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss delete mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts delete mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts diff --git a/src/frontend/app/features/about/diagnostics-page/diagnostics-page.component.ts b/src/frontend/app/features/about/diagnostics-page/diagnostics-page.component.ts index 61d832f214..f75370ea3d 100644 --- a/src/frontend/app/features/about/diagnostics-page/diagnostics-page.component.ts +++ b/src/frontend/app/features/about/diagnostics-page/diagnostics-page.component.ts @@ -1,13 +1,13 @@ import { Component, Inject, OnInit } from '@angular/core'; +import { Meta } from '@angular/platform-browser'; import { Store } from '@ngrx/store'; import { Observable } from 'rxjs'; import { filter, map } from 'rxjs/operators'; + import { Customizations, CustomizationsMetadata } from '../../../core/customizations.types'; import { AppState } from '../../../store/app-state'; import { AuthState } from '../../../store/reducers/auth.reducer'; import { SessionData } from '../../../store/types/auth.types'; -import { Meta } from '@angular/platform-browser'; -import * as moment from 'moment'; @Component({ selector: 'app-diagnostics-page', diff --git a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts index e253a7a96d..0f65b1f030 100644 --- a/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts +++ b/src/frontend/app/features/applications/application/application-instance-chart/application-instance-chart.component.ts @@ -7,7 +7,6 @@ import { MetricsChartHelpers } from '../../../../shared/components/metrics-chart import { IMetricApplication } from '../../../../store/types/metric.types'; import { MetricQueryType } from '../../../../shared/services/metrics-range-selector.types'; -// TODO: RC Remove in upstream PR @Component({ selector: 'app-application-instance-chart', templateUrl: './application-instance-chart.component.html', diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html deleted file mode 100644 index b9c68a70be..0000000000 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.html +++ /dev/null @@ -1,3 +0,0 @@ - \ No newline at end of file diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.scss deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts deleted file mode 100644 index eaf2ad7815..0000000000 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.spec.ts +++ /dev/null @@ -1,42 +0,0 @@ -// import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -// import { RouterTestingModule } from '@angular/router/testing'; - -// import { NoopAnimationsModule } from '@angular/platform-browser/animations'; -// import { CfCellSummaryChartComponent } from './cf-cell-summary-chart.component'; -// import { createBasicStoreModule } from '../../../../../test-framework/store-test-helper'; -// import { CoreModule } from '../../../../../core/core.module'; -// import { SharedModule } from '../../../../../shared/shared.module'; - -// describe('CfCellSummaryChartComponent', () => { -// let component: CfCellSummaryChartComponent; -// let fixture: ComponentFixture; - -// beforeEach(async(() => { -// TestBed.configureTestingModule({ -// declarations: [ -// CfCellSummaryChartComponent -// ], -// imports: [ -// createBasicStoreModule(), -// RouterTestingModule, -// CoreModule, -// SharedModule, -// NoopAnimationsModule -// ], -// providers: [ - -// ] -// }) -// .compileComponents(); -// })); - -// beforeEach(() => { -// fixture = TestBed.createComponent(CfCellSummaryChartComponent); -// component = fixture.componentInstance; -// fixture.detectChanges(); -// }); - -// it('should create', () => { -// expect(component).toBeTruthy(); -// }); -// }); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts deleted file mode 100644 index 361b9555d3..0000000000 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -// import { Component, Input, OnInit } from '@angular/core'; - -// import { MetricsConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.component'; -// import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types'; -// import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; -// import { MetricQueryType } from '../../../../../shared/services/metrics-range-selector.types'; -// import { FetchCFCellMetricsAction, MetricQueryConfig } from '../../../../../store/actions/metrics.actions'; -// import { IMetricMatrixResult } from '../../../../../store/types/base-metric.types'; -// import { IMetricCell } from '../../../../../store/types/metric.types'; - - -// @Component({ -// selector: 'app-cf-cell-summary-chart', -// templateUrl: './cf-cell-summary-chart.component.html', -// styleUrls: ['./cf-cell-summary-chart.component.scss'] -// }) -// export class CfCellSummaryChartComponent implements OnInit { - -// @Input() -// private cellId: string; - -// @Input() -// private endpointGuid: string; - -// @Input() -// private yAxisLabel: string; - -// @Input() -// private showLegend = true; - -// // Prometheus query string -// @Input() -// private queryString: string; - -// @Input() -// private seriesTranslation: string; - -// @Input() -// private queryRange = false; - -// @Input() -// public title: string; - -// public chartConfig: MetricsLineChartConfig; - -// public metricConfig: MetricsConfig>; - -// constructor() { } - -// private buildChartConfig() { -// const lineChartConfig = new MetricsLineChartConfig(); -// lineChartConfig.xAxisLabel = 'Time'; -// lineChartConfig.yAxisLabel = this.yAxisLabel; -// lineChartConfig.showLegend = this.showLegend; -// return lineChartConfig; -// } - -// ngOnInit() { -// this.chartConfig = this.buildChartConfig(); -// this.metricConfig = { -// getSeriesName: result => `Cell ${result.metric.bosh_job_name} (${result.metric.bosh_job_id})`, -// mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, -// sort: MetricsChartHelpers.sortBySeriesName, -// // mapSeriesItemValue: this.mapSeriesItemValue(), -// metricsAction: new FetchCFCellMetricsAction( -// this.endpointGuid, -// this.cellId, -// new MetricQueryConfig(this.queryString), -// // TODO: RC MetricQueryType.RANGE_QUERY causes failure -// this.queryRange ? MetricQueryType.RANGE_QUERY : MetricQueryType.QUERY -// // MetricQueryType.QUERY -// ), -// }; -// } - -// } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts index ac83cf4d4f..6d63d34a3c 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts @@ -1,40 +1,61 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; import { MetricsConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.component'; import { MetricsLineChartConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.types'; +import { + MetricsRangeSelectorManagerService, +} from '../../../../../../shared/services/metrics-range-selector-manager.service'; import { MetricQueryType } from '../../../../../../shared/services/metrics-range-selector.types'; import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../../store/types/metric.types'; import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; +import { MetricsRangeSelectorService } from '../../../../../../shared/services/metrics-range-selector.service'; @Component({ selector: 'app-cloud-foundry-cell-charts', templateUrl: './cloud-foundry-cell-charts.component.html', styleUrls: ['./cloud-foundry-cell-charts.component.scss'], }) -export class CloudFoundryCellChartsComponent { +export class CloudFoundryCellChartsComponent implements OnInit { public metricConfigs: [ MetricsConfig>, MetricsLineChartConfig ][]; - constructor(public cfCellService: CloudFoundryCellService) { + constructor( + public cfCellService: CloudFoundryCellService, + ) { + // private rangeSelectorManager: MetricsRangeSelectorManagerService, + // private rangeSelectorService: MetricsRangeSelectorService this.metricConfigs = [ [ - this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_containers', MetricQueryType.QUERY), - this.cfCellService.buildChartConfig('Available') + this.cfCellService.buildMetricConfig(cfCellService.createPercentageMetric( + 'firehose_value_metric_rep_capacity_remaining_containers', + 'firehose_value_metric_rep_capacity_total_containers'), + MetricQueryType.RANGE_QUERY), + this.cfCellService.buildChartConfig('Containers Used (%)') ], [ - this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_total_disk', MetricQueryType.QUERY), - this.cfCellService.buildChartConfig('Memory Available (MB)') + this.cfCellService.buildMetricConfig(cfCellService.createPercentageMetric( + 'firehose_value_metric_rep_capacity_remaining_memory', + 'firehose_value_metric_rep_capacity_total_memory'), + MetricQueryType.RANGE_QUERY), + this.cfCellService.buildChartConfig('Memory Used (%)') ], [ - this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_total_memory', MetricQueryType.QUERY), - this.cfCellService.buildChartConfig('Disk Available (MB)') + this.cfCellService.buildMetricConfig(cfCellService.createPercentageMetric( + 'firehose_value_metric_rep_capacity_remaining_disk', + 'firehose_value_metric_rep_capacity_total_disk'), + MetricQueryType.RANGE_QUERY), + this.cfCellService.buildChartConfig('Disk Used (%)') ], ]; } + + ngOnInit() { + // this.rangeSelectorManager.selectedTimeRange(this.rangeSelectorService[3]); + } } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html index e90c91fb01..781fadc10e 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.html @@ -32,7 +32,7 @@ - - - - \ No newline at end of file + + + + \ No newline at end of file diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts index bbde7d0a70..fe25145c76 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts @@ -26,7 +26,8 @@ export class CloudFoundryCellSummaryComponent { public cfCellService: CloudFoundryCellService ) { - this.metricsConfig = this.cfCellService.buildMetricConfig('firehose_value_metric_rep_unhealthy_cell', MetricQueryType.QUERY); + this.metricsConfig = this.cfCellService.buildMetricConfig( + `firehose_value_metric_rep_unhealthy_cell${cfCellService.cellId}`, MetricQueryType.QUERY); this.chartConfig = this.cfCellService.buildChartConfig('0 = Healthy, 1 = Unhealthy'); this.status$ = cfCellService.healthy$.pipe( diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index 9eb10ae1ed..d9db51f57e 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -1,4 +1,5 @@ import { Injectable } from '@angular/core'; +import * as moment from 'moment'; import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -7,12 +8,17 @@ import { MetricsConfig } from '../../../../../shared/components/metrics-chart/me import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types'; import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; import { MetricQueryType } from '../../../../../shared/services/metrics-range-selector.types'; -import { FetchCFCellMetricsAction, MetricQueryConfig } from '../../../../../store/actions/metrics.actions'; +import { + FetchCFCellMetricsAction, + IMetricQueryConfigParams, + MetricQueryConfig, +} from '../../../../../store/actions/metrics.actions'; import { entityFactory, metricSchemaKey } from '../../../../../store/helpers/entity-factory'; import { IMetricMatrixResult, IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../store/types/metric.types'; import { ActiveRouteCfCell } from '../../../cf-page.types'; + export const enum CellMetrics { HEALTHY = 'firehose_value_metric_rep_unhealthy_cell', REMAINING_CONTAINERS = 'firehose_value_metric_rep_capacity_remaining_containers', @@ -76,19 +82,30 @@ export class CloudFoundryCellService { getSeriesName: (result: IMetricMatrixResult) => `Cell ${result.metric.bosh_job_id} (${result.metric.bosh_deployment})`, mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, sort: MetricsChartHelpers.sortBySeriesName, - // mapSeriesItemValue: this.mapSeriesItemValue(), metricsAction: new FetchCFCellMetricsAction( this.cfGuid, this.cellId, - new MetricQueryConfig(queryString + `{bosh_job_id="${this.cellId}"}`, {}), - // new MetricQueryConfig(queryString, { bosh_job_id: this.cellId }), // TODO: RC Fix - this does not work - // TODO: RC MetricQueryType.RANGE_QUERY causes failure + new MetricQueryConfig(queryString, this.createMetricQueryConfig(queryRange)), queryRange - // MetricQueryType.QUERY ), }; } + private createMetricQueryConfig(queryRange: MetricQueryType): IMetricQueryConfigParams { + if (queryRange === MetricQueryType.RANGE_QUERY) { + const end = moment(); + const start = moment().subtract(1, 'day'); + const startUnix = start.unix() * 1000; + const endUnix = end.unix() * 1000; + return { + start: startUnix, + end: endUnix, + step: Math.max((endUnix - startUnix) / 200, 0) + }; + } + return {}; + } + public buildChartConfig(yAxisLabel: string): MetricsLineChartConfig { const lineChartConfig = new MetricsLineChartConfig(); lineChartConfig.xAxisLabel = 'Time'; @@ -103,7 +120,6 @@ export class CloudFoundryCellService { this.cfGuid, this.cellId, new MetricQueryConfig(metric + `{bosh_job_id="${this.cellId}"}`, {}), - // new MetricQueryConfig(metric, { bosh_job_id: this.cellId }), MetricQueryType.VALUE ); if (metric === CellMetrics.HEALTHY) { @@ -136,4 +152,9 @@ export class CloudFoundryCellService { ); } + public createPercentageMetric(total: string, remaining: string) { + // Calc % remaining then flip. Yes the math look super wrong but check out the results + return `((${remaining}{bosh_job_id="${this.cellId}"}/${total}{bosh_job_id="${this.cellId}"})-1)*100`; + } + } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-firehose/cloud-foundry-firehose.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-firehose/cloud-foundry-firehose.component.ts index 4f8ccd353b..56ff7b8556 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-firehose/cloud-foundry-firehose.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-firehose/cloud-foundry-firehose.component.ts @@ -1,16 +1,13 @@ -import { OnInit, Component } from '@angular/core'; -import * as moment from 'moment'; -import websocketConnect from 'rxjs-websockets'; +import { Component, OnInit } from '@angular/core'; import { Observable, Subject } from 'rxjs'; -import { catchError, filter, map, share } from 'rxjs/operators'; +import websocketConnect from 'rxjs-websockets'; +import { catchError, filter, share } from 'rxjs/operators'; import { environment } from '../../../../../environments/environment'; -import { LogViewerComponent } from '../../../../shared/components/log-viewer/log-viewer.component'; -import { CloudFoundryEndpointService } from '../../services/cloud-foundry-endpoint.service'; -import { FireHoseItem, HTTP_METHODS } from './cloud-foundry-firehose.types'; -import { CloudFoundryFirehoseFormatter } from './cloud-foundry-firehose-formatter'; import { LoggerService } from '../../../../core/logger.service'; import { UtilsService } from '../../../../core/utils.service'; +import { CloudFoundryEndpointService } from '../../services/cloud-foundry-endpoint.service'; +import { CloudFoundryFirehoseFormatter } from './cloud-foundry-firehose-formatter'; @Component({ selector: 'app-cloud-foundry-firehose', diff --git a/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.ts b/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.ts index 4cb7e8f9d3..567bdfb64c 100644 --- a/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.ts +++ b/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.ts @@ -1,7 +1,7 @@ -import {combineLatest as observableCombineLatest, Observable } from 'rxjs'; +import { combineLatest as observableCombineLatest, Observable } from 'rxjs'; -import {startWith, map, share } from 'rxjs/operators'; +import { startWith, map, share } from 'rxjs/operators'; import { Component, OnInit } from '@angular/core'; import { ApplicationMonitorService } from '../../../../features/applications/application-monitor.service'; @@ -23,16 +23,16 @@ export class CardAppUsageComponent implements OnInit { ngOnInit() { this.appData$ = observableCombineLatest( - this.appMonitor.appMonitor$.pipe(startWith(null)), - this.appService.applicationRunning$, - ).pipe( - map(([monitor, isRunning]) => ({ - monitor: monitor, - isRunning: isRunning, - status: !isRunning ? 'tentative' : pathGet('status.usage', monitor) - })), - share() - ); + this.appMonitor.appMonitor$.pipe(startWith()), + this.appService.applicationRunning$, + ).pipe( + map(([monitor, isRunning]) => ({ + monitor: monitor, + isRunning: isRunning, + status: !isRunning ? 'tentative' : pathGet('status.usage', monitor) + })), + share() + ); this.status$ = this.appData$.pipe( map(data => data.status) ); diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts index 0f7abd39fd..e6103d54e9 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts @@ -1,11 +1,12 @@ -import { Component, OnInit, QueryList, ViewChildren, AfterViewInit, AfterContentInit, ContentChildren } from '@angular/core'; +import { AfterContentInit, Component, ContentChildren, OnInit, QueryList } from '@angular/core'; import { Subscription } from 'rxjs'; + +import { entityFactory, metricSchemaKey } from '../../../store/helpers/entity-factory'; +import { IMetrics } from '../../../store/types/base-metric.types'; import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; import { MetricsRangeSelectorManagerService } from '../../services/metrics-range-selector-manager.service'; -import { MetricsChartComponent } from '../metrics-chart/metrics-chart.component'; import { MetricQueryType } from '../../services/metrics-range-selector.types'; -import { metricSchemaKey, entityFactory } from '../../../store/helpers/entity-factory'; -import { IMetrics } from '../../../store/types/base-metric.types'; +import { MetricsChartComponent } from '../metrics-chart/metrics-chart.component'; @Component({ selector: 'app-metrics-parent-range-selector', diff --git a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts index c2682b3fcf..258577bbcd 100644 --- a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts @@ -1,13 +1,13 @@ -import { MetricsRangeSelectorService } from './metrics-range-selector.service'; -import { MetricsAction } from './../../store/actions/metrics.actions'; -import { Injectable, EventEmitter } from '@angular/core'; - +import { Injectable } from '@angular/core'; import * as moment from 'moment'; -import { MetricQueryType, ITimeRange } from './metrics-range-selector.types'; -import { EntityMonitor } from '../monitors/entity-monitor'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, takeWhile, tap } from 'rxjs/operators'; + import { IMetrics } from '../../store/types/base-metric.types'; -import { Subscription, Subject } from 'rxjs'; -import { debounceTime, tap, takeWhile } from 'rxjs/operators'; +import { EntityMonitor } from '../monitors/entity-monitor'; +import { MetricsAction } from './../../store/actions/metrics.actions'; +import { MetricsRangeSelectorService } from './metrics-range-selector.service'; +import { ITimeRange, MetricQueryType } from './metrics-range-selector.types'; @Injectable() export class MetricsRangeSelectorManagerService { @@ -74,10 +74,11 @@ export class MetricsRangeSelectorManagerService { debounceTime(1), tap(metrics => { if (!this.selectedTimeRange) { - const { timeRange, start, end } = this.metricRangeService.getDateFromStoreMetric(metrics); + const { timeRange, start, end } = this.metricRangeService.getDateFromStoreMetric(metrics, baseAction); if (timeRange.queryType === MetricQueryType.RANGE_QUERY) { - const isDifferent = !start.isSame(this.start) || !end.isSame(this.end); + + const isDifferent = (!start || !end) || !start.isSame(this.start) || !end.isSame(this.end); if (isDifferent) { this.committedStartEnd = [start, end]; } diff --git a/src/frontend/app/shared/services/metrics-range-selector.service.ts b/src/frontend/app/shared/services/metrics-range-selector.service.ts index 03ce4824f0..39cd663a43 100644 --- a/src/frontend/app/shared/services/metrics-range-selector.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector.service.ts @@ -10,21 +10,21 @@ export class MetricsRangeSelectorService { constructor() { } public times: ITimeRange[] = [ - { - value: '5m', - label: 'The past 5 minutes', - queryType: MetricQueryType.QUERY - }, - { - value: '1h', - label: 'The past hour', - queryType: MetricQueryType.QUERY - }, - { - value: '1w', - label: 'The past week', - queryType: MetricQueryType.QUERY - }, + // { + // value: '5m', + // label: 'The past 5 minutes', + // queryType: MetricQueryType.QUERY + // }, + // { + // value: '1h', + // label: 'The past hour', + // queryType: MetricQueryType.QUERY + // }, + // { + // value: '1w', + // label: 'The past week', + // queryType: MetricQueryType.QUERY + // }, { label: 'Custom time window', queryType: MetricQueryType.RANGE_QUERY @@ -69,7 +69,7 @@ export class MetricsRangeSelectorService { }), MetricQueryType.QUERY); } - public getDateFromStoreMetric(metrics: IMetrics, times = this.times): StoreMetricTimeRange { + public getDateFromStoreMetric(metrics: IMetrics, baseAction: MetricsAction, times = this.times): StoreMetricTimeRange { if (metrics) { if (metrics.queryType === MetricQueryType.RANGE_QUERY) { const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); @@ -87,9 +87,13 @@ export class MetricsRangeSelectorService { }; } } else { + const params = baseAction ? baseAction.query.params || {} : {}; const timeRange = this.getDefaultTimeRange(times); + // , baseAction.queryType === MetricQueryType.RANGE_QUERY && params.start return { - timeRange + timeRange, + start: null, // params.start ? moment(params.start) : null, + end: null, // params.end ? moment(params.end) : null }; } } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 1446e844b9..28fc513971 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -19,7 +19,8 @@ function joinParams(queryConfig: MetricQueryConfig) { window = '', ...params } = queryConfig.params || {}; - const hasSquiggly = queryConfig.metric.endsWith('}'); + // If the query contains it's own curly brackets don't add a new set + const hasSquiggly = queryConfig.metric.indexOf('}'); const windowString = window ? `${(hasSquiggly ? '' : '{}')}[${window}]` : ''; const paramString = Object.keys(params).reduce((accum, key) => { return accum + `&${key}=${params[key]}`; From 49b820fd4b73871fe31387eeded0b1907b5ea825 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 17:05:46 +0100 Subject: [PATCH 075/114] Debugging --- src/test-e2e/po/cf-users-list.po.ts | 4 ++-- src/test-e2e/po/component.po.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test-e2e/po/cf-users-list.po.ts b/src/test-e2e/po/cf-users-list.po.ts index b1e7202bba..8372334155 100644 --- a/src/test-e2e/po/cf-users-list.po.ts +++ b/src/test-e2e/po/cf-users-list.po.ts @@ -35,8 +35,8 @@ export class UserRoleChip extends ChipComponent { expect(message).toContain(this.roleText); }); confirm.confirm(); - confirm.waitUntilNotShown(); - return this.waitUntilNotShown(); + confirm.waitUntilNotShown('Confirmation dialog'); + return this.waitUntilNotShown('User Role Chip: ' + this.roleText); } } diff --git a/src/test-e2e/po/component.po.ts b/src/test-e2e/po/component.po.ts index 4c2aaa1581..21d4e416ba 100644 --- a/src/test-e2e/po/component.po.ts +++ b/src/test-e2e/po/component.po.ts @@ -37,8 +37,8 @@ export class Component { }); } - waitUntilNotShown(): promise.Promise { - return browser.wait(until.invisibilityOf(this.locator), 20000); + waitUntilNotShown(description = 'Element'): promise.Promise { + return browser.wait(until.invisibilityOf(this.locator), 20000, description); } protected hasClass(cls, element = this.locator): promise.Promise { From 7fc03d003db57fbcd764bb4e3a9f618194eaed66 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 2 Oct 2018 17:22:32 +0100 Subject: [PATCH 076/114] Fix create org, nav to org exception - When we fetch an org we check if any user is missing any roles collection (there's too many too show) - During this check we assumed the org will always have the users - For the case of a new org there are no users --- src/frontend/app/store/reducers/users.reducer.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/app/store/reducers/users.reducer.ts b/src/frontend/app/store/reducers/users.reducer.ts index 1fb99625ea..eba26d4063 100644 --- a/src/frontend/app/store/reducers/users.reducer.ts +++ b/src/frontend/app/store/reducers/users.reducer.ts @@ -150,6 +150,9 @@ function updateUserMissingRoles(users: IRequestEntityTypeState> = action.response.entities[cfUserSchemaKey]; + if (!usersInResponse) { + return users; + } // Create a delta of the changes, this will ensure we only return an updated state if there are updates const haveUpdatedUsers: boolean = Object.values(usersInResponse).reduce((changes, user) => { From 772e0de3bc3fafe32ce5a5d591caf2b7a3d28d27 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 18:11:41 +0100 Subject: [PATCH 077/114] Speed up Top-Level CF Tests --- .../cf-level/cf-top-level-e2e.spec.ts | 56 ++++++++++--------- .../cf-level/cf-top-level-page.po.ts | 16 +++++- 2 files changed, 44 insertions(+), 28 deletions(-) diff --git a/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts index bed1a7bd63..be888b0235 100644 --- a/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts @@ -2,7 +2,8 @@ import { e2e, E2ESetup } from '../../e2e'; import { E2EConfigCloudFoundry } from '../../e2e.types'; import { ConsoleUserType } from '../../helpers/e2e-helpers'; import { CfTopLevelPage } from './cf-top-level-page.po'; - +import { SideNavMenuItem } from '../../po/side-nav.po'; +import { CFPage } from '../../po/cf-page.po'; describe('CF - Top Level - ', () => { @@ -10,40 +11,40 @@ describe('CF - Top Level - ', () => { let e2eSetup: E2ESetup; let defaultCf: E2EConfigCloudFoundry; - function setup(user: ConsoleUserType) { - e2eSetup = e2e.setup(ConsoleUserType.admin) - .clearAllEndpoints() - .registerDefaultCloudFoundry() - .connectAllEndpoints(user) - .loginAs(user) - .getInfo(user); - } - - function navToCfPage() { - defaultCf = e2e.secrets.getDefaultCFEndpoint(); - const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); - cfPage = CfTopLevelPage.forEndpoint(endpointGuid); - cfPage.navigateTo(); - cfPage.waitForPageOrChildPage(); - cfPage.loadingIndicator.waitUntilNotShown(); - } - - function testTitle() { - expect(cfPage.header.getTitleText()).toBe(defaultCf.name); + // // There is only one CF endpoint registered (since that is what we setup) + const page = new CFPage(); + page.sideNav.goto(SideNavMenuItem.CloudFoundry); + CfTopLevelPage.detect().then(p => { + cfPage = p; + cfPage.waitForPageOrChildPage(); + cfPage.loadingIndicator.waitUntilNotShown(); + }); } + beforeAll(() => { + defaultCf = e2e.secrets.getDefaultCFEndpoint(); + e2eSetup = e2e.setup(ConsoleUserType.admin) + .clearAllEndpoints() + .registerDefaultCloudFoundry() + .connectAllEndpoints(ConsoleUserType.admin) + .connectAllEndpoints(ConsoleUserType.user); + }); describe('As Admin', () => { - beforeEach(() => { - setup(ConsoleUserType.admin); + beforeAll(() => { + e2eSetup.loginAs(ConsoleUserType.admin); }); describe('Basic Tests -', () => { - beforeEach(navToCfPage); - it('Breadcrumb', testTitle); + beforeEach(() => { + }); + + it('Breadcrumb', () => { + expect(cfPage.header.getTitleText()).toBe(defaultCf.name); + }); it('Summary Panel', () => { expect(cfPage.waitForInstanceAddress().getValue()).toBe(defaultCf.url); @@ -67,8 +68,9 @@ describe('CF - Top Level - ', () => { }); describe('As User', () => { - beforeEach(() => { - setup(ConsoleUserType.user); + beforeAll(() => { + e2eSetup = e2e.setup(ConsoleUserType.admin) + .loginAs(ConsoleUserType.user); }); describe('Basic Tests -', () => { diff --git a/src/test-e2e/cloud-foundry/cf-level/cf-top-level-page.po.ts b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-page.po.ts index 9fde58db2c..324e54514f 100644 --- a/src/test-e2e/cloud-foundry/cf-level/cf-top-level-page.po.ts +++ b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-page.po.ts @@ -19,6 +19,20 @@ export class CfTopLevelPage extends CFPage { return page; } + // Detect cfGuid from the URL + public static detect(): promise.Promise { + return browser.getCurrentUrl().then(url => { + if (url.indexOf(browser.baseUrl) === 0) { + url = url.substr(browser.baseUrl.length + 1); + } + const urlParts = url.split('/'); + expect(urlParts.length).toBe(3); + expect(urlParts[0]).toBe('cloud-foundry'); + const cfGuid = urlParts[1]; + return CfTopLevelPage.forEndpoint(cfGuid); + }); + } + // Goto the Organizations view (tab) goToOrgView(): ListComponent { this.subHeader.clickItem('Organizations'); @@ -98,7 +112,7 @@ export class CfTopLevelPage extends CFPage { private goToTab(label: string, urlSuffix: string): promise.Promise { // Some tabs don't appear until the page has fully loaded - so wait until the tab is present const tabElement = this.subHeader.getItem(label); - browser.wait(this.until.presenceOf(tabElement), 10000); + browser.wait(this.until.presenceOf(tabElement), 10000, 'Tab: ' + label); return this.subHeader.goToItemAndWait(label, this.navLink, urlSuffix); } From b218d523cd7598af667942b86fa1607ad3c1af0d Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 2 Oct 2018 18:12:46 +0100 Subject: [PATCH 078/114] Revert earlier changes, just show `remaining` stats on cell charts t ab --- .../cloud-foundry-cell-charts.component.ts | 28 +++----------- .../cloud-foundry-cell.service.ts | 31 +-------------- .../card-app-usage.component.ts | 20 +++++----- .../metrics-range-selector-manager.service.ts | 2 +- .../metrics-range-selector.service.ts | 38 +++++++++---------- 5 files changed, 35 insertions(+), 84 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts index 6d63d34a3c..d2113054ea 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts @@ -16,46 +16,28 @@ import { MetricsRangeSelectorService } from '../../../../../../shared/services/m templateUrl: './cloud-foundry-cell-charts.component.html', styleUrls: ['./cloud-foundry-cell-charts.component.scss'], }) -export class CloudFoundryCellChartsComponent implements OnInit { +export class CloudFoundryCellChartsComponent { public metricConfigs: [ MetricsConfig>, MetricsLineChartConfig ][]; - constructor( - public cfCellService: CloudFoundryCellService, - ) { - // private rangeSelectorManager: MetricsRangeSelectorManagerService, - // private rangeSelectorService: MetricsRangeSelectorService - + constructor(public cfCellService: CloudFoundryCellService) { this.metricConfigs = [ [ - this.cfCellService.buildMetricConfig(cfCellService.createPercentageMetric( - 'firehose_value_metric_rep_capacity_remaining_containers', - 'firehose_value_metric_rep_capacity_total_containers'), - MetricQueryType.RANGE_QUERY), + this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_containers', MetricQueryType.RANGE_QUERY), this.cfCellService.buildChartConfig('Containers Used (%)') ], [ - this.cfCellService.buildMetricConfig(cfCellService.createPercentageMetric( - 'firehose_value_metric_rep_capacity_remaining_memory', - 'firehose_value_metric_rep_capacity_total_memory'), - MetricQueryType.RANGE_QUERY), + this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_memory', MetricQueryType.QUERY), this.cfCellService.buildChartConfig('Memory Used (%)') ], [ - this.cfCellService.buildMetricConfig(cfCellService.createPercentageMetric( - 'firehose_value_metric_rep_capacity_remaining_disk', - 'firehose_value_metric_rep_capacity_total_disk'), - MetricQueryType.RANGE_QUERY), + this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_disk', MetricQueryType.QUERY), this.cfCellService.buildChartConfig('Disk Used (%)') ], ]; } - - ngOnInit() { - // this.rangeSelectorManager.selectedTimeRange(this.rangeSelectorService[3]); - } } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index d9db51f57e..ca2b2c0ae7 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -1,5 +1,4 @@ import { Injectable } from '@angular/core'; -import * as moment from 'moment'; import { combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -8,11 +7,7 @@ import { MetricsConfig } from '../../../../../shared/components/metrics-chart/me import { MetricsLineChartConfig } from '../../../../../shared/components/metrics-chart/metrics-chart.types'; import { MetricsChartHelpers } from '../../../../../shared/components/metrics-chart/metrics.component.helpers'; import { MetricQueryType } from '../../../../../shared/services/metrics-range-selector.types'; -import { - FetchCFCellMetricsAction, - IMetricQueryConfigParams, - MetricQueryConfig, -} from '../../../../../store/actions/metrics.actions'; +import { FetchCFCellMetricsAction, MetricQueryConfig } from '../../../../../store/actions/metrics.actions'; import { entityFactory, metricSchemaKey } from '../../../../../store/helpers/entity-factory'; import { IMetricMatrixResult, IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../store/types/metric.types'; @@ -85,27 +80,12 @@ export class CloudFoundryCellService { metricsAction: new FetchCFCellMetricsAction( this.cfGuid, this.cellId, - new MetricQueryConfig(queryString, this.createMetricQueryConfig(queryRange)), + new MetricQueryConfig(queryString + `{bosh_job_id="${this.cellId}"}`, {}), queryRange ), }; } - private createMetricQueryConfig(queryRange: MetricQueryType): IMetricQueryConfigParams { - if (queryRange === MetricQueryType.RANGE_QUERY) { - const end = moment(); - const start = moment().subtract(1, 'day'); - const startUnix = start.unix() * 1000; - const endUnix = end.unix() * 1000; - return { - start: startUnix, - end: endUnix, - step: Math.max((endUnix - startUnix) / 200, 0) - }; - } - return {}; - } - public buildChartConfig(yAxisLabel: string): MetricsLineChartConfig { const lineChartConfig = new MetricsLineChartConfig(); lineChartConfig.xAxisLabel = 'Time'; @@ -114,7 +94,6 @@ export class CloudFoundryCellService { return lineChartConfig; } - private generate(metric: CellMetrics, isMetric = false): Observable { const action = new FetchCFCellMetricsAction( this.cfGuid, @@ -151,10 +130,4 @@ export class CloudFoundryCellService { map(([remaining, total]) => Number(total) - Number(remaining)) ); } - - public createPercentageMetric(total: string, remaining: string) { - // Calc % remaining then flip. Yes the math look super wrong but check out the results - return `((${remaining}{bosh_job_id="${this.cellId}"}/${total}{bosh_job_id="${this.cellId}"})-1)*100`; - } - } diff --git a/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.ts b/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.ts index 567bdfb64c..dd075584a6 100644 --- a/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.ts +++ b/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.ts @@ -23,16 +23,16 @@ export class CardAppUsageComponent implements OnInit { ngOnInit() { this.appData$ = observableCombineLatest( - this.appMonitor.appMonitor$.pipe(startWith()), - this.appService.applicationRunning$, - ).pipe( - map(([monitor, isRunning]) => ({ - monitor: monitor, - isRunning: isRunning, - status: !isRunning ? 'tentative' : pathGet('status.usage', monitor) - })), - share() - ); + this.appMonitor.appMonitor$.pipe(startWith(null)), + this.appService.applicationRunning$, + ).pipe( + map(([monitor, isRunning]) => ({ + monitor: monitor, + isRunning: isRunning, + status: !isRunning ? 'tentative' : pathGet('status.usage', monitor) + })), + share() + ); this.status$ = this.appData$.pipe( map(data => data.status) ); diff --git a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts index 258577bbcd..19062dd561 100644 --- a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts @@ -74,7 +74,7 @@ export class MetricsRangeSelectorManagerService { debounceTime(1), tap(metrics => { if (!this.selectedTimeRange) { - const { timeRange, start, end } = this.metricRangeService.getDateFromStoreMetric(metrics, baseAction); + const { timeRange, start, end } = this.metricRangeService.getDateFromStoreMetric(metrics); if (timeRange.queryType === MetricQueryType.RANGE_QUERY) { diff --git a/src/frontend/app/shared/services/metrics-range-selector.service.ts b/src/frontend/app/shared/services/metrics-range-selector.service.ts index 39cd663a43..03ce4824f0 100644 --- a/src/frontend/app/shared/services/metrics-range-selector.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector.service.ts @@ -10,21 +10,21 @@ export class MetricsRangeSelectorService { constructor() { } public times: ITimeRange[] = [ - // { - // value: '5m', - // label: 'The past 5 minutes', - // queryType: MetricQueryType.QUERY - // }, - // { - // value: '1h', - // label: 'The past hour', - // queryType: MetricQueryType.QUERY - // }, - // { - // value: '1w', - // label: 'The past week', - // queryType: MetricQueryType.QUERY - // }, + { + value: '5m', + label: 'The past 5 minutes', + queryType: MetricQueryType.QUERY + }, + { + value: '1h', + label: 'The past hour', + queryType: MetricQueryType.QUERY + }, + { + value: '1w', + label: 'The past week', + queryType: MetricQueryType.QUERY + }, { label: 'Custom time window', queryType: MetricQueryType.RANGE_QUERY @@ -69,7 +69,7 @@ export class MetricsRangeSelectorService { }), MetricQueryType.QUERY); } - public getDateFromStoreMetric(metrics: IMetrics, baseAction: MetricsAction, times = this.times): StoreMetricTimeRange { + public getDateFromStoreMetric(metrics: IMetrics, times = this.times): StoreMetricTimeRange { if (metrics) { if (metrics.queryType === MetricQueryType.RANGE_QUERY) { const start = moment.unix(parseInt(metrics.query.params.start as string, 10)); @@ -87,13 +87,9 @@ export class MetricsRangeSelectorService { }; } } else { - const params = baseAction ? baseAction.query.params || {} : {}; const timeRange = this.getDefaultTimeRange(times); - // , baseAction.queryType === MetricQueryType.RANGE_QUERY && params.start return { - timeRange, - start: null, // params.start ? moment(params.start) : null, - end: null, // params.end ? moment(params.end) : null + timeRange }; } } From 8a9faf56dd32978ca12f97fdd3acbf635173b43d Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Tue, 2 Oct 2018 18:15:34 +0100 Subject: [PATCH 079/114] Lint fix --- .../app/shared/components/date-time/date-time.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/app/shared/components/date-time/date-time.component.ts b/src/frontend/app/shared/components/date-time/date-time.component.ts index 53a060a28d..bfc5d335e9 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.ts +++ b/src/frontend/app/shared/components/date-time/date-time.component.ts @@ -31,7 +31,7 @@ export class DateTimeComponent implements OnDestroy { set dateTime(dateTime: moment.Moment) { const empty = !dateTime && this.dateTimeValue !== dateTime; - const validDate = dateTime && dateTime.isValid() && (!this.dateTimeValue || !dateTime.isSame(this.dateTimeValue)) + const validDate = dateTime && dateTime.isValid() && (!this.dateTimeValue || !dateTime.isSame(this.dateTimeValue)); if (empty || validDate) { this.dateTimeValue = dateTime; this.dateTimeChange.emit(this.dateTimeValue); From 29bf14d32e8154e222d59d1fc22036e4677935bc Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Tue, 2 Oct 2018 20:24:11 +0100 Subject: [PATCH 080/114] Speed up org tests --- .../cf-level/cf-top-level-e2e.spec.ts | 2 +- .../org-level/cf-org-level-e2e.spec.ts | 64 +++++++++++-------- .../org-level/cf-org-level-page.po.ts | 17 +++++ 3 files changed, 57 insertions(+), 26 deletions(-) diff --git a/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts index be888b0235..672045754d 100644 --- a/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/cf-level/cf-top-level-e2e.spec.ts @@ -12,7 +12,7 @@ describe('CF - Top Level - ', () => { let defaultCf: E2EConfigCloudFoundry; function navToCfPage() { - // // There is only one CF endpoint registered (since that is what we setup) + // There is only one CF endpoint registered (since that is what we setup) const page = new CFPage(); page.sideNav.goto(SideNavMenuItem.CloudFoundry); CfTopLevelPage.detect().then(p => { diff --git a/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts index 98860dd63f..7e7ac0c646 100644 --- a/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts @@ -5,25 +5,16 @@ import { E2EConfigCloudFoundry } from '../../e2e.types'; import { CFHelpers } from '../../helpers/cf-helpers'; import { ConsoleUserType } from '../../helpers/e2e-helpers'; import { CfOrgLevelPage } from './cf-org-level-page.po'; - +import { CFPage } from '../../po/cf-page.po'; +import { SideNavMenuItem } from '../../po/side-nav.po'; +import { CfTopLevelPage } from '../cf-level/cf-top-level-page.po'; +import { ListComponent } from '../../po/list.po'; describe('CF - Org Level - ', () => { let orgPage: CfOrgLevelPage; let e2eSetup: E2ESetup; let defaultCf: E2EConfigCloudFoundry; - let cfHelper: CFHelpers; - - function setup(user: ConsoleUserType) { - e2eSetup = e2e.setup(ConsoleUserType.admin) - .clearAllEndpoints() - .registerDefaultCloudFoundry() - .connectAllEndpoints(ConsoleUserType.admin) - .connectAllEndpoints(ConsoleUserType.user) - .loginAs(user) - .getInfo(); - cfHelper = new CFHelpers(e2eSetup); - } function testBreadcrumb() { orgPage.breadcrumbs.waitUntilShown(); @@ -41,19 +32,41 @@ describe('CF - Org Level - ', () => { } function navToPage() { - defaultCf = e2e.secrets.getDefaultCFEndpoint(); - const endpointGuid = e2e.helper.getEndpointGuid(e2e.info, defaultCf.name); - browser.wait(cfHelper.fetchOrg(endpointGuid, defaultCf.testOrg).then((org => { - orgPage = CfOrgLevelPage.forEndpoint(endpointGuid, org.metadata.guid); - orgPage.navigateTo(); - orgPage.waitForPageOrChildPage(); - orgPage.loadingIndicator.waitUntilNotShown(); - }))); + const page = new CFPage(); + page.sideNav.goto(SideNavMenuItem.CloudFoundry); + CfTopLevelPage.detect().then(cfPage => { + cfPage.waitForPageOrChildPage(); + cfPage.loadingIndicator.waitUntilNotShown(); + cfPage.goToOrgTab(); + + // Find the Org and click on it + const list = new ListComponent(); + list.cards.getCardsMetadata().then(cards => { + const card = cards.find(c => c.title === defaultCf.testOrg); + expect(card).toBeDefined(); + card.click(); + }); + CfOrgLevelPage.detect().then(o => { + orgPage = o; + orgPage.waitForPageOrChildPage(); + orgPage.loadingIndicator.waitUntilNotShown(); + }); + }); } + beforeAll(() => { + defaultCf = e2e.secrets.getDefaultCFEndpoint(); + e2eSetup = e2e.setup(ConsoleUserType.admin) + .clearAllEndpoints() + .registerDefaultCloudFoundry() + .connectAllEndpoints(ConsoleUserType.admin) + .connectAllEndpoints(ConsoleUserType.user); + }); + describe('As Admin', () => { - beforeEach(() => { - setup(ConsoleUserType.admin); + beforeAll(() => { + e2e.setup(ConsoleUserType.admin) + .loginAs(ConsoleUserType.admin); }); describe('Basic Tests - ', () => { @@ -68,8 +81,9 @@ describe('CF - Org Level - ', () => { }); describe('As User', () => { - beforeEach(() => { - setup(ConsoleUserType.user); + beforeAll(() => { + e2e.setup(ConsoleUserType.user) + .loginAs(ConsoleUserType.user); }); describe('Basic Tests - ', () => { diff --git a/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts index b4105e18c0..64cb3a45a8 100644 --- a/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts +++ b/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts @@ -1,4 +1,5 @@ import { CFPage } from '../../po/cf-page.po'; +import { promise, browser } from 'protractor'; export class CfOrgLevelPage extends CFPage { @@ -9,6 +10,22 @@ export class CfOrgLevelPage extends CFPage { return page; } + // Detect cfGuid from the URL + public static detect(): promise.Promise { + return browser.getCurrentUrl().then(url => { + if (url.indexOf(browser.baseUrl) === 0) { + url = url.substr(browser.baseUrl.length + 1); + } + const urlParts = url.split('/'); + expect(urlParts.length).toBe(5); + expect(urlParts[0]).toBe('cloud-foundry'); + expect(urlParts[2]).toBe('organizations'); + const cfGuid = urlParts[1]; + const orgGuid = urlParts[3]; + return CfOrgLevelPage.forEndpoint(cfGuid, orgGuid); + }); + } + goToSummaryTab() { return this.goToTab('Summary', 'summary'); } From 290438e4f6095351466766c1e6d063552e0a9778 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 3 Oct 2018 07:18:29 +0100 Subject: [PATCH 081/114] Yet more test resilience --- .../application-deploy-e2e.spec.ts | 206 ++++++++++-------- .../org-level/cf-org-level-e2e.spec.ts | 3 +- .../org-level/cf-org-level-page.po.ts | 2 +- .../space-level/cf-space-level-e2e.spec.ts | 92 +++++--- .../space-level/cf-space-level-page.po.ts | 20 ++ 5 files changed, 190 insertions(+), 133 deletions(-) diff --git a/src/test-e2e/application/application-deploy-e2e.spec.ts b/src/test-e2e/application/application-deploy-e2e.spec.ts index a689cfba9e..7eadb4fd77 100644 --- a/src/test-e2e/application/application-deploy-e2e.spec.ts +++ b/src/test-e2e/application/application-deploy-e2e.spec.ts @@ -76,119 +76,137 @@ describe('Application Deploy -', function () { }); // Allow up to 2 minutes for the application to be deployed - it('Should deploy app from GitHub', () => { - const loggingPrefix = 'Application Deploy: Deploy from Github:'; - expect(appWall.isActivePage()).toBeTruthy(); - - // Should be on deploy app modal - const deployApp = appWall.clickDeployApp(); - expect(deployApp.header.getTitleText()).toBe('Deploy'); - - // Check the steps - e2e.debugLog(`${loggingPrefix} Checking Steps`); - deployApp.stepper.getStepNames().then(steps => { - expect(steps.length).toBe(5); - expect(steps[0]).toBe('Cloud Foundry'); - expect(steps[1]).toBe('Source'); - expect(steps[2]).toBe('Source Config'); - expect(steps[3]).toBe('Overrides (Optional)'); - expect(steps[4]).toBe('Deploy'); - }); - e2e.debugLog(`${loggingPrefix} Cf/Org/Space Step`); - expect(deployApp.stepper.getActiveStepName()).toBe('Cloud Foundry'); - promise.all([ - deployApp.stepper.getStepperForm().getText('cf'), - deployApp.stepper.getStepperForm().getText('org'), - deployApp.stepper.getStepperForm().getText('space') - ]).then(([cf, org, space]) => { - if (cf !== 'Cloud Foundry' && org !== 'Organization' && space !== 'Space') { - expect(deployApp.stepper.canNext()).toBeTruthy(); - } else { - expect(deployApp.stepper.canNext()).toBeFalsy(); - } - }); - - // Fill in form - deployApp.stepper.getStepperForm().fill({ 'cf': cfName }); - deployApp.stepper.getStepperForm().fill({ 'org': orgName }); - deployApp.stepper.getStepperForm().fill({ 'space': spaceName }); - expect(deployApp.stepper.canNext()).toBeTruthy(); - - // Press next to get to source step - deployApp.stepper.next(); + describe('Should deploy app from GitHub', () => { - e2e.debugLog(`${loggingPrefix} Source Step`); - expect(deployApp.stepper.getActiveStepName()).toBe('Source'); - expect(deployApp.stepper.canNext()).toBeFalsy(); - deployApp.stepper.getStepperForm().fill({ 'projectname': testApp }); + const loggingPrefix = 'Application Deploy: Deploy from Github:'; + let deployApp; - // Press next to get to source config step - deployApp.stepper.waitUntilCanNext('Next'); - deployApp.stepper.next(); + beforeAll(() => { + // Should be on deploy app modal + deployApp = appWall.clickDeployApp(); + expect(deployApp.header.getTitleText()).toBe('Deploy'); + }); - e2e.debugLog(`${loggingPrefix} Source Config Step`); - expect(deployApp.stepper.getActiveStepName()).toBe('Source Config'); + it('Check deploy steps', () => { + expect(appWall.isActivePage()).toBeTruthy(); + // Check the steps + e2e.debugLog(`${loggingPrefix} Checking Steps`); + deployApp.stepper.getStepNames().then(steps => { + expect(steps.length).toBe(5); + expect(steps[0]).toBe('Cloud Foundry'); + expect(steps[1]).toBe('Source'); + expect(steps[2]).toBe('Source Config'); + expect(steps[3]).toBe('Overrides (Optional)'); + expect(steps[4]).toBe('Deploy'); + }); + }); - const commits = deployApp.getCommitList(); - expect(commits.getHeaderText()).toBe('Select a commit'); + it('Should pass CF/Org/Space Step', () => { + e2e.debugLog(`${loggingPrefix} Cf/Org/Space Step`); + expect(deployApp.stepper.getActiveStepName()).toBe('Cloud Foundry'); + promise.all([ + deployApp.stepper.getStepperForm().getText('cf'), + deployApp.stepper.getStepperForm().getText('org'), + deployApp.stepper.getStepperForm().getText('space') + ]).then(([cf, org, space]) => { + if (cf !== 'Cloud Foundry' && org !== 'Organization' && space !== 'Space') { + expect(deployApp.stepper.canNext()).toBeTruthy(); + } else { + expect(deployApp.stepper.canNext()).toBeFalsy(); + } + }); - expect(deployApp.stepper.canNext()).toBeFalsy(); + // Fill in form + deployApp.stepper.getStepperForm().fill({ 'cf': cfName }); + deployApp.stepper.getStepperForm().fill({ 'org': orgName }); + deployApp.stepper.getStepperForm().fill({ 'space': spaceName }); + expect(deployApp.stepper.canNext()).toBeTruthy(); - commits.getTableData().then(data => { - expect(data.length).toBeGreaterThan(0); + // Press next to get to source step + deployApp.stepper.next(); }); - commits.selectRow(0); - e2e.debugLog(`${loggingPrefix} Select a commit (selected)`); - - deployedCommit = commits.getCell(0, 2).getText(); - expect(deployApp.stepper.canNext()).toBeTruthy(); - - // Press next to get to overrides step - deployApp.stepper.next(); + it('Should pass Source step', () => { + e2e.debugLog(`${loggingPrefix} Source Step`); + expect(deployApp.stepper.getActiveStepName()).toBe('Source'); + expect(deployApp.stepper.canNext()).toBeFalsy(); + deployApp.stepper.getStepperForm().fill({ 'projectname': testApp }); - e2e.debugLog(`${loggingPrefix} Overrides Step`); - expect(deployApp.stepper.canNext()).toBeTruthy(); + // Press next to get to source config step + deployApp.stepper.waitUntilCanNext('Next'); + deployApp.stepper.next(); + }); - const overrides = deployApp.getOverridesForm(); - overrides.waitUntilShown(); - overrides.fill({ name: testAppName, random_route: true }); + it('Should pass Source Config step', () => { + e2e.debugLog(`${loggingPrefix} Source Config Step`); + expect(deployApp.stepper.getActiveStepName()).toBe('Source Config'); + const commits = deployApp.getCommitList(); + expect(commits.getHeaderText()).toBe('Select a commit'); - e2e.debugLog(`${loggingPrefix} Overrides Step - overrides set`); + expect(deployApp.stepper.canNext()).toBeFalsy(); - // Turn off waiting for Angular - the web socket connection is kept open which means the tests will timeout - // waiting for angular if we don't turn off. - browser.waitForAngularEnabled(false); + commits.getTableData().then(data => { + expect(data.length).toBeGreaterThan(0); + }); - // Press next to deploy the app - deployApp.stepper.next(); + commits.selectRow(0); + e2e.debugLog(`${loggingPrefix} Select a commit (selected)`); - e2e.debugLog(`${loggingPrefix} Deploying Step (wait)`); + deployedCommit = commits.getCell(0, 2).getText(); + expect(deployApp.stepper.canNext()).toBeTruthy(); - // Wait for the application to be fully deployed - so we see any errors that occur - deployApp.waitUntilDeployed(); + // Press next to get to overrides step + deployApp.stepper.next(); + }); - // Wait until app summary button can be pressed - deployApp.stepper.waitUntilCanNext('Go to App Summary'); + it('Should pass Overrides step', () => { + e2e.debugLog(`${loggingPrefix} Overrides Step`); + expect(deployApp.stepper.canNext()).toBeTruthy(); - e2e.debugLog(`${loggingPrefix} Deploying Step (after wait)`); - // Click next - deployApp.stepper.next(); + const overrides = deployApp.getOverridesForm(); + overrides.waitUntilShown(); + overrides.fill({ name: testAppName, random_route: true }); - e2e.sleep(1000); + e2e.debugLog(`${loggingPrefix} Overrides Step - overrides set`); + }); - e2e.debugLog(`${loggingPrefix} Waiting For Application Summary Page`); - // Should be app summary - const appSummaryPage = new CFPage('/applications/'); - appSummaryPage.waitForPageOrChildPage(); - appSummaryPage.header.waitForTitleText(testAppName); - browser.wait(ApplicationBasePage.detect() - .then(appSummary => { - appDetails.cfGuid = appSummary.cfGuid; - appDetails.appGuid = appSummary.appGuid; - }), 10000, 'Failed to wait for Application Summary page after deploying application' - ); - }, 120000); + it('Should Deploy Application', () => { + // Turn off waiting for Angular - the web socket connection is kept open which means the tests will timeout + // waiting for angular if we don't turn off. + browser.waitForAngularEnabled(false); + + // Press next to deploy the app + deployApp.stepper.next(); + + e2e.debugLog(`${loggingPrefix} Deploying Step (wait)`); + + // Wait for the application to be fully deployed - so we see any errors that occur + deployApp.waitUntilDeployed(); + + // Wait until app summary button can be pressed + deployApp.stepper.waitUntilCanNext('Go to App Summary'); + + e2e.debugLog(`${loggingPrefix} Deploying Step (after wait)`); + }, 120000); + + it('Should go to App Summary Page', () => { + // Click next + deployApp.stepper.next(); + + e2e.sleep(1000); + e2e.debugLog(`${loggingPrefix} Waiting For Application Summary Page`); + // Should be app summary + const appSummaryPage = new CFPage('/applications/'); + appSummaryPage.waitForPageOrChildPage(); + appSummaryPage.header.waitForTitleText(testAppName); + browser.wait(ApplicationBasePage.detect() + .then(appSummary => { + appDetails.cfGuid = appSummary.cfGuid; + appDetails.appGuid = appSummary.appGuid; + }), 10000, 'Failed to wait for Application Summary page after deploying application' + ); + }); + }); }); describe('Tab Tests -', () => { diff --git a/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts index 7e7ac0c646..1798e25d5d 100644 --- a/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/org-level/cf-org-level-e2e.spec.ts @@ -13,7 +13,6 @@ import { ListComponent } from '../../po/list.po'; describe('CF - Org Level - ', () => { let orgPage: CfOrgLevelPage; - let e2eSetup: E2ESetup; let defaultCf: E2EConfigCloudFoundry; function testBreadcrumb() { @@ -56,7 +55,7 @@ describe('CF - Org Level - ', () => { beforeAll(() => { defaultCf = e2e.secrets.getDefaultCFEndpoint(); - e2eSetup = e2e.setup(ConsoleUserType.admin) + e2e.setup(ConsoleUserType.admin) .clearAllEndpoints() .registerDefaultCloudFoundry() .connectAllEndpoints(ConsoleUserType.admin) diff --git a/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts b/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts index 64cb3a45a8..fed9b4185c 100644 --- a/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts +++ b/src/test-e2e/cloud-foundry/org-level/cf-org-level-page.po.ts @@ -10,7 +10,7 @@ export class CfOrgLevelPage extends CFPage { return page; } - // Detect cfGuid from the URL + // Detect from the URL public static detect(): promise.Promise { return browser.getCurrentUrl().then(url => { if (url.indexOf(browser.baseUrl) === 0) { diff --git a/src/test-e2e/cloud-foundry/space-level/cf-space-level-e2e.spec.ts b/src/test-e2e/cloud-foundry/space-level/cf-space-level-e2e.spec.ts index de421a1045..5b586dc2af 100644 --- a/src/test-e2e/cloud-foundry/space-level/cf-space-level-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/space-level/cf-space-level-e2e.spec.ts @@ -1,30 +1,18 @@ -import { browser } from 'protractor'; - -import { e2e, E2ESetup } from '../../e2e'; +import { e2e } from '../../e2e'; import { E2EConfigCloudFoundry } from '../../e2e.types'; -import { CFHelpers } from '../../helpers/cf-helpers'; import { ConsoleUserType } from '../../helpers/e2e-helpers'; import { CfSpaceLevelPage } from './cf-space-level-page.po'; +import { CFPage } from '../../po/cf-page.po'; +import { SideNavMenuItem } from '../../po/side-nav.po'; +import { CfTopLevelPage } from '../cf-level/cf-top-level-page.po'; +import { CfOrgLevelPage } from '../org-level/cf-org-level-page.po'; +import { ListComponent } from '../../po/list.po'; describe('CF - Space Level -', () => { let spacePage: CfSpaceLevelPage; - let e2eSetup: E2ESetup; let defaultCf: E2EConfigCloudFoundry; - let cfHelper: CFHelpers; - - function setup(user: ConsoleUserType) { - e2eSetup = e2e.setup(ConsoleUserType.admin) - .clearAllEndpoints() - .registerDefaultCloudFoundry() - .connectAllEndpoints(ConsoleUserType.admin) - .connectAllEndpoints(ConsoleUserType.user) - .loginAs(user) - .getInfo(); - cfHelper = new CFHelpers(e2eSetup); - } - function testBreadcrumb() { spacePage.breadcrumbs.waitUntilShown(); spacePage.breadcrumbs.getBreadcrumbs().then(breadcrumbs => { @@ -44,25 +32,56 @@ describe('CF - Space Level -', () => { } function navToPage() { - defaultCf = e2e.secrets.getDefaultCFEndpoint(); - browser.wait( - cfHelper.fetchDefaultSpaceGuid(true) - .then(spaceGuid => { - spacePage = CfSpaceLevelPage.forEndpoint( - CFHelpers.cachedDefaultCfGuid, - CFHelpers.cachedDefaultOrgGuid, - CFHelpers.cachedDefaultSpaceGuid - ); - return spacePage.navigateTo(); - }) - .then(() => spacePage.waitForPageOrChildPage()) - .then(() => spacePage.loadingIndicator.waitUntilNotShown()) - ); + const page = new CFPage(); + page.sideNav.goto(SideNavMenuItem.CloudFoundry); + CfTopLevelPage.detect().then(cfPage => { + cfPage.waitForPageOrChildPage(); + cfPage.loadingIndicator.waitUntilNotShown(); + cfPage.goToOrgTab(); + + // Find the Org and click on it + const list = new ListComponent(); + list.cards.getCardsMetadata().then(cards => { + const card = cards.find(c => c.title === defaultCf.testOrg); + expect(card).toBeDefined(); + card.click(); + }); + CfOrgLevelPage.detect().then(orgPage => { + orgPage.waitForPageOrChildPage(); + orgPage.loadingIndicator.waitUntilNotShown(); + orgPage.goToSpacesTab(); + + // Find the Space and click on it + const spaceList = new ListComponent(); + spaceList.cards.getCardsMetadata().then(cards => { + const card = cards.find(c => c.title === defaultCf.testSpace); + expect(card).toBeDefined(); + card.click(); + }); + CfSpaceLevelPage.detect().then(s => { + spacePage = s; + spacePage.waitForPageOrChildPage(); + spacePage.loadingIndicator.waitUntilNotShown(); + }); + + }); + }); } + + beforeAll(() => { + defaultCf = e2e.secrets.getDefaultCFEndpoint(); + e2e.setup(ConsoleUserType.admin) + .clearAllEndpoints() + .registerDefaultCloudFoundry() + .connectAllEndpoints(ConsoleUserType.admin) + .connectAllEndpoints(ConsoleUserType.user); + }); + describe('As Admin -', () => { - beforeEach(() => { - setup(ConsoleUserType.admin); + beforeAll(() => { + e2e.setup(ConsoleUserType.admin) + .loginAs(ConsoleUserType.admin); }); describe('Basic Tests -', () => { @@ -76,8 +95,9 @@ describe('CF - Space Level -', () => { }); describe('As User -', () => { - beforeEach(() => { - setup(ConsoleUserType.user); + beforeAll(() => { + e2e.setup(ConsoleUserType.admin) + .loginAs(ConsoleUserType.admin); }); describe('Basic Tests -', () => { diff --git a/src/test-e2e/cloud-foundry/space-level/cf-space-level-page.po.ts b/src/test-e2e/cloud-foundry/space-level/cf-space-level-page.po.ts index e06480372a..a4807b3259 100644 --- a/src/test-e2e/cloud-foundry/space-level/cf-space-level-page.po.ts +++ b/src/test-e2e/cloud-foundry/space-level/cf-space-level-page.po.ts @@ -1,4 +1,6 @@ import { CFPage } from '../../po/cf-page.po'; +import { CfOrgLevelPage } from '../org-level/cf-org-level-page.po'; +import { promise, browser } from 'protractor'; export class CfSpaceLevelPage extends CFPage { @@ -7,6 +9,24 @@ export class CfSpaceLevelPage extends CFPage { return new CfSpaceLevelPage(`/cloud-foundry/${guid}/organizations/${orgGuid}/spaces/${spaceGuid}`); } + // Detect from the URL + public static detect(): promise.Promise { + return browser.getCurrentUrl().then(url => { + if (url.indexOf(browser.baseUrl) === 0) { + url = url.substr(browser.baseUrl.length + 1); + } + const urlParts = url.split('/'); + expect(urlParts.length).toBe(7); + expect(urlParts[0]).toBe('cloud-foundry'); + expect(urlParts[2]).toBe('organizations'); + expect(urlParts[4]).toBe('spaces'); + const cfGuid = urlParts[1]; + const orgGuid = urlParts[3]; + const spaceGuid = urlParts[5]; + return CfSpaceLevelPage.forEndpoint(cfGuid, orgGuid, spaceGuid); + }); + } + goToSummaryTab() { return this.goToTab('Summary', 'summary'); } From f361b0bab8ae767c8d6e68f5588bb88390d3e4c6 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 3 Oct 2018 08:26:10 +0100 Subject: [PATCH 082/114] Fix deploy app tests --- src/test-e2e/application/application-deploy-e2e.spec.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test-e2e/application/application-deploy-e2e.spec.ts b/src/test-e2e/application/application-deploy-e2e.spec.ts index 7eadb4fd77..ec196c81e5 100644 --- a/src/test-e2e/application/application-deploy-e2e.spec.ts +++ b/src/test-e2e/application/application-deploy-e2e.spec.ts @@ -63,7 +63,7 @@ describe('Application Deploy -', function () { describe('Deploy process - ', () => { let originalTimeout = 40000; - beforeEach(() => nav.goto(SideNavMenuItem.Applications)); + beforeAll(() => nav.goto(SideNavMenuItem.Applications)); // Might take a bit longer to deploy the app than the global default timeout allows beforeEach(function() { @@ -83,12 +83,13 @@ describe('Application Deploy -', function () { beforeAll(() => { // Should be on deploy app modal + appWall.waitForPage(); + expect(appWall.isActivePage()).toBeTruthy(); deployApp = appWall.clickDeployApp(); - expect(deployApp.header.getTitleText()).toBe('Deploy'); }); it('Check deploy steps', () => { - expect(appWall.isActivePage()).toBeTruthy(); + expect(deployApp.header.getTitleText()).toBe('Deploy'); // Check the steps e2e.debugLog(`${loggingPrefix} Checking Steps`); deployApp.stepper.getStepNames().then(steps => { From 6d3dd469e79385240cc746db0528db9a5c23d3d8 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 3 Oct 2018 08:45:32 +0100 Subject: [PATCH 083/114] Allow timeout to be configured --- protractor.conf.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/protractor.conf.js b/protractor.conf.js index e2d3814f74..dccb79eac4 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -43,6 +43,11 @@ try { const timeout = 40000; const checkSuiteGlob = './src/test-e2e/check/*-e2e.spec.ts'; +if (process.env.STRATOS_SCRIPTS_TIMEOUT) { + timeout = parseInt(process.env.STRATOS_SCRIPTS_TIMEOUT); + console.log('Setting allScriptsTimeout to: ' + timeout); +} + // Allow test report to show relative times of tests const specReporterCustomProcessors = []; if (process.env.STRATOS_E2E_LOG_TIME) { From 6b8717a53a73b60379997ae5b58f793a30a17a29 Mon Sep 17 00:00:00 2001 From: Nathan Jones Date: Wed, 3 Oct 2018 10:12:43 +0100 Subject: [PATCH 084/114] Added back in start action to metrics effect --- src/frontend/app/store/effects/metrics.effects.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index 507b142dcb..3e682b8cd8 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -1,3 +1,4 @@ +import { AppState } from './../app-state'; import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Actions, Effect } from '@ngrx/effects'; @@ -7,6 +8,7 @@ import { getFullMetricQueryQuery, MetricsAction, METRICS_START } from '../action import { metricSchemaKey } from '../helpers/entity-factory'; import { IMetricsResponse } from '../types/base-metric.types'; import { IRequestAction, StartRequestAction, WrapperRequestActionFailed, WrapperRequestActionSuccess } from './../types/request.types'; +import { Store } from '@ngrx/store'; @Injectable() export class MetricsEffect { @@ -14,6 +16,7 @@ export class MetricsEffect { constructor( private actions$: Actions, private httpClient: HttpClient, + private store: Store ) { } @Effect() metrics$ = this.actions$.ofType(METRICS_START).pipe( From a6320c133a42d1f83505d7f3f1e63f060ddc3925 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 3 Oct 2018 10:18:02 +0100 Subject: [PATCH 085/114] Minor fixes --- .../cloud-foundry-cell-charts.component.ts | 6 +++--- .../cloud-foundry-cell-summary.component.ts | 3 +-- .../list-types/cf-cells/cf-cells-list-config.service.ts | 1 - src/frontend/app/store/actions/metrics.actions.ts | 6 ++---- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts index d2113054ea..07540eac8c 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts @@ -27,15 +27,15 @@ export class CloudFoundryCellChartsComponent { this.metricConfigs = [ [ this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_containers', MetricQueryType.RANGE_QUERY), - this.cfCellService.buildChartConfig('Containers Used (%)') + this.cfCellService.buildChartConfig('Containers Remaining') ], [ this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_memory', MetricQueryType.QUERY), - this.cfCellService.buildChartConfig('Memory Used (%)') + this.cfCellService.buildChartConfig('Memory Remaining (MB)') ], [ this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_disk', MetricQueryType.QUERY), - this.cfCellService.buildChartConfig('Disk Used (%)') + this.cfCellService.buildChartConfig('Disk Remaining (MB)') ], ]; diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts index fe25145c76..7093ad8c8c 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts @@ -26,8 +26,7 @@ export class CloudFoundryCellSummaryComponent { public cfCellService: CloudFoundryCellService ) { - this.metricsConfig = this.cfCellService.buildMetricConfig( - `firehose_value_metric_rep_unhealthy_cell${cfCellService.cellId}`, MetricQueryType.QUERY); + this.metricsConfig = this.cfCellService.buildMetricConfig(`firehose_value_metric_rep_unhealthy_cell`, MetricQueryType.QUERY); this.chartConfig = this.cfCellService.buildChartConfig('0 = Healthy, 1 = Unhealthy'); this.status$ = cfCellService.healthy$.pipe( diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts index 8d6318a41b..e567845321 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts @@ -6,7 +6,6 @@ import { ListView } from '../../../../../store/actions/list.actions'; import { AppState } from '../../../../../store/app-state'; import { IMetricVectorResult } from '../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../store/types/metric.types'; -import { getIntegerFieldSortFunction } from '../../data-sources-controllers/local-filtering-sorting'; import { TableCellBooleanIndicatorComponent, TableCellBooleanIndicatorComponentConfig, diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 28fc513971..2a7f9b5048 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -20,11 +20,9 @@ function joinParams(queryConfig: MetricQueryConfig) { ...params } = queryConfig.params || {}; // If the query contains it's own curly brackets don't add a new set - const hasSquiggly = queryConfig.metric.indexOf('}'); + const hasSquiggly = queryConfig.metric.indexOf('}') >= 0; const windowString = window ? `${(hasSquiggly ? '' : '{}')}[${window}]` : ''; - const paramString = Object.keys(params).reduce((accum, key) => { - return accum + `&${key}=${params[key]}`; - }, ''); + const paramString = Object.keys(params).reduce((accum, key) => accum + `&${key}=${params[key]}`, ''); return windowString + paramString || ''; } From 8929e774dd039b1a9666b62a858c92ac4c9c2d72 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 3 Oct 2018 11:52:13 +0100 Subject: [PATCH 086/114] Remove rogue self dependency --- Gopkg.lock | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index c8894e97eb..c8321e1e51 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -171,30 +171,6 @@ pruneopts = "UT" revision = "7dc373669fa10ddf827c37c595dee30a2f001be9" -[[projects]] - digest = "1:ce6d7efd1b0df664411b6ffbe83fca8d14b20d3cb82e222a8be1b7c4d93b38e0" - name = "github.com/cloudfoundry-incubator/stratos" - packages = [ - "src/jetstream/config", - "src/jetstream/datastore", - "src/jetstream/plugins/cfapppush", - "src/jetstream/plugins/cfapppush/pushapp", - "src/jetstream/plugins/cfappssh", - "src/jetstream/plugins/cloudfoundry", - "src/jetstream/plugins/cloudfoundryhosting", - "src/jetstream/plugins/metrics", - "src/jetstream/plugins/userinfo", - "src/jetstream/repository/cnsis", - "src/jetstream/repository/console_config", - "src/jetstream/repository/crypto", - "src/jetstream/repository/goose-db-version", - "src/jetstream/repository/interfaces", - "src/jetstream/repository/tokens", - ] - pruneopts = "UT" - revision = "74090ba09e68dcd9caae73436fe17d11cbb90f2a" - version = "2.1.1" - [[projects]] digest = "1:24fc51072bca32204c162b79ede835505b3c5c5cce4e2e0cd6184ff2376486c9" name = "github.com/cloudfoundry/bosh-cli" From ea22460052352c1d66904dc14a5bc7eace49654d Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 3 Oct 2018 12:39:58 +0100 Subject: [PATCH 087/114] Removed custom.module.ts, small tidy ups --- src/frontend/app/custom.module.ts | 1 - .../metrics-chart/metrics-chart.component.theme.scss | 3 ++- .../metrics-parent-range-selector.component.ts | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) delete mode 120000 src/frontend/app/custom.module.ts diff --git a/src/frontend/app/custom.module.ts b/src/frontend/app/custom.module.ts deleted file mode 120000 index d55e73834c..0000000000 --- a/src/frontend/app/custom.module.ts +++ /dev/null @@ -1 +0,0 @@ -/Users/nathanjones/go/src/github.com/cloudfoundry-incubator/stratos/src/frontend/misc/custom/custom.module.ts_ \ No newline at end of file diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss index 589c94951a..93f12229cb 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.theme.scss @@ -1,10 +1,11 @@ @import '~@angular/material/theming'; + @mixin metrics-chart-theme($theme, $app-theme) { $primary: map-get($theme, primary); + .metrics-chart { &__overlay { &-inner { - // background-color: mat-color($primary, 500); background-color: mat-contrast($primary, 500); } } diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts index 0f7abd39fd..315941917b 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts @@ -15,7 +15,7 @@ import { IMetrics } from '../../../store/types/base-metric.types'; MetricsRangeSelectorManagerService ] }) -export class MetricsParentRangeSelectorComponent implements OnInit, AfterContentInit { +export class MetricsParentRangeSelectorComponent implements AfterContentInit { private actionSub: Subscription; @ContentChildren(MetricsChartComponent) @@ -53,8 +53,4 @@ export class MetricsParentRangeSelectorComponent implements OnInit, AfterContent }); } - - ngOnInit() { - } - } From e27603d74b81ad477b101e40517b61a3b9168266 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 3 Oct 2018 13:27:44 +0100 Subject: [PATCH 088/114] Fix vertical sizing for charts with time/date selectors --- .../metrics-chart.component.scss | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss index 563b641348..1ef4df3eec 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.scss @@ -1,35 +1,46 @@ @import '../../../../sass/mixins'; + .metrics-chart { $animation-function: cubic-bezier(0, 0, .2, 1); $animation-time: 250ms; + display: flex; + flex: 1; + height: 100%; width: 100%; + &__header { align-items: center; display: flex; } + &__card { height: 300px; margin-bottom: 15px; } + &__outer { display: flex; flex-direction: column; height: 100%; width: 100%; } + &__title { margin: 0 10px 10px; text-align: left; } + &__loading { margin: auto; min-width: 100px; width: 10%; } + &__refreshing { position: absolute; } + &__overlay { background-color: rgba(0, 0, 0, 0); bottom: 0; @@ -41,9 +52,11 @@ transition: visibility 0s linear $animation-time, background-color $animation-time $animation-function; visibility: hidden; z-index: 1; + &-buttons { margin-top: 10px; } + &-click { bottom: 0; left: 0; @@ -51,10 +64,12 @@ right: 0; top: 0; } + &-show { background-color: rgba(0, 0, 0, .6); transition-delay: 0s; visibility: visible; + .metrics-chart__overlay-inner { transform: translateY(0); } @@ -77,31 +92,39 @@ overflow: hidden; } } + &__selected-range { align-items: center; display: flex; } + &__selected-range-dates { display: none; font-size: 14px; opacity: .6; padding-left: 10px; + @include breakpoint(tablet) { display: flex; } } + &__selected-range-date { font-weight: bold; } + &__selected-range-to { padding: 0 10px; } + &__selected-range-edit { cursor: pointer; margin-left: 10px; } + .no-content { padding: 20px 15px; + &__name { font-size: 12px; padding-top: 5px; From 2bfa68be85d5e33fbf34fe773f5d385d41e34e2c Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 3 Oct 2018 13:28:03 +0100 Subject: [PATCH 089/114] Improve cell healthy chart --- .../cloud-foundry-cell-summary.component.scss | 4 ---- .../cloud-foundry-cell-summary.component.ts | 8 ++++++-- .../cloud-foundry-cell/cloud-foundry-cell.service.ts | 7 +++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss index 76efc96bd6..e29f568f97 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.scss @@ -1,7 +1,3 @@ -.metric-chart-wrapper { - height: 500px; -} - mat-card { margin-bottom: 20px; } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts index 7093ad8c8c..a0abf625ab 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts @@ -26,8 +26,12 @@ export class CloudFoundryCellSummaryComponent { public cfCellService: CloudFoundryCellService ) { - this.metricsConfig = this.cfCellService.buildMetricConfig(`firehose_value_metric_rep_unhealthy_cell`, MetricQueryType.QUERY); - this.chartConfig = this.cfCellService.buildChartConfig('0 = Healthy, 1 = Unhealthy'); + this.metricsConfig = this.cfCellService.buildMetricConfig( + `firehose_value_metric_rep_unhealthy_cell`, + MetricQueryType.QUERY, + (value) => value === '0' ? '1' : '0' + ); + this.chartConfig = this.cfCellService.buildChartConfig('Cell Healthy (1)'); this.status$ = cfCellService.healthy$.pipe( map(health => { diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index ca2b2c0ae7..c2479a41b1 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -72,11 +72,14 @@ export class CloudFoundryCellService { } - public buildMetricConfig(queryString: string, queryRange: MetricQueryType): MetricsConfig> { + public buildMetricConfig( + queryString: string, + queryRange: MetricQueryType, + mapSeriesItemValue?: (value) => any): MetricsConfig> { return { getSeriesName: (result: IMetricMatrixResult) => `Cell ${result.metric.bosh_job_id} (${result.metric.bosh_deployment})`, mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, - sort: MetricsChartHelpers.sortBySeriesName, + mapSeriesItemValue, metricsAction: new FetchCFCellMetricsAction( this.cfGuid, this.cellId, From 6b0e45eb76dfb9736952edd43e7b91fcf13fd21d Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 3 Oct 2018 13:28:07 +0100 Subject: [PATCH 090/114] Make instance termination more resilient --- src/test-e2e/application/application-deploy-e2e.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test-e2e/application/application-deploy-e2e.spec.ts b/src/test-e2e/application/application-deploy-e2e.spec.ts index ec196c81e5..7edf9c8ee3 100644 --- a/src/test-e2e/application/application-deploy-e2e.spec.ts +++ b/src/test-e2e/application/application-deploy-e2e.spec.ts @@ -451,8 +451,7 @@ describe('Application Deploy -', function () { expect(confirm.getMessage()).toBe('Are you sure you want to terminate instance 0?'); confirm.confirm(); appInstances.cardInstances.waitForRunningInstancesText('0 / 1'); - appInstances.list.empty.getDefault().waitUntilShown(); - expect(appInstances.list.getTotalResults()).toBe(0); + // The instance can come back very quickly, so we can't check for 0 rows }); afterAll(() => applicationE2eHelper.deleteApplication(null, { appName: testAppName })); From 346265312853dd7225bad96aa85bc31f9280ed9b Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 3 Oct 2018 13:32:40 +0100 Subject: [PATCH 091/114] Fix linting --- .../app/shared/components/date-time/date-time.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/app/shared/components/date-time/date-time.component.ts b/src/frontend/app/shared/components/date-time/date-time.component.ts index 53a060a28d..bfc5d335e9 100644 --- a/src/frontend/app/shared/components/date-time/date-time.component.ts +++ b/src/frontend/app/shared/components/date-time/date-time.component.ts @@ -31,7 +31,7 @@ export class DateTimeComponent implements OnDestroy { set dateTime(dateTime: moment.Moment) { const empty = !dateTime && this.dateTimeValue !== dateTime; - const validDate = dateTime && dateTime.isValid() && (!this.dateTimeValue || !dateTime.isSame(this.dateTimeValue)) + const validDate = dateTime && dateTime.isValid() && (!this.dateTimeValue || !dateTime.isSame(this.dateTimeValue)); if (empty || validDate) { this.dateTimeValue = dateTime; this.dateTimeChange.emit(this.dateTimeValue); From 4fedc940965734d06066d47375dff561f488d1a5 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 3 Oct 2018 13:49:38 +0100 Subject: [PATCH 092/114] Remove old code --- .../app/features/cloud-foundry/cloud-foundry.module.ts | 4 ---- .../cloud-foundry-cell-base.component.scss | 5 +---- .../cloud-foundry-cell-charts.component.scss | 5 +---- .../cloud-foundry-cell-charts.component.spec.ts | 2 -- .../cloud-foundry-cell-summary.component.spec.ts | 4 +--- .../components/metrics-chart/metrics-chart.component.html | 3 --- 6 files changed, 3 insertions(+), 20 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts index ad1f59c1cc..e246869b6a 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts @@ -28,9 +28,6 @@ import { EditSpaceComponent } from './edit-space/edit-space.component'; import { CloudFoundryEndpointService } from './services/cloud-foundry-endpoint.service'; import { CloudFoundryOrganizationService } from './services/cloud-foundry-organization.service'; import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; -// import { -// CfCellSummaryChartComponent, -// } from './tabs/cloud-foundry-cells/cf-cell-summary-chart/cf-cell-summary-chart.component'; import { CloudFoundryCellBaseComponent, } from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component'; @@ -108,7 +105,6 @@ import { UsersRolesComponent } from './users/manage-users/manage-users.component CloudFoundryCellBaseComponent, CloudFoundryCellSummaryComponent, CloudFoundryCellChartsComponent, - // CfCellSummaryChartComponent, CloudFoundryBuildPacksComponent, CloudFoundryStacksComponent, CloudFoundrySecurityGroupsComponent, diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.scss index c149aedff7..8b13789179 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.scss +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.scss @@ -1,4 +1 @@ -.metric-chart-wrapper { - height: 500px; - margin-bottom: 20px; -} + diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.scss index c149aedff7..8b13789179 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.scss +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.scss @@ -1,4 +1 @@ -.metric-chart-wrapper { - height: 500px; - margin-bottom: 20px; -} + diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts index c51f36d678..ff3a89bb9e 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.spec.ts @@ -2,7 +2,6 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; import { ActiveRouteCfCell } from '../../../../cf-page.types'; -import { CfCellSummaryChartComponent } from '../../cf-cell-summary-chart/cf-cell-summary-chart.component'; import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; import { CloudFoundryCellChartsComponent } from './cloud-foundry-cell-charts.component'; @@ -14,7 +13,6 @@ describe('CloudFoundryCellChartsComponent', () => { TestBed.configureTestingModule({ declarations: [ CloudFoundryCellChartsComponent, - CfCellSummaryChartComponent ], imports: [...BaseTestModules], providers: [ diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts index db80996f64..ff05aeec84 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts @@ -1,11 +1,10 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { of as observableOf } from 'rxjs'; import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; import { ActiveRouteCfCell } from '../../../../cf-page.types'; -import { CfCellSummaryChartComponent } from '../../cf-cell-summary-chart/cf-cell-summary-chart.component'; import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; import { CloudFoundryCellSummaryComponent } from './cloud-foundry-cell-summary.component'; -import { of as observableOf } from 'rxjs'; class MockCloudFoundryCellService { cfGuid = 'cfGuid'; @@ -37,7 +36,6 @@ describe('CloudFoundryCellSummaryComponent', () => { TestBed.configureTestingModule({ declarations: [ CloudFoundryCellSummaryComponent, - CfCellSummaryChartComponent ], imports: [...BaseTestModules], providers: [ diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index cd63aa3162..8489f45a21 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -23,8 +23,5 @@

{{title}}

{{ chartConfig.chartType }} chart type not found
- -
No results found
-
\ No newline at end of file From aa72d56bb52836157728dc2712c7729b92b9f967 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 3 Oct 2018 14:29:09 +0100 Subject: [PATCH 093/114] Fix tests + fix sub leak --- .../application-wall.component.spec.ts | 5 ++--- ...ics-parent-range-selector.component.spec.ts | 18 ++++++++++++++++-- .../metrics-parent-range-selector.component.ts | 13 +++++++++++-- .../metrics-range-selector.component.spec.ts | 18 ++++++++++++++++-- ...rics-range-selector-manager.service.spec.ts | 6 +++++- .../metrics-range-selector-manager.service.ts | 16 ++++++++-------- 6 files changed, 58 insertions(+), 18 deletions(-) diff --git a/src/frontend/app/features/applications/application-wall/application-wall.component.spec.ts b/src/frontend/app/features/applications/application-wall/application-wall.component.spec.ts index 991ab41a7b..90f473a4dc 100644 --- a/src/frontend/app/features/applications/application-wall/application-wall.component.spec.ts +++ b/src/frontend/app/features/applications/application-wall/application-wall.component.spec.ts @@ -1,13 +1,12 @@ +import { DatePipe } from '@angular/common'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { RouterTestingModule } from '@angular/router/testing'; import { CoreModule } from '../../../core/core.module'; -import { MDAppModule } from '../../../core/md.module'; import { SharedModule } from '../../../shared/shared.module'; -import { ApplicationWallComponent } from './application-wall.component'; -import { DatePipe } from '@angular/common'; import { createBasicStoreModule } from '../../../test-framework/store-test-helper'; +import { ApplicationWallComponent } from './application-wall.component'; describe('ApplicationWallComponent', () => { let component: ApplicationWallComponent; diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.spec.ts b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.spec.ts index 30befca0a6..07158364bc 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.spec.ts +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.spec.ts @@ -1,5 +1,13 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { DateFormatPipe } from 'ngx-moment'; +import { CoreModule } from '../../../core/core.module'; +import { createBasicStoreModule } from '../../../test-framework/store-test-helper'; +import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; +import { MetricsRangeSelectorService } from '../../services/metrics-range-selector.service'; +import { DateTimeComponent } from '../date-time/date-time.component'; +import { StartEndDateComponent } from '../start-end-date/start-end-date.component'; import { MetricsParentRangeSelectorComponent } from './metrics-parent-range-selector.component'; describe('MetricsParentRangeSelectorComponent', () => { @@ -8,9 +16,15 @@ describe('MetricsParentRangeSelectorComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ MetricsParentRangeSelectorComponent ] + declarations: [MetricsParentRangeSelectorComponent, StartEndDateComponent, DateFormatPipe, DateTimeComponent], + imports: [ + CoreModule, + createBasicStoreModule(), + NoopAnimationsModule + ], + providers: [MetricsRangeSelectorService, EntityMonitorFactory] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts index 315941917b..1fc6468642 100644 --- a/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts +++ b/src/frontend/app/shared/components/metrics-parent-range-selector/metrics-parent-range-selector.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, QueryList, ViewChildren, AfterViewInit, AfterContentInit, ContentChildren } from '@angular/core'; +import { Component, OnInit, QueryList, ViewChildren, AfterViewInit, AfterContentInit, ContentChildren, OnDestroy } from '@angular/core'; import { Subscription } from 'rxjs'; import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; import { MetricsRangeSelectorManagerService } from '../../services/metrics-range-selector-manager.service'; @@ -15,7 +15,7 @@ import { IMetrics } from '../../../store/types/base-metric.types'; MetricsRangeSelectorManagerService ] }) -export class MetricsParentRangeSelectorComponent implements AfterContentInit { +export class MetricsParentRangeSelectorComponent implements AfterContentInit, OnDestroy { private actionSub: Subscription; @ContentChildren(MetricsChartComponent) @@ -29,6 +29,9 @@ export class MetricsParentRangeSelectorComponent implements AfterContentInit { ) { } ngAfterContentInit() { + if (!this.metricsCharts || !this.metricsCharts.first) { + return; + } const action = this.metricsCharts.first.metricsConfig.metricsAction; const metricsMonitor = this.entityMonitorFactory.create( action.metricId, @@ -53,4 +56,10 @@ export class MetricsParentRangeSelectorComponent implements AfterContentInit { }); } + ngOnDestroy() { + if (this.actionSub) { + this.actionSub.unsubscribe(); + } + } + } diff --git a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.spec.ts b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.spec.ts index 7b42cde8ca..d6c3f56933 100644 --- a/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.spec.ts +++ b/src/frontend/app/shared/components/metrics-range-selector/metrics-range-selector.component.spec.ts @@ -1,5 +1,13 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { DateFormatPipe } from 'ngx-moment'; +import { CoreModule } from '../../../core/core.module'; +import { createBasicStoreModule } from '../../../test-framework/store-test-helper'; +import { EntityMonitorFactory } from '../../monitors/entity-monitor.factory.service'; +import { MetricsRangeSelectorService } from '../../services/metrics-range-selector.service'; +import { DateTimeComponent } from '../date-time/date-time.component'; +import { StartEndDateComponent } from '../start-end-date/start-end-date.component'; import { MetricsRangeSelectorComponent } from './metrics-range-selector.component'; describe('MetricsRangeSelectorComponent', () => { @@ -8,9 +16,15 @@ describe('MetricsRangeSelectorComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - declarations: [ MetricsRangeSelectorComponent ] + declarations: [MetricsRangeSelectorComponent, StartEndDateComponent, DateFormatPipe, DateTimeComponent], + imports: [ + CoreModule, + createBasicStoreModule(), + NoopAnimationsModule + ], + providers: [MetricsRangeSelectorService, EntityMonitorFactory] }) - .compileComponents(); + .compileComponents(); })); beforeEach(() => { diff --git a/src/frontend/app/shared/services/metrics-range-selector-manager.service.spec.ts b/src/frontend/app/shared/services/metrics-range-selector-manager.service.spec.ts index 62108907ee..c93f02c0ea 100644 --- a/src/frontend/app/shared/services/metrics-range-selector-manager.service.spec.ts +++ b/src/frontend/app/shared/services/metrics-range-selector-manager.service.spec.ts @@ -1,11 +1,15 @@ import { TestBed, inject } from '@angular/core/testing'; import { MetricsRangeSelectorManagerService } from './metrics-range-selector-manager.service'; +import { MetricsRangeSelectorService } from './metrics-range-selector.service'; describe('MetricsRangeSelectorManagerService', () => { beforeEach(() => { TestBed.configureTestingModule({ - providers: [MetricsRangeSelectorManagerService] + providers: [ + MetricsRangeSelectorManagerService, + MetricsRangeSelectorService + ], }); }); diff --git a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts index c2682b3fcf..23053b4096 100644 --- a/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts +++ b/src/frontend/app/shared/services/metrics-range-selector-manager.service.ts @@ -1,13 +1,13 @@ -import { MetricsRangeSelectorService } from './metrics-range-selector.service'; -import { MetricsAction } from './../../store/actions/metrics.actions'; -import { Injectable, EventEmitter } from '@angular/core'; - +import { Injectable } from '@angular/core'; import * as moment from 'moment'; -import { MetricQueryType, ITimeRange } from './metrics-range-selector.types'; -import { EntityMonitor } from '../monitors/entity-monitor'; +import { Subject, Subscription } from 'rxjs'; +import { debounceTime, takeWhile, tap } from 'rxjs/operators'; + import { IMetrics } from '../../store/types/base-metric.types'; -import { Subscription, Subject } from 'rxjs'; -import { debounceTime, tap, takeWhile } from 'rxjs/operators'; +import { EntityMonitor } from '../monitors/entity-monitor'; +import { MetricsAction } from './../../store/actions/metrics.actions'; +import { MetricsRangeSelectorService } from './metrics-range-selector.service'; +import { ITimeRange, MetricQueryType } from './metrics-range-selector.types'; @Injectable() export class MetricsRangeSelectorManagerService { From ac5133e860af634ff1c321c55f869e6b0111f2f7 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 3 Oct 2018 15:12:15 +0100 Subject: [PATCH 094/114] Reliability --- .../application-deploy-e2e.spec.ts | 6 ++-- .../po/application-page-instances.po.ts | 29 +++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/test-e2e/application/application-deploy-e2e.spec.ts b/src/test-e2e/application/application-deploy-e2e.spec.ts index 7edf9c8ee3..e9d3080632 100644 --- a/src/test-e2e/application/application-deploy-e2e.spec.ts +++ b/src/test-e2e/application/application-deploy-e2e.spec.ts @@ -450,8 +450,10 @@ describe('Application Deploy -', function () { confirm.waitUntilShown(); expect(confirm.getMessage()).toBe('Are you sure you want to terminate instance 0?'); confirm.confirm(); - appInstances.cardInstances.waitForRunningInstancesText('0 / 1'); - // The instance can come back very quickly, so we can't check for 0 rows + + browser.wait(() => { + return appInstances.list.table.getTableDataRaw().then(table => table.rows.length === 0); + }); }); afterAll(() => applicationE2eHelper.deleteApplication(null, { appName: testAppName })); diff --git a/src/test-e2e/application/po/application-page-instances.po.ts b/src/test-e2e/application/po/application-page-instances.po.ts index ce74fe1100..65cdeea2a0 100644 --- a/src/test-e2e/application/po/application-page-instances.po.ts +++ b/src/test-e2e/application/po/application-page-instances.po.ts @@ -3,6 +3,7 @@ import { ApplicationBasePage } from './application-page.po'; import { CardAppInstances } from './card-app-instances.po'; import { CardAppStatus } from './card-app-status.po'; import { CardAppUsage } from './card-app-usage.po'; +import { getLocaleTimeFormat } from '@angular/common'; export class ApplicationPageInstancesTab extends ApplicationBasePage { @@ -19,4 +20,32 @@ export class ApplicationPageInstancesTab extends ApplicationBasePage { this.list = new ListComponent(); } + parseUptime(s: string) { + let uptime = 0; + const parts = s.split(' '); + parts.forEach(p => { + if (p.endsWith('s')) { + uptime += this.getTime(p); + } else if (p.endsWith('m')) { + uptime += this.getTime(p) * 60; + } + }); + return uptime; + } + + private getTime(str: string) { + const v = str.substr(0, str.length - 1); + return parseInt(v, 10); + } + + getUptime(index: number) { + // Get the uptime for the instance + return this.list.table.getTableDataRaw().then(table => { + if (index <= (table.rows.length - 1) ) { + const row = table.rows[index]; + return this.parseUptime(row[5]); + } + return -1; + }); + } } From 91299a713524584eb78193b7d03b75b0eef8350e Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 3 Oct 2018 15:24:35 +0100 Subject: [PATCH 095/114] Test fixes --- .../edit-space-step.component.spec.ts | 1 - ...oud-foundry-cell-summary.component.spec.ts | 23 +++++++++++++++++++ .../cloud-foundry-cells.component.spec.ts | 16 ++++++++++--- .../cloud-foundry-feature-flags.component.ts | 15 ++++++++++-- .../app/test-framework/store-test-helper.ts | 5 ++-- 5 files changed, 52 insertions(+), 8 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/edit-space/edit-space-step/edit-space-step.component.spec.ts b/src/frontend/app/features/cloud-foundry/edit-space/edit-space-step/edit-space-step.component.spec.ts index 00d2b07da6..86c3c49537 100644 --- a/src/frontend/app/features/cloud-foundry/edit-space/edit-space-step/edit-space-step.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/edit-space/edit-space-step/edit-space-step.component.spec.ts @@ -5,7 +5,6 @@ import { generateTestCfEndpointServiceProvider, } from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; import { CloudFoundrySpaceServiceMock } from '../../../../test-framework/cloud-foundry-space.service.mock'; -import { ActiveRouteCfOrgSpace } from '../../cf-page.types'; import { CloudFoundrySpaceService } from '../../services/cloud-foundry-space.service'; import { EditSpaceStepComponent } from './edit-space-step.component'; diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts index ff05aeec84..93b01e9715 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts @@ -5,6 +5,11 @@ import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry- import { ActiveRouteCfCell } from '../../../../cf-page.types'; import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; import { CloudFoundryCellSummaryComponent } from './cloud-foundry-cell-summary.component'; +import { MetricQueryType } from '../../../../../../shared/services/metrics-range-selector.types'; +import { MetricsChartHelpers } from '../../../../../../shared/components/metrics-chart/metrics.component.helpers'; +import { FetchCFCellMetricsAction, MetricQueryConfig } from '../../../../../../store/actions/metrics.actions'; +import { MetricsConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.component'; +import { MetricsLineChartConfig, MetricsChartTypes } from '../../../../../../shared/components/metrics-chart/metrics-chart.types'; class MockCloudFoundryCellService { cfGuid = 'cfGuid'; @@ -26,6 +31,24 @@ class MockCloudFoundryCellService { usageMemory$ = observableOf(null); remainingMemory$ = observableOf(null); totalMemory$ = observableOf(null); + + buildMetricConfig = (queryString: string, queryRange: MetricQueryType): MetricsConfig => ({ + getSeriesName: (result: any) => `${result}`, + mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, + metricsAction: new FetchCFCellMetricsAction( + 'guid', + 'cellId', + new MetricQueryConfig(queryString, {}), + queryRange + ), + }) + buildChartConfig = (yAxisLabel: string): MetricsLineChartConfig => ({ + chartType: MetricsChartTypes.LINE, + xAxisLabel: 'Time', + yAxisLabel: yAxisLabel, + showLegend: false + }) + } describe('CloudFoundryCellSummaryComponent', () => { diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts index 20d084ec43..1fadde3442 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cells.component.spec.ts @@ -4,6 +4,7 @@ import { CfCellsListConfigService, } from '../../../../shared/components/list/list-types/cf-cells/cf-cells-list-config.service'; import { BaseTestModules } from '../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { createBasicStoreModule } from '../../../../test-framework/store-test-helper'; import { ActiveRouteCfCell } from '../../cf-page.types'; import { CloudFoundryCellsComponent } from './cloud-foundry-cells.component'; @@ -14,11 +15,20 @@ describe('CloudFoundryCellsComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ declarations: [CloudFoundryCellsComponent], - imports: [...BaseTestModules], + imports: [ + ...BaseTestModules, + createBasicStoreModule() + ], providers: [ CfCellsListConfigService, - ActiveRouteCfCell - ] + { + provide: ActiveRouteCfCell, + useFactory: () => ({ + cfGuid: 'cfGuid', + cellId: 'cellId' + }), + } + ], }) .compileComponents(); })); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component.ts index cdd1c68108..ba2f9ae9d2 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-feature-flags/cloud-foundry-feature-flags.component.ts @@ -1,5 +1,7 @@ -import { Component } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; +import { IFeatureFlag } from '../../../../core/cf-api.types'; +import { ListDataSource } from '../../../../shared/components/list/data-sources-controllers/list-data-source'; import { CfFeatureFlagsListConfigService, } from '../../../../shared/components/list/list-types/cf-feature-flags/cf-feature-flags-list-config.service'; @@ -16,4 +18,13 @@ import { ListConfig } from '../../../../shared/components/list/list.component.ty } ] }) -export class CloudFoundryFeatureFlagsComponent { } +export class CloudFoundryFeatureFlagsComponent implements OnInit { + + constructor(private listConfig: ListConfig) { + const dataSource: ListDataSource = listConfig.getDataSource(); + } + + ngOnInit() { + } + +} diff --git a/src/frontend/app/test-framework/store-test-helper.ts b/src/frontend/app/test-framework/store-test-helper.ts index 995de2771d..3bdd54a7e9 100644 --- a/src/frontend/app/test-framework/store-test-helper.ts +++ b/src/frontend/app/test-framework/store-test-helper.ts @@ -582,7 +582,8 @@ export function getInitialTestStoreState(): AppState { serviceBinding: {}, service: {}, githubCommits: {}, - domain: {} + domain: {}, + metrics: {} }, dashboard: { sidenavOpen: true, @@ -21729,7 +21730,7 @@ export function getInitialTestStoreState(): AppState { token_endpoint: 'https://cf.uaa.127.0.0.1.xip.io:2793', doppler_logging_endpoint: 'wss://doppler.127.0.0.1.xip.io:4443', skip_ssl_validation: true, - sso_allowed: true, + sso_allowed: true, user: { guid: 'bcf78136-6225-4515-bf8e-a32243deea0c', name: 'admin', From 49f129b39a029d0eab46f50f65af8264598a6348 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Wed, 3 Oct 2018 15:35:37 +0100 Subject: [PATCH 096/114] Fix const bug --- protractor.conf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protractor.conf.js b/protractor.conf.js index dccb79eac4..2bbca6e7d2 100644 --- a/protractor.conf.js +++ b/protractor.conf.js @@ -40,7 +40,7 @@ try { } // This is the maximum amount of time ALL before/after/it's must execute in -const timeout = 40000; +let timeout = 40000; const checkSuiteGlob = './src/test-e2e/check/*-e2e.spec.ts'; if (process.env.STRATOS_SCRIPTS_TIMEOUT) { From 9bf45a2fe853e27ac3f6f127b0f1ea85fa39dc9b Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Wed, 3 Oct 2018 17:16:06 +0100 Subject: [PATCH 097/114] Initial Cell Applications list - Still to do... app/space/org links --- .../cloud-foundry/cloud-foundry.module.ts | 4 + .../cloud-foundry/cloud-foundry.routing.ts | 7 ++ .../cloud-foundry-cell-apps.component.html | 1 + .../cloud-foundry-cell-apps.component.scss | 1 + .../cloud-foundry-cell-apps.component.spec.ts | 30 +++++++ .../cloud-foundry-cell-apps.component.ts | 19 ++++ .../cloud-foundry-cell-base.component.ts | 6 +- .../cloud-foundry-cell-charts.component.ts | 6 +- .../table-cell-async.component.html | 5 ++ .../table-cell-async.component.scss | 1 + .../table-cell-async.component.spec.ts | 41 +++++++++ .../table-cell-async.component.ts | 24 +++++ .../table-cell/table-cell.component.ts | 2 + .../cf-cell-apps-list-config.service.ts | 60 +++++++++++++ .../cf-cell-apps/cf-cell-apps-source.ts | 90 +++++++++++++++++++ .../app/store/actions/metrics.actions.ts | 13 +++ 16 files changed, 304 insertions(+), 6 deletions(-) create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.html create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.scss create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.spec.ts create mode 100644 src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.ts create mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.html create mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.scss create mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.spec.ts create mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.ts create mode 100644 src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts create mode 100644 src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-source.ts diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts index e246869b6a..21f42eb2d8 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.module.ts @@ -28,6 +28,9 @@ import { EditSpaceComponent } from './edit-space/edit-space.component'; import { CloudFoundryEndpointService } from './services/cloud-foundry-endpoint.service'; import { CloudFoundryOrganizationService } from './services/cloud-foundry-organization.service'; import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; +import { + CloudFoundryCellAppsComponent, +} from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component'; import { CloudFoundryCellBaseComponent, } from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component'; @@ -104,6 +107,7 @@ import { UsersRolesComponent } from './users/manage-users/manage-users.component CloudFoundryCellsComponent, CloudFoundryCellBaseComponent, CloudFoundryCellSummaryComponent, + CloudFoundryCellAppsComponent, CloudFoundryCellChartsComponent, CloudFoundryBuildPacksComponent, CloudFoundryStacksComponent, diff --git a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts index 0b9f46ca32..a5f434ca9d 100644 --- a/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts +++ b/src/frontend/app/features/cloud-foundry/cloud-foundry.routing.ts @@ -11,6 +11,9 @@ import { CloudFoundryComponent } from './cloud-foundry/cloud-foundry.component'; import { EditOrganizationComponent } from './edit-organization/edit-organization.component'; import { EditSpaceComponent } from './edit-space/edit-space.component'; import { CloudFoundryBuildPacksComponent } from './tabs/cloud-foundry-build-packs/cloud-foundry-build-packs.component'; +import { + CloudFoundryCellAppsComponent, +} from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component'; import { CloudFoundryCellBaseComponent, } from './tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component'; @@ -198,6 +201,10 @@ const cloudFoundry: Routes = [{ { path: 'charts', component: CloudFoundryCellChartsComponent + }, + { + path: 'apps', + component: CloudFoundryCellAppsComponent } ] }, diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.html new file mode 100644 index 0000000000..dd2b6cb351 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.scss b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.scss new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.scss @@ -0,0 +1 @@ + diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.spec.ts new file mode 100644 index 0000000000..14429e9560 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.spec.ts @@ -0,0 +1,30 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { CloudFoundryCellAppsComponent } from './cloud-foundry-cell-apps.component'; + + +describe('CloudFoundryCellAppsComponent', () => { + let component: CloudFoundryCellAppsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + CloudFoundryCellAppsComponent, + ], + imports: [...BaseTestModules], + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CloudFoundryCellAppsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.ts new file mode 100644 index 0000000000..6fb58b49d9 --- /dev/null +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; + +import { + CfCellAppsListConfigService, +} from '../../../../../../shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service'; +import { ListConfig } from '../../../../../../shared/components/list/list.component.types'; + +@Component({ + selector: 'app-cloud-foundry-cell-apps', + templateUrl: './cloud-foundry-cell-apps.component.html', + styleUrls: ['./cloud-foundry-cell-apps.component.scss'], + providers: [ + { + provide: ListConfig, + useClass: CfCellAppsListConfigService + } + ] +}) +export class CloudFoundryCellAppsComponent { } diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts index 56ce1c3485..6426bccc75 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts @@ -5,9 +5,9 @@ import { first, map } from 'rxjs/operators'; import { IHeaderBreadcrumb } from '../../../../../../shared/components/page-header/page-header.types'; import { ISubHeaderTabs } from '../../../../../../shared/components/page-subheader/page-subheader.types'; import { entityFactory, metricSchemaKey } from '../../../../../../store/helpers/entity-factory'; +import { getActiveRouteCfCellProvider } from '../../../../cf.helpers'; import { CloudFoundryEndpointService } from '../../../../services/cloud-foundry-endpoint.service'; import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; -import { getActiveRouteCfCellProvider } from '../../../../cf.helpers'; @Component({ selector: 'app-cloud-foundry-cell-base', @@ -29,6 +29,10 @@ export class CloudFoundryCellBaseComponent { link: 'charts', label: 'Charts' }, + { + link: 'apps', + label: 'Applications' + }, ]; public breadcrumbs$: Observable; diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts index 07540eac8c..0837ccc03a 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts @@ -1,15 +1,11 @@ -import { Component, OnInit } from '@angular/core'; +import { Component } from '@angular/core'; import { MetricsConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.component'; import { MetricsLineChartConfig } from '../../../../../../shared/components/metrics-chart/metrics-chart.types'; -import { - MetricsRangeSelectorManagerService, -} from '../../../../../../shared/services/metrics-range-selector-manager.service'; import { MetricQueryType } from '../../../../../../shared/services/metrics-range-selector.types'; import { IMetricMatrixResult } from '../../../../../../store/types/base-metric.types'; import { IMetricCell } from '../../../../../../store/types/metric.types'; import { CloudFoundryCellService } from '../cloud-foundry-cell.service'; -import { MetricsRangeSelectorService } from '../../../../../../shared/services/metrics-range-selector.service'; @Component({ selector: 'app-cloud-foundry-cell-charts', diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.html b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.html new file mode 100644 index 0000000000..e4dcdcf196 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.html @@ -0,0 +1,5 @@ + + + {{ pathGet(config.pathToValue, value)}} + + \ No newline at end of file diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.scss b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.scss new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.scss @@ -0,0 +1 @@ + diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.spec.ts b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.spec.ts new file mode 100644 index 0000000000..682f6e63cc --- /dev/null +++ b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.spec.ts @@ -0,0 +1,41 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { of as observableOf } from 'rxjs'; + +import { CoreModule } from '../../../../../core/core.module'; +import { APIResource } from '../../../../../store/types/api.types'; +import { createBasicStoreModule } from '../../../../../test-framework/store-test-helper'; +import { IListDataSource } from '../../data-sources-controllers/list-data-source-types'; +import { ListConfig } from '../../list.component.types'; +import { TableCellAsyncComponent } from './table-cell-async.component'; + +describe('TableCellAsyncComponent', () => { + let component: TableCellAsyncComponent; + let fixture: ComponentFixture>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + providers: [ + ListConfig + ], + declarations: [TableCellAsyncComponent], + imports: [ + CoreModule, + createBasicStoreModule(), + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TableCellAsyncComponent); + component = fixture.componentInstance; + component.dataSource = { + } as IListDataSource; + component.rowState = observableOf({}); + fixture.detectChanges(); + }); + + it('should be created', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.ts new file mode 100644 index 0000000000..22c880ca16 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.ts @@ -0,0 +1,24 @@ +import { Component } from '@angular/core'; + +import { pathGet } from '../../../../../core/utils.service'; +import { TableCellCustom } from '../../list.types'; + + +export interface TableCellAsyncConfig { + pathToObs: string; + pathToValue: string; +} + +@Component({ + selector: 'app-table-cell-async', + templateUrl: './table-cell-async.component.html', + styleUrls: ['./table-cell-async.component.scss'] +}) +export class TableCellAsyncComponent extends TableCellCustom { + + constructor() { + super(); + } + + pathGet = pathGet; +} diff --git a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts index 02d60c297b..e953022c4b 100644 --- a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts +++ b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts @@ -105,6 +105,7 @@ import { ICellDefinition } from '../table.types'; import { TableCellSpaceNameComponent } from '../../list-types/cf-spaces-service-instances/table-cell-space-name/table-cell-space-name.component'; import { TableCellCfCellComponent } from '../../list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component'; import { TableCellBooleanIndicatorComponent } from '../table-cell-boolean-indicator/table-cell-boolean-indicator.component'; +import { TableCellAsyncComponent } from '../table-cell-async/table-cell-async.component'; /* tslint:enable:max-line-length */ @@ -119,6 +120,7 @@ export const listTableCells = [ TableCellEventActionComponent, TableCellEventDetailComponent, TableCellActionsComponent, + TableCellAsyncComponent, TableCellAppNameComponent, TableCellEndpointStatusComponent, TableCellEndpointNameComponent, diff --git a/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts new file mode 100644 index 0000000000..19974f33f3 --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts @@ -0,0 +1,60 @@ +import { Injectable } from '@angular/core'; +import { Store } from '@ngrx/store'; + +import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; +import { ActiveRouteCfCell } from '../../../../../features/cloud-foundry/cf-page.types'; +import { ListView } from '../../../../../store/actions/list.actions'; +import { AppState } from '../../../../../store/app-state'; +import { TableCellAsyncComponent, TableCellAsyncConfig } from '../../list-table/table-cell-async/table-cell-async.component'; +import { ListViewTypes } from '../../list.component.types'; +import { BaseCfListConfig } from '../base-cf/base-cf-list-config'; +import { CfCellApp, CfCellAppsDataSource } from './cf-cell-apps-source'; + +@Injectable() +export class CfCellAppsListConfigService extends BaseCfListConfig { + + dataSource: CfCellAppsDataSource; + defaultView = 'table' as ListView; + viewType = ListViewTypes.TABLE_ONLY; + enableTextFilter = false; + text = { + title: null, + noEntries: 'There are no applications' + }; + + constructor(store: Store, activeRouteCfCell: ActiveRouteCfCell, entityServiceFactory: EntityServiceFactory) { + super(); + this.dataSource = new CfCellAppsDataSource(store, activeRouteCfCell.cfGuid, activeRouteCfCell.cellId, this, entityServiceFactory); + } + + getColumns = () => [ + { + columnId: 'app', headerCell: () => 'Application', + cellComponent: TableCellAsyncComponent, + cellFlex: '1', + cellConfig: { + pathToObs: 'appEntityService', + pathToValue: 'entity.name' + } as TableCellAsyncConfig, + }, + { + columnId: 'space', headerCell: () => 'Space', + cellComponent: TableCellAsyncComponent, + cellFlex: '1', + cellConfig: { + pathToObs: 'appEntityService', + pathToValue: 'entity.space.entity.name' + } as TableCellAsyncConfig, + }, + { + columnId: 'org', headerCell: () => 'Organization', + cellComponent: TableCellAsyncComponent, + cellFlex: '1', + cellConfig: { + pathToObs: 'appEntityService', + pathToValue: 'entity.space.entity.organization.entity.name' + } as TableCellAsyncConfig, + }, + ] + getDataSource = () => this.dataSource; +} diff --git a/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-source.ts b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-source.ts new file mode 100644 index 0000000000..cd1cdc281c --- /dev/null +++ b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-source.ts @@ -0,0 +1,90 @@ +import { Store } from '@ngrx/store'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { IApp } from '../../../../../core/cf-api.types'; +import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; +import { GetApplication } from '../../../../../store/actions/application.actions'; +import { FetchCFCellMetricsPaginatedAction, MetricQueryConfig } from '../../../../../store/actions/metrics.actions'; +import { AppState } from '../../../../../store/app-state'; +import { + applicationSchemaKey, + entityFactory, + organizationSchemaKey, + spaceSchemaKey, +} from '../../../../../store/helpers/entity-factory'; +import { createEntityRelationKey } from '../../../../../store/helpers/entity-relations/entity-relations.types'; +import { APIResource } from '../../../../../store/types/api.types'; +import { IMetrics, IMetricVectorResult } from '../../../../../store/types/base-metric.types'; +import { IMetricApplication } from '../../../../../store/types/metric.types'; +import { MetricQueryType } from '../../../../services/metrics-range-selector.types'; +import { ListDataSource } from '../../data-sources-controllers/list-data-source'; +import { IListConfig } from '../../list.component.types'; + +export interface CfCellApp { + appGuid: string; + appEntityService: Observable>; +} + +export class CfCellAppsDataSource + extends ListDataSource>> { + + static appIdPath = 'metric.application_id'; + private appEntityServices: { [appGuid: string]: Observable> }; + + constructor( + store: Store, + cfGuid: string, + cellId: string, + listConfig: IListConfig, + entityServiceFactory: EntityServiceFactory + ) { + const action = new FetchCFCellMetricsPaginatedAction( + cfGuid, + cellId, + new MetricQueryConfig(`firehose_container_metric_cpu_percentage{bosh_job_id="${cellId}"}`, {}), + MetricQueryType.QUERY + ); + + super({ + store, + action, + schema: entityFactory(action.entityKey), + getRowUniqueId: (row: CfCellApp) => row.appGuid, + paginationKey: action.paginationKey, + isLocal: true, + transformEntity: map((response) => { + if (!response || response.length === 0) { + return []; + } + return response[0].data.result.map(res => ({ + appGuid: res.metric.application_id, + appEntityService: this.createAppEntityService(res.metric.application_id, cfGuid, entityServiceFactory) + })); + }), + listConfig + }); + this.appEntityServices = {}; + } + + private createAppEntityService( + appGuid: string, + cfGuid: string, + entityServiceFactory: EntityServiceFactory): Observable> { + if (!this.appEntityServices[appGuid]) { + this.appEntityServices[appGuid] = entityServiceFactory.create>( + applicationSchemaKey, + entityFactory(applicationSchemaKey), + appGuid, + new GetApplication(appGuid, cfGuid, [ + createEntityRelationKey(applicationSchemaKey, spaceSchemaKey), + createEntityRelationKey(spaceSchemaKey, organizationSchemaKey) + ]), + true + ).waitForEntity$.pipe( + map(entityInfo => entityInfo.entity) + ); + } + return this.appEntityServices[appGuid]; + } +} diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 3bd2a8e1d2..7c1ee23a78 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -88,6 +88,19 @@ export class FetchCFMetricsPaginatedAction extends FetchCFMetricsAction implemen }; } +export class FetchCFCellMetricsPaginatedAction extends FetchCFCellMetricsAction implements PaginatedAction { + constructor(cfGuid: string, cellId: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { + super(cfGuid, cellId, query, queryType); + this.paginationKey = this.metricId; + } + actions = []; + paginationKey: string; + initialParams = { + 'order-direction': 'desc', + 'order-direction-field': 'id', + }; +} + export class FetchApplicationMetricsAction extends MetricsAction { constructor(guid: string, cfGuid: string, query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { super(guid, cfGuid, query, `${MetricsAction.getBaseMetricsURL()}/cf/app/${guid}`, queryType); From 32ea55d5e52ed0327b946ce71d96aed16d66d461 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 4 Oct 2018 08:24:27 +0100 Subject: [PATCH 098/114] Resilience --- src/test-e2e/application/application-deploy-e2e.spec.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/test-e2e/application/application-deploy-e2e.spec.ts b/src/test-e2e/application/application-deploy-e2e.spec.ts index e9d3080632..304891c19c 100644 --- a/src/test-e2e/application/application-deploy-e2e.spec.ts +++ b/src/test-e2e/application/application-deploy-e2e.spec.ts @@ -450,10 +450,6 @@ describe('Application Deploy -', function () { confirm.waitUntilShown(); expect(confirm.getMessage()).toBe('Are you sure you want to terminate instance 0?'); confirm.confirm(); - - browser.wait(() => { - return appInstances.list.table.getTableDataRaw().then(table => table.rows.length === 0); - }); }); afterAll(() => applicationE2eHelper.deleteApplication(null, { appName: testAppName })); From cdb5a8fc164288731b4510c3151e73bccfaf4628 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 4 Oct 2018 11:46:54 +0100 Subject: [PATCH 099/114] Add async behaviour to default cell (value + link) --- .../app-table-cell-default.component.ts | 56 +++++++++++++++--- .../table-cell-async.component.html | 5 -- .../table-cell-async.component.scss | 1 - .../table-cell-async.component.spec.ts | 41 ------------- .../table-cell-async.component.ts | 24 -------- .../table-cell/table-cell.component.ts | 2 - .../components/list/list-table/table.types.ts | 12 ++++ .../cf-cell-apps-list-config.service.ts | 57 ++++++++++++------- .../cf-cells/cf-cells-list-config.service.ts | 1 - 9 files changed, 99 insertions(+), 100 deletions(-) delete mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.html delete mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.scss delete mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.spec.ts delete mode 100644 src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.ts diff --git a/src/frontend/app/shared/components/list/list-table/app-table-cell-default/app-table-cell-default.component.ts b/src/frontend/app/shared/components/list/list-table/app-table-cell-default/app-table-cell-default.component.ts index 7141731f39..777d7be8a5 100644 --- a/src/frontend/app/shared/components/list/list-table/app-table-cell-default/app-table-cell-default.component.ts +++ b/src/frontend/app/shared/components/list/list-table/app-table-cell-default/app-table-cell-default.component.ts @@ -1,5 +1,7 @@ -import { Component, Input } from '@angular/core'; +import { Component, Input, OnDestroy } from '@angular/core'; +import { Subscription } from 'rxjs'; +import { pathGet } from '../../../../../core/utils.service'; import { TableCellCustom } from '../../list.types'; import { objectHelper } from './../../../../../core/helper-classes/object.helpers'; import { ICellDefinition } from './../table.types'; @@ -10,7 +12,8 @@ import { ICellDefinition } from './../table.types'; templateUrl: 'app-table-cell-default.component.html', styleUrls: ['app-table-cell-default.component.scss'] }) -export class TableCellDefaultComponent extends TableCellCustom { +export class TableCellDefaultComponent extends TableCellCustom implements OnDestroy { + public cellDefinition: ICellDefinition; private _row: T; @@ -23,6 +26,8 @@ export class TableCellDefaultComponent extends TableCellCustom { } } + private asyncSub: Subscription; + public valueContext = { value: null }; public isLink = false; public isExternalLink = false; @@ -33,18 +38,49 @@ export class TableCellDefaultComponent extends TableCellCustom { public init() { this.setValueGenerator(); this.setValue(this.row); - this.isLink = !!this.cellDefinition.getLink; - if (this.isLink) { - this.linkValue = this.cellDefinition.getLink(this.row); - } + this.setSyncLink(); + } + + private setupLinkDeps() { if (this.cellDefinition.newTab) { this.linkTarget = '_blank'; } this.isExternalLink = this.isLink && this.cellDefinition.externalLink; } + private setSyncLink() { + if (!this.cellDefinition.getLink) { + return; + } + this.isLink = true; + this.linkValue = this.cellDefinition.getLink(this.row); + this.setupLinkDeps(); + } + + private setupAsyncLink(value) { + if (!this.cellDefinition.getAsyncLink) { + return; + } + this.isLink = true; + this.linkValue = this.cellDefinition.getAsyncLink(value); + this.setupLinkDeps(); + } + + private setupAsync(row) { + if (this.asyncSub) { + return; + } + const asyncConfig = this.cellDefinition.asyncValue; + this.asyncSub = row[asyncConfig.pathToObs].subscribe(value => { + this.valueContext.value = pathGet(asyncConfig.pathToValue, value); + this.setupAsyncLink(value); + }); + } + private setValue(row: T) { - if (this.valueGenerator) { + if (this.cellDefinition && this.cellDefinition.asyncValue) { + this.setupAsync(row); + } else if (this.valueGenerator) { this.valueContext.value = this.valueGenerator(row); } } @@ -66,4 +102,10 @@ export class TableCellDefaultComponent extends TableCellCustom { return null; } + ngOnDestroy() { + if (this.asyncSub) { + this.asyncSub.unsubscribe(); + } + } + } diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.html b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.html deleted file mode 100644 index e4dcdcf196..0000000000 --- a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.html +++ /dev/null @@ -1,5 +0,0 @@ - - - {{ pathGet(config.pathToValue, value)}} - - \ No newline at end of file diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.scss b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.scss deleted file mode 100644 index 8b13789179..0000000000 --- a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.scss +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.spec.ts b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.spec.ts deleted file mode 100644 index 682f6e63cc..0000000000 --- a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.spec.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { of as observableOf } from 'rxjs'; - -import { CoreModule } from '../../../../../core/core.module'; -import { APIResource } from '../../../../../store/types/api.types'; -import { createBasicStoreModule } from '../../../../../test-framework/store-test-helper'; -import { IListDataSource } from '../../data-sources-controllers/list-data-source-types'; -import { ListConfig } from '../../list.component.types'; -import { TableCellAsyncComponent } from './table-cell-async.component'; - -describe('TableCellAsyncComponent', () => { - let component: TableCellAsyncComponent; - let fixture: ComponentFixture>; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - providers: [ - ListConfig - ], - declarations: [TableCellAsyncComponent], - imports: [ - CoreModule, - createBasicStoreModule(), - ] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(TableCellAsyncComponent); - component = fixture.componentInstance; - component.dataSource = { - } as IListDataSource; - component.rowState = observableOf({}); - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.ts deleted file mode 100644 index 22c880ca16..0000000000 --- a/src/frontend/app/shared/components/list/list-table/table-cell-async/table-cell-async.component.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Component } from '@angular/core'; - -import { pathGet } from '../../../../../core/utils.service'; -import { TableCellCustom } from '../../list.types'; - - -export interface TableCellAsyncConfig { - pathToObs: string; - pathToValue: string; -} - -@Component({ - selector: 'app-table-cell-async', - templateUrl: './table-cell-async.component.html', - styleUrls: ['./table-cell-async.component.scss'] -}) -export class TableCellAsyncComponent extends TableCellCustom { - - constructor() { - super(); - } - - pathGet = pathGet; -} diff --git a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts index e953022c4b..02d60c297b 100644 --- a/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts +++ b/src/frontend/app/shared/components/list/list-table/table-cell/table-cell.component.ts @@ -105,7 +105,6 @@ import { ICellDefinition } from '../table.types'; import { TableCellSpaceNameComponent } from '../../list-types/cf-spaces-service-instances/table-cell-space-name/table-cell-space-name.component'; import { TableCellCfCellComponent } from '../../list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component'; import { TableCellBooleanIndicatorComponent } from '../table-cell-boolean-indicator/table-cell-boolean-indicator.component'; -import { TableCellAsyncComponent } from '../table-cell-async/table-cell-async.component'; /* tslint:enable:max-line-length */ @@ -120,7 +119,6 @@ export const listTableCells = [ TableCellEventActionComponent, TableCellEventDetailComponent, TableCellActionsComponent, - TableCellAsyncComponent, TableCellAppNameComponent, TableCellEndpointStatusComponent, TableCellEndpointNameComponent, diff --git a/src/frontend/app/shared/components/list/list-table/table.types.ts b/src/frontend/app/shared/components/list/list-table/table.types.ts index cdf7a50d6e..ea14461a98 100644 --- a/src/frontend/app/shared/components/list/list-table/table.types.ts +++ b/src/frontend/app/shared/components/list/list-table/table.types.ts @@ -5,6 +5,15 @@ import { listTableCells, TableCellComponent } from './table-cell/table-cell.comp import { TableRowComponent } from './table-row/table-row.component'; import { TableComponent } from './table.component'; +export interface ICellAsyncValue { + pathToObs: string; + pathToValue: string; +} + +export interface ICellAsyncLink { + pathToValue: string; +} + export interface ICellDefinition { // Dot separated path to get the value from the row valuePath?: string; @@ -14,7 +23,10 @@ export interface ICellDefinition { externalLink?: boolean; // Automatically turns the cell into a link getLink?: (row: T) => string; + // Used in conjunction with asyncValue + getAsyncLink?: (value) => string; newTab?: boolean; + asyncValue?: ICellAsyncValue; } export type CellConfigFunction = (row: T) => any; diff --git a/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts index 19974f33f3..f5b37fa946 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts @@ -5,10 +5,12 @@ import { EntityServiceFactory } from '../../../../../core/entity-service-factory import { ActiveRouteCfCell } from '../../../../../features/cloud-foundry/cf-page.types'; import { ListView } from '../../../../../store/actions/list.actions'; import { AppState } from '../../../../../store/app-state'; -import { TableCellAsyncComponent, TableCellAsyncConfig } from '../../list-table/table-cell-async/table-cell-async.component'; +import { ITableColumn } from '../../list-table/table.types'; import { ListViewTypes } from '../../list.component.types'; import { BaseCfListConfig } from '../base-cf/base-cf-list-config'; import { CfCellApp, CfCellAppsDataSource } from './cf-cell-apps-source'; +import { APIResource } from '../../../../../store/types/api.types'; +import { IApp, ISpace } from '../../../../../core/cf-api.types'; @Injectable() export class CfCellAppsListConfigService extends BaseCfListConfig { @@ -27,33 +29,50 @@ export class CfCellAppsListConfigService extends BaseCfListConfig { this.dataSource = new CfCellAppsDataSource(store, activeRouteCfCell.cfGuid, activeRouteCfCell.cellId, this, entityServiceFactory); } - getColumns = () => [ + getColumns = (): ITableColumn[] => [ { - columnId: 'app', headerCell: () => 'Application', - cellComponent: TableCellAsyncComponent, + columnId: 'app', + headerCell: () => 'Application', cellFlex: '1', - cellConfig: { - pathToObs: 'appEntityService', - pathToValue: 'entity.name' - } as TableCellAsyncConfig, + cellDefinition: { + getAsyncLink: (value: APIResource) => `/applications/${value.entity.cfGuid}/${value.metadata.guid}/summary`, + asyncValue: { + pathToObs: 'appEntityService', + pathToValue: 'entity.name' + } + }, }, { - columnId: 'space', headerCell: () => 'Space', - cellComponent: TableCellAsyncComponent, + columnId: 'space', + headerCell: () => 'Space', cellFlex: '1', - cellConfig: { - pathToObs: 'appEntityService', - pathToValue: 'entity.space.entity.name' - } as TableCellAsyncConfig, + cellDefinition: { + getAsyncLink: (value: APIResource) => { + const spaceEntity = value.entity.space as APIResource; + const cf = `/cloud-foundry/${value.entity.cfGuid}/`; + const org = `organizations/${spaceEntity.entity.organization.metadata.guid}`; + const space = `/spaces/${spaceEntity.metadata.guid}/summary`; + return cf + org + space; + }, + asyncValue: { + pathToObs: 'appEntityService', + pathToValue: 'entity.space.entity.name' + } + }, }, { columnId: 'org', headerCell: () => 'Organization', - cellComponent: TableCellAsyncComponent, cellFlex: '1', - cellConfig: { - pathToObs: 'appEntityService', - pathToValue: 'entity.space.entity.organization.entity.name' - } as TableCellAsyncConfig, + cellDefinition: { + getAsyncLink: (value: APIResource) => { + const space = value.entity.space as APIResource; + return `/cloud-foundry/${value.entity.cfGuid}/organizations/${space.entity.organization.metadata.guid}/summary`; + }, + asyncValue: { + pathToObs: 'appEntityService', + pathToValue: 'entity.space.entity.organization.entity.name' + } + }, }, ] getDataSource = () => this.dataSource; diff --git a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts index e567845321..06d0f8ba3a 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cells/cf-cells-list-config.service.ts @@ -45,7 +45,6 @@ export class CfCellsListConfigService extends BaseCfListConfig) => `/cloud-foundry/${this.activeRouteCfCell.cfGuid}/cells/${row.metric.bosh_job_id}/summary` }, - class: 'table-column-select', cellFlex: '1', sort: { type: 'sort', From ac9a9e3154e0f0f31a914d12753397fd214bf028 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 4 Oct 2018 12:52:54 +0100 Subject: [PATCH 100/114] Disable test --- src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts b/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts index 2a7ac39fd7..b873d89957 100644 --- a/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts +++ b/src/test-e2e/cloud-foundry/organizations-spaces-e2e.spec.ts @@ -162,7 +162,7 @@ describe('CF - Manage Organizations and Spaces', () => { }); }); - it('Should create an org and a space', () => { + xit('Should create an org and a space', () => { const cardView = cloudFoundry.goToOrgView(); // Click the add button to add an organization From 8336ff0b5c82cbef8c9851ac6b44290a7dded630 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 4 Oct 2018 13:03:02 +0100 Subject: [PATCH 101/114] First round of minor tweaks following review --- .../cloud-foundry-cell-base.component.ts | 6 +++--- .../cloud-foundry-cell.service.ts | 2 +- .../table-cell-cf-cell.component.html | 9 ++++++--- .../table-cell-cf-cell.component.ts | 2 +- .../cf-cell-apps-list-config.service.ts | 17 +++++++++++++---- .../cf-cell-apps/cf-cell-apps-source.ts | 2 ++ 6 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts index 6426bccc75..844787aaa1 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.ts @@ -27,11 +27,11 @@ export class CloudFoundryCellBaseComponent { }, { link: 'charts', - label: 'Charts' + label: 'Metrics' }, { link: 'apps', - label: 'Applications' + label: 'App Instances' }, ]; @@ -49,7 +49,7 @@ export class CloudFoundryCellBaseComponent { this.waitForEntityId = cfCellService.healthyMetricId; this.name$ = cfCellService.cellMetric$.pipe( - map(metric => `${metric.bosh_job_id} (${metric.bosh_deployment})`) + map(metric => `${metric.bosh_job_id}`) ); this.breadcrumbs$ = cfEndpointService.endpoint$.pipe( diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index c2479a41b1..96d257dbd0 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -77,7 +77,7 @@ export class CloudFoundryCellService { queryRange: MetricQueryType, mapSeriesItemValue?: (value) => any): MetricsConfig> { return { - getSeriesName: (result: IMetricMatrixResult) => `Cell ${result.metric.bosh_job_id} (${result.metric.bosh_deployment})`, + getSeriesName: (result: IMetricMatrixResult) => `Cell ${result.metric.bosh_job_id}`, mapSeriesItemName: MetricsChartHelpers.getDateSeriesName, mapSeriesItemValue, metricsAction: new FetchCFCellMetricsAction( diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html index 41b0195573..49383287bc 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.html @@ -1,3 +1,6 @@ - - {{cellMetric.bosh_job_id}} ({{cellMetric.bosh_deployment}}) - \ No newline at end of file + + {{cellMetric.bosh_job_id}} + + + + \ No newline at end of file diff --git a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts index b89eeedf12..9f4d7c0396 100644 --- a/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts +++ b/src/frontend/app/shared/components/list/list-types/app-instance/table-cell-cf-cell/table-cell-cf-cell.component.ts @@ -45,7 +45,7 @@ export class TableCellCfCellComponent extends TableCellCustom i filter(metric => !!metric), tap(metric => { this.cellLink = `/cloud-foundry/${cfGuid}/cells/${metric.bosh_job_id}/summary`; - // If we're polled to get metric then make sure to unsub + // If we're polling to get metric then make sure to unsub if (this.fetchMetricsSub) { this.fetchMetricsSub.unsubscribe(); } diff --git a/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts index f5b37fa946..8411ca72e6 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-list-config.service.ts @@ -1,16 +1,16 @@ import { Injectable } from '@angular/core'; import { Store } from '@ngrx/store'; +import { IApp, ISpace } from '../../../../../core/cf-api.types'; import { EntityServiceFactory } from '../../../../../core/entity-service-factory.service'; import { ActiveRouteCfCell } from '../../../../../features/cloud-foundry/cf-page.types'; import { ListView } from '../../../../../store/actions/list.actions'; import { AppState } from '../../../../../store/app-state'; +import { APIResource } from '../../../../../store/types/api.types'; import { ITableColumn } from '../../list-table/table.types'; import { ListViewTypes } from '../../list.component.types'; import { BaseCfListConfig } from '../base-cf/base-cf-list-config'; import { CfCellApp, CfCellAppsDataSource } from './cf-cell-apps-source'; -import { APIResource } from '../../../../../store/types/api.types'; -import { IApp, ISpace } from '../../../../../core/cf-api.types'; @Injectable() export class CfCellAppsListConfigService extends BaseCfListConfig { @@ -24,7 +24,7 @@ export class CfCellAppsListConfigService extends BaseCfListConfig { noEntries: 'There are no applications' }; - constructor(store: Store, activeRouteCfCell: ActiveRouteCfCell, entityServiceFactory: EntityServiceFactory) { + constructor(store: Store, private activeRouteCfCell: ActiveRouteCfCell, entityServiceFactory: EntityServiceFactory) { super(); this.dataSource = new CfCellAppsDataSource(store, activeRouteCfCell.cfGuid, activeRouteCfCell.cellId, this, entityServiceFactory); } @@ -32,7 +32,7 @@ export class CfCellAppsListConfigService extends BaseCfListConfig { getColumns = (): ITableColumn[] => [ { columnId: 'app', - headerCell: () => 'Application', + headerCell: () => 'App Name', cellFlex: '1', cellDefinition: { getAsyncLink: (value: APIResource) => `/applications/${value.entity.cfGuid}/${value.metadata.guid}/summary`, @@ -42,6 +42,15 @@ export class CfCellAppsListConfigService extends BaseCfListConfig { } }, }, + { + columnId: 'appInstance', + headerCell: () => 'App Instance', + cellDefinition: { + valuePath: 'metric.instance_index', + getLink: (row: CfCellApp) => `/applications/${this.activeRouteCfCell.cfGuid}/${row.appGuid}/instances` + }, + cellFlex: '1', + }, { columnId: 'space', headerCell: () => 'Space', diff --git a/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-source.ts b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-source.ts index cd1cdc281c..b8a99df869 100644 --- a/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-source.ts +++ b/src/frontend/app/shared/components/list/list-types/cf-cell-apps/cf-cell-apps-source.ts @@ -22,6 +22,7 @@ import { ListDataSource } from '../../data-sources-controllers/list-data-source' import { IListConfig } from '../../list.component.types'; export interface CfCellApp { + metric: IMetricApplication; appGuid: string; appEntityService: Observable>; } @@ -58,6 +59,7 @@ export class CfCellAppsDataSource return []; } return response[0].data.result.map(res => ({ + metric: res.metric, appGuid: res.metric.application_id, appEntityService: this.createAppEntityService(res.metric.application_id, cfGuid, entityServiceFactory) })); From 3c852c812c83cbfdae76c2621b4afecdf25fdea5 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 4 Oct 2018 14:13:02 +0100 Subject: [PATCH 102/114] Remove MetricQueryType.VALUE --- .../cloud-foundry-cell.service.ts | 3 +- .../services/metrics-range-selector.types.ts | 2 - .../app/store/actions/metrics.actions.ts | 37 +++++++++++++------ .../app/store/effects/metrics.effects.ts | 8 +--- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index 96d257dbd0..8991f63020 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -102,7 +102,8 @@ export class CloudFoundryCellService { this.cfGuid, this.cellId, new MetricQueryConfig(metric + `{bosh_job_id="${this.cellId}"}`, {}), - MetricQueryType.VALUE + MetricQueryType.QUERY, + false ); if (metric === CellMetrics.HEALTHY) { this.healthyMetricId = action.metricId; diff --git a/src/frontend/app/shared/services/metrics-range-selector.types.ts b/src/frontend/app/shared/services/metrics-range-selector.types.ts index 106fd1dbd1..ea83531d78 100644 --- a/src/frontend/app/shared/services/metrics-range-selector.types.ts +++ b/src/frontend/app/shared/services/metrics-range-selector.types.ts @@ -15,6 +15,4 @@ export interface StoreMetricTimeRange { export enum MetricQueryType { QUERY = 'query', RANGE_QUERY = 'query_range', - // Response contains a single value instead of a series of values - VALUE = 'value' } diff --git a/src/frontend/app/store/actions/metrics.actions.ts b/src/frontend/app/store/actions/metrics.actions.ts index 7c1ee23a78..dd363702ca 100644 --- a/src/frontend/app/store/actions/metrics.actions.ts +++ b/src/frontend/app/store/actions/metrics.actions.ts @@ -44,8 +44,9 @@ export class MetricsAction implements IRequestAction { public endpointGuid: string, public query: MetricQueryConfig, public url: string, - public queryType: MetricQueryType = MetricQueryType.QUERY) { - this.metricId = MetricsAction.buildMetricKey(guid, query, queryType); + public queryType: MetricQueryType = MetricQueryType.QUERY, + isSeries = true) { + this.metricId = MetricsAction.buildMetricKey(guid, query, isSeries); } entityKey = metricSchemaKey; type = METRICS_START; @@ -56,22 +57,29 @@ export class MetricsAction implements IRequestAction { } // Builds the key that is used to store the metric in the app state. - static buildMetricKey(guid: string, query: MetricQueryConfig, queryType: MetricQueryType) { - - const valueOrSeries = queryType === MetricQueryType.VALUE ? 'value' : 'series'; - return `${guid}:${query.metric}:${valueOrSeries}`; + static buildMetricKey(guid: string, query: MetricQueryConfig, isSeries: boolean) { + return `${guid}:${query.metric}:${isSeries ? 'series' : 'value'}`; } } export class FetchCFMetricsAction extends MetricsAction { - constructor(cfGuid: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { - super(cfGuid, cfGuid, query, `${MetricsAction.getBaseMetricsURL()}/cf`, queryType); + constructor( + cfGuid: string, + public query: MetricQueryConfig, + queryType: MetricQueryType = MetricQueryType.QUERY, + isSeries = true) { + super(cfGuid, cfGuid, query, `${MetricsAction.getBaseMetricsURL()}/cf`, queryType, isSeries); } } export class FetchCFCellMetricsAction extends MetricsAction { - constructor(cfGuid: string, cellId: string, public query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { - super(cfGuid + '-' + cellId, cfGuid, query, `${MetricsAction.getBaseMetricsURL()}/cf`, queryType); + constructor( + cfGuid: string, + cellId: string, + public query: MetricQueryConfig, + queryType: MetricQueryType = MetricQueryType.QUERY, + isSeries = true) { + super(cfGuid + '-' + cellId, cfGuid, query, `${MetricsAction.getBaseMetricsURL()}/cf`, queryType, isSeries); } } @@ -102,8 +110,13 @@ export class FetchCFCellMetricsPaginatedAction extends FetchCFCellMetricsAction } export class FetchApplicationMetricsAction extends MetricsAction { - constructor(guid: string, cfGuid: string, query: MetricQueryConfig, queryType: MetricQueryType = MetricQueryType.QUERY) { - super(guid, cfGuid, query, `${MetricsAction.getBaseMetricsURL()}/cf/app/${guid}`, queryType); + constructor( + guid: string, + cfGuid: string, + query: MetricQueryConfig, + queryType: MetricQueryType = MetricQueryType.QUERY, + isSeries = true) { + super(guid, cfGuid, query, `${MetricsAction.getBaseMetricsURL()}/cf/app/${guid}`, queryType, isSeries); } } diff --git a/src/frontend/app/store/effects/metrics.effects.ts b/src/frontend/app/store/effects/metrics.effects.ts index 8384094e7e..cb398ae4e1 100644 --- a/src/frontend/app/store/effects/metrics.effects.ts +++ b/src/frontend/app/store/effects/metrics.effects.ts @@ -91,14 +91,8 @@ export class MetricsEffect { })); private buildFullUrl(action: MetricsAction) { - return `${action.url}/${this.queryType(action.queryType)}?query=${getFullMetricQueryQuery(action.query)}`; + return `${action.url}/${action.queryType}?query=${getFullMetricQueryQuery(action.query)}`; } - private queryType(type: MetricQueryType): string { - if (type === MetricQueryType.VALUE) { - return MetricQueryType.QUERY; - } - return type; - } } From a1d0bcb0b38bd1ecfa97e7793d55b2db8df7efc9 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 4 Oct 2018 14:17:15 +0100 Subject: [PATCH 103/114] Fix odd not app instance tab --> app instance tab exceptions --- .../cards/card-app-usage/card-app-usage.component.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.html b/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.html index 1aaa23b5fa..0a5c1d166d 100644 --- a/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.html +++ b/src/frontend/app/shared/components/cards/card-app-usage/card-app-usage.component.html @@ -1,6 +1,6 @@ - + @@ -27,4 +27,4 @@ Application is not running - + \ No newline at end of file From a9af3ac4deb1070f60b77a187f4d5b1afa47d259 Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 4 Oct 2018 15:05:19 +0100 Subject: [PATCH 104/114] Run ffmpeg install in background --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d4889e213a..3f370c6589 100644 --- a/.travis.yml +++ b/.travis.yml @@ -65,7 +65,7 @@ jobs: # Install ffmpeg so we can capture a video of the display as the tests run - sudo add-apt-repository -y ppa:mc3man/trusty-media - sudo apt-get -qq update - - sudo apt-get install -y ffmpeg + - sudo apt-get install -y ffmpeg > ./ffmpeg.log & script: - "./deploy/ci/travis/fetch-depcache.sh" - "./deploy/ci/travis/run-e2e-tests.sh video" From 31c71627fc53431f61cb53226ec56ddae1f95ccd Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 4 Oct 2018 15:19:19 +0100 Subject: [PATCH 105/114] Re-enable text in boolean indicators --- .../components/boolean-indicator/boolean-indicator.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.ts b/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.ts index e175209f04..f4d2c3e288 100644 --- a/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.ts +++ b/src/frontend/app/shared/components/boolean-indicator/boolean-indicator.component.ts @@ -19,7 +19,7 @@ export class BooleanIndicatorComponent implements OnInit { @Input() isTrue: boolean; @Input() type: BooleanIndicatorType; - @Input() showText: boolean; + @Input() showText = true; // Should we use a subtle display - this won't show the No option as danger (typically red) @Input() subtle = true; From 839c04513091556f13c8777796a67117ee7260a7 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 4 Oct 2018 15:45:40 +0100 Subject: [PATCH 106/114] Fix unit test --- .../cloud-foundry-cell-apps.component.spec.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.spec.ts index 14429e9560..f8b03c7253 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-apps/cloud-foundry-cell-apps.component.spec.ts @@ -1,6 +1,7 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { BaseTestModules } from '../../../../../../test-framework/cloud-foundry-endpoint-service.helper'; +import { ActiveRouteCfCell } from '../../../../cf-page.types'; import { CloudFoundryCellAppsComponent } from './cloud-foundry-cell-apps.component'; @@ -13,6 +14,7 @@ describe('CloudFoundryCellAppsComponent', () => { declarations: [ CloudFoundryCellAppsComponent, ], + providers: [ActiveRouteCfCell], imports: [...BaseTestModules], }) .compileComponents(); From 434f66a69929d9f4e338de88a9909a76fbc74d50 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 4 Oct 2018 16:22:46 +0100 Subject: [PATCH 107/114] Improve healthy chart, show rounded numbers for containers, start `remaining` charts at 0 --- .../cloud-foundry-cell-charts.component.ts | 5 ++++- .../cloud-foundry-cell-summary.component.ts | 4 +++- .../cloud-foundry-cell/cloud-foundry-cell.service.ts | 1 + .../metrics-chart/metrics-chart.component.html | 2 +- .../components/metrics-chart/metrics-chart.component.ts | 9 ++------- .../components/metrics-chart/metrics-chart.types.ts | 8 +++++++- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts index 0837ccc03a..4cd6eeff41 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-charts/cloud-foundry-cell-charts.component.ts @@ -23,7 +23,10 @@ export class CloudFoundryCellChartsComponent { this.metricConfigs = [ [ this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_containers', MetricQueryType.RANGE_QUERY), - this.cfCellService.buildChartConfig('Containers Remaining') + { + ...this.cfCellService.buildChartConfig('Containers Remaining'), + yAxisTickFormatting: (label: string) => Math.round(Number(label)).toString() + } ], [ this.cfCellService.buildMetricConfig('firehose_value_metric_rep_capacity_remaining_memory', MetricQueryType.QUERY), diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts index a0abf625ab..e363b45a7c 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.ts @@ -31,7 +31,9 @@ export class CloudFoundryCellSummaryComponent { MetricQueryType.QUERY, (value) => value === '0' ? '1' : '0' ); - this.chartConfig = this.cfCellService.buildChartConfig('Cell Healthy (1)'); + this.chartConfig = this.cfCellService.buildChartConfig('Cell Health'); + this.chartConfig.yAxisTickFormatting = (label) => label === '1' ? 'Healthy' : 'Not Healthy'; + this.chartConfig.yAxisTicks = ['0', '1']; this.status$ = cfCellService.healthy$.pipe( map(health => { diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts index 8991f63020..fed348b3f9 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell.service.ts @@ -94,6 +94,7 @@ export class CloudFoundryCellService { lineChartConfig.xAxisLabel = 'Time'; lineChartConfig.yAxisLabel = yAxisLabel; lineChartConfig.showLegend = false; + lineChartConfig.autoScale = false; return lineChartConfig; } diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html index 8489f45a21..2bc1d6bce6 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.html @@ -18,7 +18,7 @@

{{title}}

+ [xAxis]="true" [yAxis]="true" [autoScale]="chartConfig.autoScale" [yAxisTicks]="chartConfig.yAxisTicks" [yAxisTickFormatting]="chartConfig.yAxisTickFormatting"> {{ chartConfig.chartType }} chart type not found
diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts index a6db9b07fd..a45ea1aa11 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.component.ts @@ -11,7 +11,7 @@ import { MetricQueryType } from '../../services/metrics-range-selector.types'; import { MetricsRangeSelectorComponent } from '../metrics-range-selector/metrics-range-selector.component'; import { ChartSeries, IMetrics, MetricResultTypes } from './../../../store/types/base-metric.types'; import { EntityMonitorFactory } from './../../monitors/entity-monitor.factory.service'; -import { IMetricsChartConfig, MetricsChartTypes } from './metrics-chart.types'; +import { MetricsChartTypes, MetricsLineChartConfig } from './metrics-chart.types'; import { MetricsChartManager } from './metrics.component.manager'; export interface MetricsConfig { @@ -21,11 +21,6 @@ export interface MetricsConfig { mapSeriesItemValue?: (value) => any; sort?: (a: ChartSeries, b: ChartSeries) => number; } -export interface MetricsChartConfig { - chartType: MetricsChartTypes; - xAxisLabel?: string; - yAxisLabel?: string; -} @Component({ selector: 'app-metrics-chart', @@ -36,7 +31,7 @@ export class MetricsChartComponent implements OnInit, OnDestroy, AfterContentIni @Input() public metricsConfig: MetricsConfig; @Input() - public chartConfig: IMetricsChartConfig; + public chartConfig: MetricsLineChartConfig; @Input() public title: string; diff --git a/src/frontend/app/shared/components/metrics-chart/metrics-chart.types.ts b/src/frontend/app/shared/components/metrics-chart/metrics-chart.types.ts index e41b534eae..7c673cfa10 100644 --- a/src/frontend/app/shared/components/metrics-chart/metrics-chart.types.ts +++ b/src/frontend/app/shared/components/metrics-chart/metrics-chart.types.ts @@ -11,11 +11,14 @@ export enum MetricsChartTypes { LINE = 'line' } -export interface IMetricsChartConfig { +interface IMetricsChartConfig { chartType: MetricsChartTypes; showLegend?: boolean; xAxisLabel?: string; yAxisLabel?: string; + autoScale?: boolean; + yAxisTicks?: any[]; + yAxisTickFormatting?: (label: string) => string; } export class MetricsLineChartConfig implements IMetricsChartConfig { @@ -23,4 +26,7 @@ export class MetricsLineChartConfig implements IMetricsChartConfig { showLegend = true; xAxisLabel?: string; yAxisLabel?: string; + autoScale = true; // This should be on by default + yAxisTicks?: any[]; + yAxisTickFormatting?: (label: string) => string; } From 2898d9ea9fba1ed86de39a9e56c0a3e8282996ca Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 4 Oct 2018 17:03:56 +0100 Subject: [PATCH 108/114] Improve background install of ffmpeg --- .travis.yml | 3 +-- deploy/ci/travis/run-e2e-tests.sh | 13 +++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3f370c6589..142551397d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,10 +62,9 @@ jobs: - name: E2E Tests before_script: - chmod +x ./deploy/ci/travis/run-e2e-tests.sh - # Install ffmpeg so we can capture a video of the display as the tests run + # We will install ffmpeg so we can capture a video of the display as the tests run - sudo add-apt-repository -y ppa:mc3man/trusty-media - sudo apt-get -qq update - - sudo apt-get install -y ffmpeg > ./ffmpeg.log & script: - "./deploy/ci/travis/fetch-depcache.sh" - "./deploy/ci/travis/run-e2e-tests.sh video" diff --git a/deploy/ci/travis/run-e2e-tests.sh b/deploy/ci/travis/run-e2e-tests.sh index a162b0744b..b6d6268393 100755 --- a/deploy/ci/travis/run-e2e-tests.sh +++ b/deploy/ci/travis/run-e2e-tests.sh @@ -24,6 +24,15 @@ export CERTS_PATH=./dev-certs # Single arg if set to 'video' will use ffmpeg to capture the browser window as a video as the tests run CAPTURE_VIDEO=$1 +export E2E_REPORT_FOLDER="./e2e-reports/${TIMESTAMP}-Travis-Job-${TRAVIS_JOB_NUMBER}" +mkdir -p "${E2E_REPORT_FOLDER}" + +if [ "$CAPTURE_VIDEO" == "video" ]; then + echo "Starting background install of ffmpeg" + sudo apt-get install -y ffmpeg > ${E2E_REPORT_FOLDER}/ffmpeg-install.log & + FFMPEG_INSTALL_PID=$! +fi + echo "Using local deployment for e2e tests" # Quick deploy locally # Start a local UAA - this will take a few seconds to come up in the background @@ -49,11 +58,11 @@ E2E_TARGET="e2e -- --dev-server-target= --base-url=https://127.0.0.1:5443" # Test report folder name override TIMESTAMP=`date '+%Y%m%d-%H.%M.%S'` -export E2E_REPORT_FOLDER="./e2e-reports/${TIMESTAMP}-Travis-Job-${TRAVIS_JOB_NUMBER}" -mkdir -p "${E2E_REPORT_FOLDER}" # Capture video if configured if [ "$CAPTURE_VIDEO" == "video" ]; then + echo "Waiting for ffmpeg install to complete..." + wait ${FFMPEG_INSTALL_PID} echo "Starting video capture" ffmpeg -video_size 1366x768 -framerate 25 -f x11grab -draw_mouse 0 -i :99.0 ${E2E_REPORT_FOLDER}/ScreenCapture.mp4 >/dev/null 2>&1 & FFMPEG=$! From 4484ae100fe7fc92486ec7a90a0ba77807edbf8c Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Thu, 4 Oct 2018 17:35:13 +0100 Subject: [PATCH 109/114] Fix new failing unit test --- .../cloud-foundry-cell-summary.component.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts index 93b01e9715..6d03154966 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-summary/cloud-foundry-cell-summary.component.spec.ts @@ -46,7 +46,8 @@ class MockCloudFoundryCellService { chartType: MetricsChartTypes.LINE, xAxisLabel: 'Time', yAxisLabel: yAxisLabel, - showLegend: false + showLegend: false, + autoScale: true }) } From 3332328b573c8102aa26b1d237a8af95e6ad48ed Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Thu, 4 Oct 2018 19:17:30 +0100 Subject: [PATCH 110/114] Fix timestamp --- deploy/ci/travis/run-e2e-tests.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/deploy/ci/travis/run-e2e-tests.sh b/deploy/ci/travis/run-e2e-tests.sh index b6d6268393..2609de938e 100755 --- a/deploy/ci/travis/run-e2e-tests.sh +++ b/deploy/ci/travis/run-e2e-tests.sh @@ -24,6 +24,9 @@ export CERTS_PATH=./dev-certs # Single arg if set to 'video' will use ffmpeg to capture the browser window as a video as the tests run CAPTURE_VIDEO=$1 +# Test report folder name override +TIMESTAMP=`date '+%Y%m%d-%H.%M.%S'` + export E2E_REPORT_FOLDER="./e2e-reports/${TIMESTAMP}-Travis-Job-${TRAVIS_JOB_NUMBER}" mkdir -p "${E2E_REPORT_FOLDER}" @@ -56,9 +59,6 @@ popd E2E_TARGET="e2e -- --dev-server-target= --base-url=https://127.0.0.1:5443" -# Test report folder name override -TIMESTAMP=`date '+%Y%m%d-%H.%M.%S'` - # Capture video if configured if [ "$CAPTURE_VIDEO" == "video" ]; then echo "Waiting for ffmpeg install to complete..." From 9608d45d0639e0b152437033bf04136069643f29 Mon Sep 17 00:00:00 2001 From: Irfan Habib Date: Thu, 4 Oct 2018 20:58:11 +0100 Subject: [PATCH 111/114] Add helm chart labels --- .../console/templates/deployment.yaml | 19 +++++++++++++++---- .../console/templates/pre-install.yaml | 18 +++++++++++++++--- .../kubernetes/console/templates/secrets.yaml | 18 +++++++++++++++--- .../kubernetes/console/templates/service.yaml | 16 ++++++++++++---- 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/deploy/kubernetes/console/templates/deployment.yaml b/deploy/kubernetes/console/templates/deployment.yaml index 763db6f481..5ab3452939 100644 --- a/deploy/kubernetes/console/templates/deployment.yaml +++ b/deploy/kubernetes/console/templates/deployment.yaml @@ -9,8 +9,11 @@ spec: template: metadata: labels: - app: "{{ .Release.Name }}" - component: "console" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" spec: containers: - image: {{.Values.kube.registry.hostname}}/{{.Values.kube.organization}}/{{.Values.images.console}}:{{.Values.consoleVersion}} @@ -243,12 +246,20 @@ kind: Deployment metadata: name: console-mariadb labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-mariadb" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" spec: template: metadata: labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-mariadb" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" spec: containers: - name: mariadb diff --git a/deploy/kubernetes/console/templates/pre-install.yaml b/deploy/kubernetes/console/templates/pre-install.yaml index d5087c9daa..b17c8ee0a6 100644 --- a/deploy/kubernetes/console/templates/pre-install.yaml +++ b/deploy/kubernetes/console/templates/pre-install.yaml @@ -4,7 +4,11 @@ kind: PersistentVolumeClaim metadata: name: "{{ .Release.Name }}-encryption-key-volume" labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-encryption-volume" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" annotations: {{- if .Values.storageClass }} volume.beta.kubernetes.io/storage-class: {{ .Values.storageClass | quote }} @@ -26,7 +30,11 @@ apiVersion: v1 metadata: name: console-mariadb labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-mariadb-volume" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" annotations: {{- if .Values.mariadb.persistence.storageClass }} volume.beta.kubernetes.io/storage-class: {{ .Values.mariadb.persistence.storageClass | quote }} @@ -50,7 +58,11 @@ kind: PersistentVolumeClaim metadata: name: "{{ .Release.Name }}-upgrade-volume" labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-upgrade-volume" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" annotations: {{- if .Values.storageClass }} volume.beta.kubernetes.io/storage-class: {{ .Values.storageClass | quote }} diff --git a/deploy/kubernetes/console/templates/secrets.yaml b/deploy/kubernetes/console/templates/secrets.yaml index da0288fc60..77de88455c 100644 --- a/deploy/kubernetes/console/templates/secrets.yaml +++ b/deploy/kubernetes/console/templates/secrets.yaml @@ -5,7 +5,11 @@ type: Opaque metadata: name: "{{ .Release.Name }}-secret" labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-secret" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" data: stolon: {{ .Values.dbPassword | b64enc }} db-password: {{ .Values.mariadb.mariadbPassword | b64enc }} @@ -16,7 +20,11 @@ kind: Secret metadata: name: "{{ .Release.Name }}-mariadb-secret" labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-mariadb-secret" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" type: Opaque data: {{- if .Values.mariadb.mariadbRootPassword }} @@ -38,7 +46,11 @@ type: kubernetes.io/dockercfg metadata: name: {{ .Values.dockerRegistrySecret }} labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-dockerreg-secret" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" data: .dockercfg: {{ template "imagePullSecret" . }} {{- end}} diff --git a/deploy/kubernetes/console/templates/service.yaml b/deploy/kubernetes/console/templates/service.yaml index c9817bb58d..dcefd45b63 100644 --- a/deploy/kubernetes/console/templates/service.yaml +++ b/deploy/kubernetes/console/templates/service.yaml @@ -2,7 +2,11 @@ apiVersion: v1 kind: Service metadata: labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-ext-service" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" name: "{{ .Release.Name }}-ui-ext" spec: ports: @@ -25,7 +29,7 @@ spec: protocol: TCP targetPort: 443 selector: - app: "{{ .Release.Name }}" + app.kubernetes.io/instance: "{{ .Release.Name }}" component: console {{- if or .Values.useLb .Values.services.loadbalanced }} type: LoadBalancer @@ -41,7 +45,11 @@ kind: Service metadata: name: "{{ .Release.Name }}-mariadb" labels: - app: "{{ .Release.Name }}" + app.kubernetes.io/name: "stratos" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + app.kubernetes.io/component: "console-mariadb-service" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}" spec: type: ClusterIP ports: @@ -49,4 +57,4 @@ spec: port: 3306 targetPort: mysql selector: - app: "{{ .Release.Name }}" + app.kubernetes.io/instance: "{{ .Release.Name }}" From 70273bce827db2dc4e0abe589158f8d0b25f82c0 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 5 Oct 2018 12:02:18 +0100 Subject: [PATCH 112/114] Fixes following demo walkthrough - Show snackbar error when scaling app instances (for instance hit memory quota) - Ensure radio button in bind to existing route table is disabled for current app (refresh on stepper) - Tweaked width of app instance table cell column - Show 'Cell:' in cell summary breadcrumb - Unused params --- .../deploy-application-step3.component.ts | 8 +++--- .../routes/map-routes/map-routes.component.ts | 2 +- .../cloud-foundry-cell-base.component.html | 4 +-- .../card-app-instances.component.ts | 25 +++++++++++++------ .../table-cell-radio.component.ts | 16 +++++++++++- .../cf-app-instances-config.service.ts | 2 +- 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts b/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts index 8bcae63d2e..ba17165ddd 100644 --- a/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts +++ b/src/frontend/app/features/applications/deploy-application/deploy-application-step3/deploy-application-step3.component.ts @@ -49,19 +49,17 @@ export class DeployApplicationStep3Component implements OnDestroy { private errorSub: Subscription; private validSub: Subscription; - private fsData: FileScannerInfo; - constructor( private store: Store, - private snackBar: MatSnackBar, + snackBar: MatSnackBar, public cfOrgSpaceService: CfOrgSpaceDataService, - private http: HttpClient, + http: HttpClient, ) { this.deployer = new DeployApplicationDeployer(store, cfOrgSpaceService, http); // Observables this.errorSub = this.deployer.status$.pipe( filter((status) => status.error) - ).subscribe(status => this.snackBar.open(status.errorMsg, 'Dismiss')); + ).subscribe(status => snackBar.open(status.errorMsg, 'Dismiss')); const appGuid$ = this.deployer.applicationGuid$.pipe( filter((appGuid) => appGuid !== null), diff --git a/src/frontend/app/features/applications/routes/map-routes/map-routes.component.ts b/src/frontend/app/features/applications/routes/map-routes/map-routes.component.ts index 3956582010..271de36fcb 100644 --- a/src/frontend/app/features/applications/routes/map-routes/map-routes.component.ts +++ b/src/frontend/app/features/applications/routes/map-routes/map-routes.component.ts @@ -36,7 +36,7 @@ export class MapRoutesComponent implements OnInit, OnDestroy { constructor( private store: Store, private appService: ApplicationService, - private listConfig: ListConfig, + listConfig: ListConfig, private paginationMonitorFactory: PaginationMonitorFactory ) { this.routesDataSource = listConfig.getDataSource() as CfAppRoutesDataSource; diff --git a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.html b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.html index 1ffad9bb46..91f3bfff4a 100644 --- a/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.html +++ b/src/frontend/app/features/cloud-foundry/tabs/cloud-foundry-cells/cloud-foundry-cell/cloud-foundry-cell-base/cloud-foundry-cell-base.component.html @@ -1,6 +1,6 @@ -

{{ name$ | async }}

+

Cell: {{ name }}

- + \ No newline at end of file diff --git a/src/frontend/app/shared/components/cards/card-app-instances/card-app-instances.component.ts b/src/frontend/app/shared/components/cards/card-app-instances/card-app-instances.component.ts index f0ed16c943..871ba3f424 100644 --- a/src/frontend/app/shared/components/cards/card-app-instances/card-app-instances.component.ts +++ b/src/frontend/app/shared/components/cards/card-app-instances/card-app-instances.component.ts @@ -1,14 +1,13 @@ -import { CardStatus } from './../../application-state/application-state.service'; import { Component, ElementRef, Input, OnDestroy, OnInit, Renderer, ViewChild } from '@angular/core'; -import { Store } from '@ngrx/store'; import { Observable, Subscription } from 'rxjs'; +import { map, first, tap } from 'rxjs/operators'; import { ApplicationService } from '../../../../features/applications/application.service'; import { AppMetadataTypes } from '../../../../store/actions/app-metadata.actions'; -import { AppState } from '../../../../store/app-state'; -import { ConfirmationDialogService } from '../../confirmation-dialog.service'; -import { map } from 'rxjs/operators'; import { ConfirmationDialogConfig } from '../../confirmation-dialog.config'; +import { ConfirmationDialogService } from '../../confirmation-dialog.service'; +import { CardStatus } from './../../application-state/application-state.service'; +import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from '@angular/material'; const appInstanceScaleToZeroConfirmation = new ConfirmationDialogConfig('Set Instance count to 0', 'Are you sure you want to set the instance count to 0?', 'Confirm', true); @@ -30,10 +29,10 @@ export class CardAppInstancesComponent implements OnInit, OnDestroy { status$: Observable; constructor( - private store: Store, public appService: ApplicationService, private renderer: Renderer, - private confirmDialog: ConfirmationDialogService) { + private confirmDialog: ConfirmationDialogService, + private snackBar: MatSnackBar) { this.status$ = this.appService.applicationState$.pipe( map(state => state.indicator) ); @@ -52,6 +51,7 @@ export class CardAppInstancesComponent implements OnInit, OnDestroy { public runningInstances$: Observable; private app: any; + private snackBarRef: MatSnackBarRef; ngOnInit() { this.sub = this.appService.application$.subscribe(app => { @@ -64,6 +64,9 @@ export class CardAppInstancesComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.sub.unsubscribe(); + if (this.snackBarRef) { + this.snackBarRef.dismiss(); + } } scaleUp(current: number) { @@ -95,7 +98,13 @@ export class CardAppInstancesComponent implements OnInit, OnDestroy { if (value === 0) { this.confirmDialog.open(appInstanceScaleToZeroConfirmation, doUpdate); } else { - doUpdate(); + doUpdate().pipe( + first(), + ).subscribe(actionState => { + if (actionState.error) { + this.snackBarRef = this.snackBar.open(`Failed to update instance count: ${actionState.message}`, 'Dismiss'); + } + }); } } } diff --git a/src/frontend/app/shared/components/list/list-table/table-cell-radio/table-cell-radio.component.ts b/src/frontend/app/shared/components/list/list-table/table-cell-radio/table-cell-radio.component.ts index 4be09b65db..664c74eb2c 100644 --- a/src/frontend/app/shared/components/list/list-table/table-cell-radio/table-cell-radio.component.ts +++ b/src/frontend/app/shared/components/list/list-table/table-cell-radio/table-cell-radio.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnInit, Input } from '@angular/core'; import { TableCellCustom } from '../../list.types'; @@ -10,7 +10,21 @@ import { TableCellCustom } from '../../list.types'; export class TableCellRadioComponent extends TableCellCustom implements OnInit { disable: boolean; + private _row: T; + @Input('row') + get row() { return this._row; } + set row(row: T) { + this._row = row; + if (row) { + this.updateDisabled(); + } + } + ngOnInit() { + this.updateDisabled(); + } + + updateDisabled() { this.disable = this.config ? this.config.isDisabled(this.row) : false; } } 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 3d9c3e4df5..dc82e805a8 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 @@ -116,7 +116,7 @@ export class CfAppInstancesConfigService implements IListConfig metricResults$: null }, cellComponent: TableCellCfCellComponent, - cellFlex: '3' + cellFlex: '2' }; viewType = ListViewTypes.TABLE_ONLY; From 827b6cbed908227945479f123b93b8f7fa74bf59 Mon Sep 17 00:00:00 2001 From: Richard Cox Date: Fri, 5 Oct 2018 13:08:17 +0100 Subject: [PATCH 113/114] Fix delete of request data on disconnect/unregister - Previously disconnecting any endpoint would wipe out all app, org and space data - This was due to to an error accessing the entity.cfGuid property - This is now fixed, along with some general tidy up --- .../api-request-reducers.generator.ts | 8 +++--- .../application-add-remove-reducer.ts | 10 +++---- ...endpoint-disconnect-application.reducer.ts | 27 +++++++++---------- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/src/frontend/app/store/reducers/api-request-reducers.generator.ts b/src/frontend/app/store/reducers/api-request-reducers.generator.ts index f709972793..3330259796 100644 --- a/src/frontend/app/store/reducers/api-request-reducers.generator.ts +++ b/src/frontend/app/store/reducers/api-request-reducers.generator.ts @@ -132,16 +132,16 @@ export function requestDataReducer(state, action) { [appSummarySchemaKey]: [updateAppSummaryRoutesReducer], [applicationSchemaKey]: [ updateApplicationRoutesReducer(), - endpointDisconnectApplicationReducer('application') + endpointDisconnectApplicationReducer() ], [spaceSchemaKey]: [ - endpointDisconnectApplicationReducer('space'), - applicationAddRemoveReducer('space'), + endpointDisconnectApplicationReducer(), + applicationAddRemoveReducer(), userSpaceOrgReducer(true) ], [organizationSchemaKey]: [ updateOrganizationSpaceReducer(), - endpointDisconnectApplicationReducer('organization'), + endpointDisconnectApplicationReducer(), userSpaceOrgReducer(false) ] }; diff --git a/src/frontend/app/store/reducers/application-add-remove-reducer.ts b/src/frontend/app/store/reducers/application-add-remove-reducer.ts index c0f0e49be6..7c2c1fced9 100644 --- a/src/frontend/app/store/reducers/application-add-remove-reducer.ts +++ b/src/frontend/app/store/reducers/application-add-remove-reducer.ts @@ -5,13 +5,13 @@ import { IApp, ISpace } from '../../core/cf-api.types'; import { deepMergeState } from '../helpers/reducer.helper'; -export function applicationAddRemoveReducer(name) { +export function applicationAddRemoveReducer() { return function (state: APIResource, action: APISuccessOrFailedAction) { switch (action.type) { case CREATE_SUCCESS: - return addApplicationToSpace(state, action); + return addApplicationToSpace(state, action); case DELETE_SUCCESS: - return deleteApplicationFromSpace(state, action); + return deleteApplicationFromSpace(state, action); } return state; }; @@ -28,7 +28,7 @@ function addApplicationToSpace(state: APIResource, action: APISuccessOrFailedAct if (space.entity.apps) { const newSpaceEntity = { entity: { - apps: [ ...space.entity.apps, app.metadata.guid ] + apps: [...space.entity.apps, app.metadata.guid] } }; updatedSpaces[spaceGuid] = newSpaceEntity; @@ -53,7 +53,7 @@ function deleteApplicationFromSpace(state: APIResource, action: APISuccessOrFail const updatedSpaces = {}; Object.keys(state).forEach(spaceGuid => { const space = state[spaceGuid] as APIResource; - const apps = space.entity.apps; + const apps = space.entity.apps; if (apps && apps.findIndex((value) => value === appGuid) >= 0) { const newSpaceEntity = { entity: { diff --git a/src/frontend/app/store/reducers/endpoint-disconnect-application.reducer.ts b/src/frontend/app/store/reducers/endpoint-disconnect-application.reducer.ts index 7228873c7e..e9d12abbc8 100644 --- a/src/frontend/app/store/reducers/endpoint-disconnect-application.reducer.ts +++ b/src/frontend/app/store/reducers/endpoint-disconnect-application.reducer.ts @@ -1,26 +1,23 @@ -import { APIResource } from '../types/api.types'; -import { IRequestEntityTypeState } from '../app-state'; +import { IApp } from '../../core/cf-api.types'; import { DISCONNECT_ENDPOINTS_SUCCESS, DisconnectEndpoint, UNREGISTER_ENDPOINTS_SUCCESS } from '../actions/endpoint.actions'; -export function endpointDisconnectApplicationReducer(entityKey) { - return function (state: APIResource, action: DisconnectEndpoint) { +import { APIResource } from '../types/api.types'; + +export function endpointDisconnectApplicationReducer() { + return function (state: { [appGuid: string]: APIResource<{ cfGuid: string }> }, action: DisconnectEndpoint) { switch (action.type) { case DISCONNECT_ENDPOINTS_SUCCESS: case UNREGISTER_ENDPOINTS_SUCCESS: - return deletionApplicationFromEndpoint(state, action.guid, entityKey); + return deletionApplicationFromEndpoint(state, action.guid); } return state; }; } -function deletionApplicationFromEndpoint(state: APIResource, endpointGuid, entityKey: string) { - const oldEntities = Object.values(state); - const entities = {}; - oldEntities.forEach(app => { - if (app.cfGuid !== endpointGuid && app.guid) { - entities[app.guid] = app; +function deletionApplicationFromEndpoint(state: { [appGuid: string]: APIResource<{ cfGuid: string }> }, endpointGuid) { + return Object.values(state).reduce((newEntities, app) => { + if (app.entity.cfGuid !== endpointGuid && app.metadata.guid) { + newEntities[app.metadata.guid] = app; } - }); - return entities; + return newEntities; + }, {}); } - - From 0c093e3c0a06355d6b8c2971bf2ae09b1debf76e Mon Sep 17 00:00:00 2001 From: Neil MacDougall Date: Fri, 5 Oct 2018 14:47:30 +0100 Subject: [PATCH 114/114] Weekly update 5 October --- docs/status_updates.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/status_updates.md b/docs/status_updates.md index a9e51ed782..c09ebcd373 100644 --- a/docs/status_updates.md +++ b/docs/status_updates.md @@ -2,6 +2,22 @@ Weekly status updates are published here. +## 5th October 2018 + +The team have been preparing for the CF Summit in Basel next week. + +Highlights for this week (continuing from last week): + +- E2E Tests and Automation - Focusing on making the E2E tests more resilient to timing issues. + +- Metrics - Added Diego Cell to the App Instances table when metrics are available and added a Cell view which shows metrics for a given Diego cell. + +Fixes: + +- Ensure we handle orgs with no users [\#3098](https://github.com/cloudfoundry-incubator/stratos/pull/3098) - fixes a bug creating a space in a new org. + +- Endpoint Registration: Only show SSO option for CF Endpoint type [#\3105](https://github.com/cloudfoundry-incubator/stratos/pull/3105) + ## 28th September 2018 This week saw the release of 2.1.1 and an update 2.1.2. It was necessary to tag a 2.1.2 release to resolve a broken backend dependency that would affect users deploying via 'cf push'. You should use 2.1.2 and not 2.1.1.
Usage