Skip to content

Commit

Permalink
Added impact analysis data model, ui components and improved tests #79
Browse files Browse the repository at this point in the history
  • Loading branch information
tillias committed Nov 2, 2020
1 parent 6fc2744 commit 3adf1ea
Show file tree
Hide file tree
Showing 24 changed files with 361 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

Expand All @@ -30,7 +31,7 @@ public ImpactAnalysisService(GraphLoaderService graphLoaderService) {

public Optional<Result> calculate(final Long microserviceId) {
final GraphContext context = getConnectedSubgraphWithoutCycles(microserviceId);
if (context.hasEmptyGraph()){
if (context.hasEmptyGraph()) {
return Optional.empty();
}

Expand All @@ -45,7 +46,7 @@ public Optional<Result> calculate(final Long microserviceId) {

final Graph<Microservice, DefaultEdge> affectedGraph = new AsSubgraph<>(reversed, affectedMicroservices);

final Result result = new Result().target(context.getTarget());
final Result result = new Result().createdOn(Instant.now()).target(context.getTarget());

do {
final List<Microservice> verticesWithoutIncomingEdges = affectedGraph.vertexSet().stream()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.github.microcatalog.web.rest.custom;

import com.github.microcatalog.domain.custom.impact.analysis.Result;
import com.github.microcatalog.service.custom.ImpactAnalysisService;
import io.github.jhipster.web.util.ResponseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Optional;

/**
* REST controller for calculation of impact analysis {@link Result}
*/
@RestController
@RequestMapping("/api")
public class ImpactAnalysisCustomResource {

private final Logger log = LoggerFactory.getLogger(ImpactAnalysisCustomResource.class);

private final ImpactAnalysisService service;

public ImpactAnalysisCustomResource(ImpactAnalysisService service) {
this.service = service;
}

/**
* {@code GET /impact-analysis/microservice/:microserviceId} : get the impact analysis for "microserviceId"
*
* @param microserviceId id of Microservice for which impact analysis should be performed
* @return impact analysis calculated for given microservice
*/
@GetMapping("/impact-analysis/microservice/{microserviceId}")
public ResponseEntity<Result> calculate(@PathVariable Long microserviceId) {
log.debug("REST request to get impact analysis Result for microserviceId : {}", microserviceId);
final Optional<Result> impactAnalysisResult = service.calculate(microserviceId);
return ResponseUtil.wrapOrNotFound(impactAnalysisResult);
}
}
4 changes: 4 additions & 0 deletions src/main/webapp/app/dashboard/dashboard-routing.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import { RouterModule } from '@angular/router';
path: 'release-path',
loadChildren: () => import('./release-path-dashboard/release-path-dashboard.module').then(m => m.ReleasePathDashboardModule),
},
{
path: 'impact-analysis',
loadChildren: () => import('./impact-analysis/impact-analysis-dashboard.module').then(m => m.ImpactAnalysisDashboardModule),
},
]),
],
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
<button type="submit" [routerLink]="['/dashboard','release-path', selectedNodeId()]" class="btn btn-block btn-success btn-sm"
[disabled]="!nodeSelection">Build release path
</button>
<button type="submit" [routerLink]="['/dashboard','impact-analysis', selectedNodeId()]" class="btn btn-block btn-success btn-sm"
[disabled]="!nodeSelection">Impact analysis
</button>
</div>
</ng-template>
</ngb-panel>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<div class="row">
<div class="col-8">
<jhi-impact-analysis-graph></jhi-impact-analysis-graph>
</div>
<div class="col-4">
<jhi-impact-analysis-legend></jhi-impact-analysis-legend>
</div>
</div>

Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component, OnInit } from '@angular/core';

@Component({
selector: 'jhi-impact-analysis-dashboard',
templateUrl: './impact-analysis-dashboard.component.html',
styleUrls: ['./impact-analysis-dashboard.component.scss'],
})
export class ImpactAnalysisDashboardComponent implements OnInit {
constructor() {}

ngOnInit(): void {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NgModule } from '@angular/core';
import { MicrocatalogSharedModule } from 'app/shared/shared.module';
import { ImpactAnalysisDashboardComponent } from './impact-analysis-dashboard.component';
import { RouterModule } from '@angular/router';
import { impactAnalysisDashboardRoute } from './impact-analysis-dashboard.route';
import { ImpactAnalysisGraphModule } from './impact-analysis-graph/impact-analysis-graph.module';
import { ImpactAnalysisLegendModule } from './impact-analysis-legend/impact-analysis-legend.module';

@NgModule({
imports: [
MicrocatalogSharedModule,
ImpactAnalysisGraphModule,
ImpactAnalysisLegendModule,
RouterModule.forChild(impactAnalysisDashboardRoute),
],
declarations: [ImpactAnalysisDashboardComponent],
})
export class ImpactAnalysisDashboardModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { ActivatedRouteSnapshot, Resolve, Router, Routes } from '@angular/router';
import { ImpactAnalysisDashboardComponent } from './impact-analysis-dashboard.component';
import { Authority } from 'app/shared/constants/authority.constants';
import { UserRouteAccessService } from 'app/core/auth/user-route-access-service';
import { Injectable } from '@angular/core';
import { EMPTY, Observable, of } from 'rxjs';
import { catchError, flatMap } from 'rxjs/operators';
import { HttpResponse } from '@angular/common/http';
import { IResult, Result } from 'app/shared/model/impact/analysis/result.model';
import { ImpactAnalysisCustomService } from 'app/entities/release-path/custom/impact-analysis-custom.service';

@Injectable({ providedIn: 'root' })
export class ImpactAnalysisResultResolve implements Resolve<IResult> {
constructor(private service: ImpactAnalysisCustomService, private router: Router) {}

resolve(route: ActivatedRouteSnapshot): Observable<IResult> | Observable<never> {
const id = route.params['id'];
if (id) {
return this.service.find(id).pipe(
flatMap((analysisResult: HttpResponse<IResult>) => {
if (analysisResult.body) {
return of(analysisResult.body);
} else {
this.router.navigate(['404']);
return EMPTY;
}
}),
catchError(error => {
alert('Error building release path. ' + error.error.detail);
return EMPTY;
})
);
}
return of(new Result());
}
}

export const impactAnalysisDashboardRoute: Routes = [
{
path: ':id',
component: ImpactAnalysisDashboardComponent,
resolve: {
analysisResult: ImpactAnalysisResultResolve,
},
data: {
authorities: [Authority.USER],
pageTitle: 'microcatalogApp.microservice.home.title',
},
canActivate: [UserRouteAccessService],
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<p>impact-analysis-graph works!</p>
<p>{{analysisResult?.createdOn}}</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { IResult } from '../../../shared/model/impact/analysis/result.model';

@Component({
selector: 'jhi-impact-analysis-graph',
templateUrl: './impact-analysis-graph.component.html',
styleUrls: ['./impact-analysis-graph.component.scss'],
})
export class ImpactAnalysisGraphComponent implements OnInit {
analysisResult?: IResult;

constructor(protected activatedRoute: ActivatedRoute) {}

ngOnInit(): void {
this.activatedRoute.data.subscribe(({ analysisResult }) => {
this.analysisResult = analysisResult;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { ImpactAnalysisGraphComponent } from './impact-analysis-graph.component';
import { MicrocatalogSharedModule } from '../../../shared/shared.module';

@NgModule({
declarations: [ImpactAnalysisGraphComponent],
imports: [MicrocatalogSharedModule],
exports: [ImpactAnalysisGraphComponent],
})
export class ImpactAnalysisGraphModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<p>impact-analysis-legend works!</p>
<p>Groups: {{analysisResult?.groups?.length}}</p>
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Component, OnInit } from '@angular/core';
import { IResult } from '../../../shared/model/impact/analysis/result.model';
import { ActivatedRoute } from '@angular/router';

@Component({
selector: 'jhi-impact-analysis-legend',
templateUrl: './impact-analysis-legend.component.html',
styleUrls: ['./impact-analysis-legend.component.scss'],
})
export class ImpactAnalysisLegendComponent implements OnInit {
analysisResult?: IResult;

constructor(protected activatedRoute: ActivatedRoute) {}

ngOnInit(): void {
this.activatedRoute.data.subscribe(({ analysisResult }) => {
this.analysisResult = analysisResult;
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { NgModule } from '@angular/core';
import { MicrocatalogSharedModule } from 'app/shared/shared.module';
import { ImpactAnalysisLegendComponent } from './impact-analysis-legend.component';

@NgModule({
declarations: [ImpactAnalysisLegendComponent],
imports: [MicrocatalogSharedModule],
exports: [ImpactAnalysisLegendComponent],
})
export class ImpactAnalysisLegendModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Injectable } from '@angular/core';
import { SERVER_API_URL } from '../../../app.constants';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import * as moment from 'moment';
import { IResult } from '../../../shared/model/impact/analysis/result.model';

type EntityResponseType = HttpResponse<IResult>;

@Injectable({
providedIn: 'root',
})
export class ImpactAnalysisCustomService {
public resourceUrl = SERVER_API_URL + 'api/impact-analysis';

constructor(protected http: HttpClient) {}

find(microserviceId: number): Observable<EntityResponseType> {
return this.http
.get<IResult>(`${this.resourceUrl}/microservice/${microserviceId}`, { observe: 'response' })
.pipe(map((res: EntityResponseType) => this.convertDateFromServer(res)));
}

protected convertDateFromServer(res: EntityResponseType): EntityResponseType {
if (res.body) {
res.body.createdOn = res.body.createdOn ? moment(res.body.createdOn) : undefined;
}
return res;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { IItem } from './item.model';

export interface IGroup {
items: IItem[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { IMicroservice } from '../../microservice.model';

export interface IItem {
target: IMicroservice;
siblings: IMicroservice[];
}
13 changes: 13 additions & 0 deletions src/main/webapp/app/shared/model/impact/analysis/result.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Moment } from 'moment';
import { IMicroservice } from '../../microservice.model';
import { IGroup } from './group.model';

export interface IResult {
createdOn?: Moment;
target?: IMicroservice;
groups?: IGroup[];
}

export class Result implements IResult {
constructor(public createdOn?: Moment, public groups?: IGroup[], public target?: IMicroservice) {}
}
Loading

0 comments on commit 3adf1ea

Please sign in to comment.