Skip to content

Commit

Permalink
Add view for live statistics
Browse files Browse the repository at this point in the history
Let's now write a new feature for our application.
Since `Flux` and `Mono` enable interesting streaming scenario,
we can stream the live count of project creations to our app and
chart that data on a dedicated page.
  • Loading branch information
bclozel authored and snicoll committed May 24, 2019
1 parent c15d4d9 commit 75b95b2
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 1 deletion.
Expand Up @@ -3,8 +3,11 @@
import java.time.LocalDate;

import io.spring.sample.dashboard.stats.StatsService;
import io.spring.sample.dashboard.stats.support.GenerationStatisticsItem;
import reactor.core.publisher.Flux;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
Expand Down Expand Up @@ -41,4 +44,9 @@ public String showDashboard(@PathVariable @DateTimeFormat(iso = DateTimeFormat.I
return "index";
}

@GetMapping(path = "/live/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<GenerationStatisticsItem> liveStats() {
return this.statsService.fetchLiveStats();
}

}
Expand Up @@ -10,5 +10,6 @@ public class WebConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/live").setViewName("live");
}
}
Expand Up @@ -2,6 +2,7 @@

import io.spring.sample.dashboard.stats.support.Event;
import io.spring.sample.dashboard.stats.support.GenerationStatistics;
import io.spring.sample.dashboard.stats.support.GenerationStatisticsItem;
import io.spring.sample.dashboard.stats.support.GeneratorClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand Down Expand Up @@ -33,4 +34,9 @@ public Flux<GeneratorClient> fetchGeneratorClients(String fromDate, String toDat
.retrieve().bodyToFlux(GeneratorClient.class);
}

public Flux<GenerationStatisticsItem> liveStats() {
return this.client.get().uri("/live-statistics")
.retrieve().bodyToFlux(GenerationStatisticsItem.class);
}

}
Expand Up @@ -6,6 +6,7 @@
import io.spring.sample.dashboard.DashboardProperties;
import io.spring.sample.dashboard.stats.support.Event;
import io.spring.sample.dashboard.stats.support.GenerationStatistics;
import io.spring.sample.dashboard.stats.support.GenerationStatisticsItem;
import io.spring.sample.dashboard.stats.support.ReverseLookupDescriptor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand All @@ -19,14 +20,16 @@ public class StatsService {

private final ReverseLookupClient lookupClient;

private final Flux<GenerationStatisticsItem> liveStats;

private Duration timeout;

public StatsService(StatsClient statsClient, ReverseLookupClient lookupClient,
DashboardProperties properties) {
this.statsClient = statsClient;
this.lookupClient = lookupClient;
this.timeout = properties.getReverseLookup().getTimeout();

this.liveStats = statsClient.liveStats().share().cache(20);
}

public Mono<StatsContainer> fetchStats(String fromDate, String toDate) {
Expand All @@ -53,4 +56,9 @@ Flux<ReverseLookupDescriptor> fetchTopClients(String fromDate, String toDate) {
);
}

public Flux<GenerationStatisticsItem> fetchLiveStats() {
return this.liveStats;
}

}

103 changes: 103 additions & 0 deletions dashboard/src/main/resources/templates/live.html
@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Initializr Dashboard</title>

<link data-th-href="@{/webjars/bulma/css/bulma.css}" rel="stylesheet"/>
<link rel="stylesheet" data-th-href="@{/webjars/font-awesome/css/font-awesome.min.css}"/>
<link rel="stylesheet" data-th-href="@{/webjars/highcharts/css/highcharts.css}"/>
<script type="text/javascript" data-th-src="@{/webjars/highcharts/highcharts.js}"></script>
<script type="text/javascript" data-th-src="@{/webjars/highcharts/modules/annotations.js}"></script>
</head>
<body>
<section class="hero is-primary is-bold">
<div class="hero-body">
<div class="container">
<h1 class="title">
<i class="fa fa-area-chart" aria-hidden="true"></i>&nbsp;Initializr Dashboard
</h1>
</div>
</div>
</section>
<section class="section">
<div class="container">
<div class="container">
<div id="chart" style="height: 400px; min-width: 310px"></div>
</div>
</div>
</section>
<script data-th-inline="javascript">
var chart = Highcharts.chart('chart', {
chart: {
type: 'column'
},
title: {
text: 'Live project generation'
},
xAxis: {
type: 'datetime',
labels: {
rotation: -45,
style: {
fontSize: '13px',
fontFamily: 'Verdana, sans-serif'
}
}
},
yAxis: {
min: 0,
allowDecimals: false,
title: {
text: 'Project count'
}
},
responsive: {
rules: [{
condition: {
maxWidth: 500
},
chartOptions: {
legend: {
layout: 'horizontal',
align: 'center',
verticalAlign: 'bottom'
}
}
}]
},
plotOptions: {
series: {
animation:{
duration:250
},
pointPadding: 0,
groupPadding: 0,
borderWidth: 0
}
},
series: [{
name: 'projects',
data: []

}]
});
var appendGenerationData = function (sample) {
chart.series
.filter(function (serie) {
return serie.name == 'projects'
})
.forEach(function (serie) {
var shift = serie.data.length > 40;
serie.addPoint([Date.parse(sample.range.from), sample.projectsCount], true, shift);
});
};
var projectGenerationEventSource = new EventSource(/*[[@{|/live/stream|}]]*/ "/live");
projectGenerationEventSource.onmessage = function (e) {
appendGenerationData(JSON.parse(e.data));
};
</script>

</body>
</html>

0 comments on commit 75b95b2

Please sign in to comment.