Refactorization and add metrics system.

This commit is contained in:
2019-08-01 21:47:15 +02:00
parent b3942828be
commit b7bfd817e9
19 changed files with 257 additions and 35 deletions

View File

@@ -46,8 +46,16 @@ public class AccountController {
@JsonView(View.UserDTO.class)
@PostMapping("/login")
public User login(@RequestBody final User pUser) throws BadCredentialsException {
return accountService.authenticate(pUser);
public User login(@RequestBody final User pUser, HttpServletResponse pResponse) throws BadCredentialsException {
User result = null;
try {
result = accountService.authenticate(pUser);
} catch(BadCredentialsException ex) {
pResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
return result;
}
@GetMapping("/logout")

View File

@@ -0,0 +1,14 @@
package org.codiki.core.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Clock;
@Configuration
public class CoreConfig {
@Bean
public Clock clock() {
return Clock.systemDefaultZone();
}
}

View File

@@ -0,0 +1,37 @@
package org.codiki.core.entities.dto;
import java.time.LocalDate;
import java.time.LocalDateTime;
public class Metrics {
/** Rest API version number. */
private String version;
/** Application start date. */
private LocalDateTime uptime;
/** Platform name. */
private String platform;
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public LocalDateTime getUptime() {
return uptime;
}
public void setUptime(LocalDateTime uptime) {
this.uptime = uptime;
}
public String getPlatform() {
return platform;
}
public void setPlatform(String platform) {
this.platform = platform;
}
}

View File

@@ -0,0 +1,26 @@
package org.codiki.metrics;
import org.codiki.core.entities.dto.Metrics;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/metrics")
public class MetricsController {
private MetricsService metricsService;
/**
* Constructor.
* @param metricsService Metrics service.
*/
public MetricsController(MetricsService metricsService) {
this.metricsService = metricsService;
}
@GetMapping("/healthCheck")
public Metrics healthCheck() {
return metricsService.getMetrics();
}
}

View File

@@ -0,0 +1,44 @@
package org.codiki.metrics;
import org.codiki.core.entities.dto.Metrics;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.Clock;
import java.time.LocalDateTime;
@Service
public class MetricsService {
/** Application start date. */
private LocalDateTime uptime;
/** Application version number. */
private String appVersion;
/** Platform name. */
private String appPlatform;
/**
* Constructor.
* @param clock System clock.
*/
public MetricsService(Clock clock,
@Value("${app.version}") String appVersion,
@Value("${app.platform}") String appPlatform) {
uptime = LocalDateTime.now(clock);
this.appVersion = appVersion;
this.appPlatform = appPlatform;
}
/**
* Returns application metrics like uptime, version number or platform name and others.
* @return Application metrics.
*/
public Metrics getMetrics() {
Metrics metrics = new Metrics();
metrics.setUptime(uptime);
metrics.setVersion(appVersion);
metrics.setPlatform(appPlatform);
return metrics;
}
}

View File

@@ -18,6 +18,7 @@ import org.codiki.core.entities.persistence.User;
import org.codiki.core.repositories.PostRepository;
import org.codiki.core.services.ParserService;
import org.codiki.core.services.UserService;
import org.codiki.core.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.DeleteMapping;
@@ -36,23 +37,37 @@ import com.fasterxml.jackson.annotation.JsonView;
public class PostController {
private static final int LIMIT_POSTS_HOME = 20;
@Autowired
/** Service to parse post content. */
private ParserService parserService;
@Autowired
/** Posts repository. */
private PostRepository postRepository;
@Autowired
/** Posts business service. */
private PostService postService;
@Autowired
/** Users business service. */
private UserService userService;
/**
* Constructor.
* @param parserService Service to parse post content.
* @param postRepository Posts repository.
* @param postService Posts business service.
* @param userService Users business service.
*/
public PostController(ParserService parserService,
PostRepository postRepository,
PostService postService,
UserService userService) {
this.parserService = parserService;
this.postRepository = postRepository;
this.postService = postService;
this.userService = userService;
}
@GetMapping
public List<PostDTO> getAll() {
return StreamSupport.stream(postRepository.findAll().spliterator(), false)
.map(PostDTO::new).collect(Collectors.toList());
.map(PostDTO::new)
.collect(Collectors.toList());
}
@JsonView(View.PostDTO.class)
@@ -70,16 +85,11 @@ public class PostController {
@GetMapping("/{postKey}/source")
public Post getByKeyAndSource(@PathVariable("postKey")final String pPostKey,
final HttpServletResponse response) {
Post result = null;
final Optional<Post> post = postRepository.getByKey(pPostKey);
if(post.isPresent()) {
result = post.get();
} else {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
}
return result;
return postRepository.getByKey(pPostKey)
.orElseGet(() -> {
response.setStatus(HttpServletResponse.SC_NOT_FOUND);
return null;
});
}
@JsonView(View.PostDTO.class)
@@ -121,9 +131,10 @@ public class PostController {
@JsonView(View.PostDTO.class)
@PostMapping("/preview")
public Post preview(@RequestBody final Post pPost) {
pPost.setImage(pPost.getImage() == null || "".equals(pPost.getImage())
pPost.setImage(StringUtils.isNull(pPost.getImage())
? "https://news-cdn.softpedia.com/images/news2/this-is-the-default-wallpaper-of-the-gnome-3-20-desktop-environment-500743-2.jpg"
: pPost.getImage());
pPost.setText(parserService.parse(pPost.getText()));
return pPost;
@@ -133,15 +144,7 @@ public class PostController {
@PostMapping("/")
public Post insert(@RequestBody final PostDTO pPost, final HttpServletRequest pRequest,
final HttpServletResponse pResponse, final Principal pPrincipal) {
Post result = null;
Optional<Post> postCreated = postService.insert(pPost, pRequest, pResponse, pPrincipal);
if(postCreated.isPresent()) {
result = postCreated.get();
}
return result;
return postService.insert(pPost, pRequest, pResponse, pPrincipal).orElse(null);
}
@PutMapping("/")

View File

@@ -1,6 +1,8 @@
app:
name: Codiki
description: A wiki application.
version: 1.2.0
platform: develop
codiki:
files:

View File

@@ -36,6 +36,7 @@ import { ForbiddenComponent } from './forbidden/forbidden.component';
import { SearchComponent } from './search/search.component';
import { SigninComponent } from './signin/signin.component';
import { VersionRevisionComponent } from './version-revisions/version-revisions.component';
import { HealthCheckComponent } from './health-check/health-check.component';
// Reusable components
import { PostCardComponent } from './core/post-card/post-card.component';
@@ -57,6 +58,7 @@ import { CreateUpdatePostService } from './posts/create-update/create-update-pos
import { SearchService } from './search/search.service';
import { SigninService } from './signin/signin.service';
import { VersionRevisionService } from './version-revisions/version-revisions.service';
import { HealthCheckService } from './health-check/health-check.service';
@NgModule({
declarations: [
@@ -81,7 +83,8 @@ import { VersionRevisionService } from './version-revisions/version-revisions.se
SearchComponent,
SearchBarComponent,
ProgressBarComponent,
ForbiddenComponent
ForbiddenComponent,
HealthCheckComponent
],
imports: [
BrowserModule,
@@ -110,6 +113,7 @@ import { VersionRevisionService } from './version-revisions/version-revisions.se
CreateUpdatePostService,
VersionRevisionService,
SearchService,
HealthCheckService,
{ provide: HTTP_INTERCEPTORS, useClass: UnauthorizedInterceptor, multi: true }
],
bootstrap: [AppComponent]

View File

@@ -14,6 +14,7 @@ import { PostComponent } from './posts/post.component';
import { ByCategoryComponent } from './posts/byCategory/by-category.component';
import { CreateUpdatePostComponent } from './posts/create-update/create-update-post.component';
import { VersionRevisionComponent } from './version-revisions/version-revisions.component';
import { HealthCheckComponent } from './health-check/health-check.component';
import { SearchComponent } from './search/search.component';
export const appRoutes: Routes = [
@@ -31,6 +32,7 @@ export const appRoutes: Routes = [
{ path: 'changePassword', component: ChangePasswordComponent, canActivate: [AuthGuard] },
{ path: 'profilEdit', component: ProfilEditionComponent, canActivate: [AuthGuard] },
{ path: 'versionrevisions', component: VersionRevisionComponent },
{ path: 'healthCheck', component: HealthCheckComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
{ path: '**', redirectTo: '/home' }
];

View File

@@ -73,3 +73,11 @@ export class VersionRevision {
public bugfix: boolean
) { }
}
export class Metrics {
constructor(
public version: String,
public uptime: Date,
public platform: String
) { }
}

View File

@@ -7,6 +7,13 @@
<i id="appVersion" routerLink="/versionrevisions" mdbTooltip="Notes de versions" placement="top" mdbRippleRadius>
{{appVersion}}
</i>
<i id="healthCheck"
class="fa fa-heartbeat"
routerLink="/healthCheck"
mdbTooltip="Ligne de vie"
placement="top"
mdbRippleRadius>
</i>
</span>
<span class="float-right">
<a target="_blank" href="./assets/doc/codiki_user_manual.pdf" mdbTooltip="Manuel d'utilisation" placement="top" mdbRippleRadius>

View File

@@ -23,3 +23,8 @@ span.anticopy {
display: inline;
cursor: pointer;
}
#healthCheck {
margin-left: 10px;
cursor: pointer;
}

View File

@@ -0,0 +1,23 @@
<div class="col-md-8 offset-md-2 col-lg-6 offset-lg-3">
<mdb-card>
<!--Card content-->
<mdb-card-body>
<!--Title-->
<mdb-card-title>
<h4>Status du serveur</h4>
</mdb-card-title>
<ul class="list-group list-group-flush">
<li class="list-group-item">
Plateforme <mdb-badge pill="true" primary="true" class="float-right">{{metrics.platform}}</mdb-badge>
</li>
<li class="list-group-item">
Version <mdb-badge pill="true" primary="true" class="float-right">{{metrics.version}}</mdb-badge>
</li>
<li class="list-group-item">
Démarré depuis {{metrics.uptime}}
</li>
</ul>
</mdb-card-body>
</mdb-card>
</div>

View File

@@ -0,0 +1,23 @@
import { HealthCheckService } from './health-check.service';
import { Component, OnInit } from '@angular/core';
import { Metrics } from '../core/entities';
@Component({
selector: 'app-health-check',
templateUrl: './health-check.component.html',
styleUrls: ['./health-check.component.scss']
})
export class HealthCheckComponent implements OnInit {
metrics: Metrics = new Metrics('1.0.0', null, '?');
constructor(
private healthCheckService: HealthCheckService
) {}
ngOnInit() {
this.healthCheckService.healthCheck().subscribe(metrics => {
this.metrics = metrics;
});
}
}

View File

@@ -0,0 +1,16 @@
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Metrics } from '../core/entities';
@Injectable({
providedIn: 'root'
})
export class HealthCheckService {
constructor(private http: HttpClient) {}
healthCheck(): Observable<Metrics> {
return this.http.get<Metrics>('/api/metrics/healthCheck');
}
}

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Post } from '../core/entities';