Skip to content
This repository was archived by the owner on May 14, 2025. It is now read-only.

Commit b261309

Browse files
ghillertcppwfs
authored andcommitted
gh-408 Refactor and port Analytics Dashboard tab
* Add `app-counter-header` directive * Add Bar Chart Fixed Code review comments on merge. Identified and fixed a couple of bugs as well.
1 parent eef387b commit b261309

15 files changed

+700
-30
lines changed

ui/src/app/analytics/analytics.module.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,19 @@ import { DashboardComponent } from './dashboard/dashboard.component';
77

88
import { AnalyticsService } from './analytics.service';
99
import { AnalyticsRoutingModule } from './analytics-routing.module';
10+
import { BarChartComponent } from './charts/bar-chart/bar-chart.component';
1011
import { GraphChartComponent } from './charts/graph-chart/graph-chart.component';
12+
import { CounterHeaderComponent } from './components/counter-header.component';
1113

1214
@NgModule({
1315
imports: [ AnalyticsRoutingModule, SharedModule ],
14-
declarations: [ AnalyticsComponent, CountersComponent, DashboardComponent, GraphChartComponent ],
16+
declarations: [
17+
AnalyticsComponent,
18+
BarChartComponent,
19+
CountersComponent,
20+
CounterHeaderComponent,
21+
DashboardComponent,
22+
GraphChartComponent ],
1523
providers: [ AnalyticsService ]
1624
})
1725
export class AnalyticsModule { }

ui/src/app/analytics/analytics.service.ts

Lines changed: 161 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import 'rxjs/add/operator/catch';
77
import 'rxjs/add/operator/map';
88

99
import { ErrorHandler, Page } from '../shared/model';
10-
import { Counter } from './counters/model/counter.model';
10+
import { Counter } from './model/counter.model';
11+
import { DashboardItem } from './model/dashboard-item.model';
12+
import { MetricType } from './model/metric-type.model';
13+
1114
import { HttpUtils } from '../shared/support/http.utils';
1215
import { ToastyService } from 'ng2-toasty';
1316
/**
@@ -22,6 +25,11 @@ export class AnalyticsService {
2225
public _counterInterval = 2;
2326
public counterPoller: Subscription;
2427

28+
public metricTypes: MetricType[] = MetricType.getMetricTypes();
29+
30+
private rowId = 1; // For Dashboard
31+
public dashboardItems: DashboardItem[];
32+
2533
constructor(
2634
private http: Http,
2735
private errorHandler: ErrorHandler,
@@ -89,10 +97,11 @@ export class AnalyticsService {
8997
*
9098
* @param detailed If true will request additional counter values from the REST endpoint
9199
*/
92-
public getAllCounters(detailed = false): Observable<Page<Counter>> {
100+
private getAllCounters(detailed = false): Observable<Page<Counter>> {
93101

94102
if (!this.counters) {
95103
this.counters = new Page<Counter>();
104+
this.counters.pageSize = 50;
96105
}
97106

98107
const params = HttpUtils.getPaginationParams(this.counters.pageNumber, this.counters.pageSize);
@@ -104,28 +113,38 @@ export class AnalyticsService {
104113

105114
requestOptionsArgs.search = params;
106115
return this.http.get(this.metricsCountersUrl, requestOptionsArgs)
107-
.map(this.extractData.bind(this))
116+
.map(response => this.extractData(response, detailed))
108117
.catch(this.errorHandler.handleError);
109118
}
110119

111-
private extractData(response: Response): Page<Counter> {
120+
private extractData(response: Response, handleRates: boolean): Page<Counter> {
112121
const body = response.json();
113122
const items: Counter[] = [];
114123
const cache: Counter[] = [];
115-
for (const oldCounter of this.counters.items) {
116-
cache[oldCounter.name] = oldCounter;
117-
}
118-
if (body._embedded && body._embedded.counterResourceList) {
119-
for (const counterResourceListItems of body._embedded.counterResourceList) {
120-
const counter = new Counter().deserialize(counterResourceListItems);
121-
122-
if (cache[counter.name]) {
123-
const cached = cache[counter.name];
124-
counter.rates = cached.rates;
125-
counter.rates.push((counter.value - cached.value) / this.counterInterval);
126-
counter.rates.splice(0, counter.rates.length - this.totalCacheSize());
124+
125+
if (handleRates) {
126+
for (const oldCounter of this.counters.items) {
127+
cache[oldCounter.name] = oldCounter;
128+
}
129+
if (body._embedded && body._embedded.counterResourceList) {
130+
for (const counterResourceListItems of body._embedded.counterResourceList) {
131+
const counter = new Counter().deserialize(counterResourceListItems);
132+
133+
if (cache[counter.name]) {
134+
const cached = cache[counter.name];
135+
counter.rates = cached.rates;
136+
counter.rates.push((counter.value - cached.value) / this.counterInterval);
137+
counter.rates.splice(0, counter.rates.length - this.totalCacheSize());
138+
}
139+
items.push(counter);
140+
}
141+
}
142+
} else {
143+
if (body._embedded && body._embedded.metricResourceList) {
144+
for (const metricResourceListItems of body._embedded.metricResourceList) {
145+
const counter = new Counter().deserialize(metricResourceListItems);
146+
items.push(counter);
127147
}
128-
items.push(counter);
129148
}
130149
}
131150

@@ -138,4 +157,129 @@ export class AnalyticsService {
138157
this.counters.update(page);
139158
return page;
140159
}
160+
161+
/**
162+
* Adds a new empty dashboard item to the dashboardItems array.
163+
* @param {number} index the location it should be added.
164+
* @returns {DashboardItem} the new instance of the {DashboardItem}.
165+
*/
166+
addNewDashboardItem(index?: number): DashboardItem {
167+
if (!this.dashboardItems) {
168+
this.dashboardItems = [];
169+
}
170+
const dashboardItem = new DashboardItem();
171+
dashboardItem.id = this.rowId++;
172+
dashboardItem.refreshRate = 2;
173+
dashboardItem.visualization = '';
174+
175+
if (index) {
176+
this.dashboardItems.splice(index, 0, dashboardItem);
177+
} else {
178+
this.dashboardItems.push(dashboardItem);
179+
}
180+
return dashboardItem;
181+
}
182+
183+
/**
184+
* Remove dashboard item from dashboardItems array and splice the array.
185+
* @param {number} index the offset of the dashboard item to remove.
186+
*/
187+
removeDashboardItem(index: number): void {
188+
if (!this.dashboardItems || this.dashboardItems.length === 0) {
189+
return;
190+
}
191+
this.dashboardItems.splice(index, 1);
192+
}
193+
194+
/**
195+
* Retrieve a list of all dashboardItems.
196+
* @returns {Observable<DashboardItem>} observable of dashboard items.
197+
*/
198+
getAllDashboardItems(): Observable<DashboardItem> {
199+
if (!this.dashboardItems) {
200+
this.addNewDashboardItem();
201+
}
202+
return Observable.from(this.dashboardItems);
203+
}
204+
205+
/**
206+
* Retrieve all metrics for a specific type.
207+
* @param {MetricType} metricType the specific metric type to retrieve.
208+
* @returns {Observable<Page<Counter>>} Page containing the metrics.
209+
*/
210+
getStreamsForMetricType(metricType: MetricType) {
211+
if (MetricType.COUNTER === metricType) {
212+
return this.getAllCounters();
213+
} else {
214+
this.toastyService.error(`Metric type ${metricType.name} is not supported.`);
215+
}
216+
}
217+
218+
resetDashboard() {
219+
this.dashboardItems.length = 0;
220+
this.addNewDashboardItem();
221+
}
222+
223+
/**
224+
* Starts the polling process for a single counters. Method
225+
* will check if the poller is already running and will
226+
* start the poller only if the poller is undefined or
227+
* stopped. The subscription is store on the {@link DashboardItem}.
228+
*/
229+
public startPollingForSingleDashboardItem(dashboardItem: DashboardItem) {
230+
console.log(dashboardItem);
231+
if (!dashboardItem.counterPoller || dashboardItem.counterPoller.closed) {
232+
dashboardItem.counterPoller = Observable.interval(dashboardItem.refreshRate * 1000)
233+
.switchMap(() => this.getSingleCounter(dashboardItem.counter.name)).subscribe(
234+
result => {
235+
dashboardItem.counter.rates.push((result.value - dashboardItem.counter.value) / dashboardItem.refreshRate);
236+
dashboardItem.counter.rates.splice(0, dashboardItem.counter.rates.length - this.totalCacheSize());
237+
dashboardItem.counter.value = result.value;
238+
},
239+
error => {
240+
console.log('error', error);
241+
this.toastyService.error(error);
242+
});
243+
}
244+
}
245+
246+
/**
247+
* Stops the polling process for counters if the poller
248+
* is running and is defined.
249+
*/
250+
public stopPollingOfSingleDashboardItem(dashboardItem: DashboardItem) {
251+
if (dashboardItem.counterPoller && !dashboardItem.counterPoller.closed) {
252+
dashboardItem.counterPoller.unsubscribe();
253+
}
254+
}
255+
256+
/**
257+
* Restarts the polling process for counters if the poller
258+
* is running and is defined. Will stop the poller first and restart
259+
* the counter with the {@link DashboardItem}s refresh rate.
260+
*
261+
* Will NOT restart the poller if the refresh rate is zero.
262+
*/
263+
public restartPollingOfSingleDashboardItem(dashboardItem: DashboardItem) {
264+
this.stopPollingOfSingleDashboardItem(dashboardItem);
265+
if (dashboardItem.refreshRate > 0 ) {
266+
this.startPollingForSingleDashboardItem(dashboardItem);
267+
}
268+
}
269+
270+
/**
271+
* Retrieves all counters. Will take pagination into account.
272+
*
273+
* @param detailed If true will request additional counter values from the REST endpoint
274+
*/
275+
private getSingleCounter(counterName: string): Observable<Counter> {
276+
const requestOptionsArgs: RequestOptionsArgs = HttpUtils.getDefaultRequestOptions();
277+
return this.http.get(this.metricsCountersUrl + '/' + counterName, requestOptionsArgs)
278+
.map(response => {
279+
const body = response.json();
280+
console.log('body', body);
281+
return new Counter().deserialize(body);
282+
})
283+
.catch(this.errorHandler.handleError);
284+
}
141285
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<div class="d3-chart" #chart></div>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
@import '~pivotal-ui-git-clone/src/css/pui-variables';
2+
3+
.d3-chart {
4+
.bar {
5+
fill: $blue-1;
6+
7+
&:hover {
8+
fill: $blue-3;
9+
}
10+
}
11+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
import { BarChartComponent } from './bar-chart.component';
3+
4+
describe('BarChartComponent', () => {
5+
let component: BarChartComponent;
6+
let fixture: ComponentFixture<BarChartComponent>;
7+
8+
beforeEach(async(() => {
9+
TestBed.configureTestingModule({
10+
declarations: [ BarChartComponent ]
11+
})
12+
.compileComponents();
13+
}));
14+
15+
beforeEach(() => {
16+
fixture = TestBed.createComponent(BarChartComponent);
17+
component = fixture.componentInstance;
18+
});
19+
20+
it('should be created', () => {
21+
expect(component).toBeTruthy();
22+
});
23+
});

0 commit comments

Comments
 (0)