mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 02:24:11 +00:00
[CST-5535] test cases added.
This commit is contained in:
@@ -57,6 +57,7 @@
|
|||||||
"@angular/core": "~11.2.14",
|
"@angular/core": "~11.2.14",
|
||||||
"@angular/forms": "~11.2.14",
|
"@angular/forms": "~11.2.14",
|
||||||
"@angular/localize": "11.2.14",
|
"@angular/localize": "11.2.14",
|
||||||
|
"@angular/material": "9.2.0",
|
||||||
"@angular/platform-browser": "~11.2.14",
|
"@angular/platform-browser": "~11.2.14",
|
||||||
"@angular/platform-browser-dynamic": "~11.2.14",
|
"@angular/platform-browser-dynamic": "~11.2.14",
|
||||||
"@angular/platform-server": "~11.2.14",
|
"@angular/platform-server": "~11.2.14",
|
||||||
|
@@ -210,8 +210,8 @@ import { ServerCheckGuard } from './core/server-check/server-check.guard';
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'health',
|
path: 'health',
|
||||||
loadChildren: () => import('./health-page/health.routing.module')
|
loadChildren: () => import('./health-page/health.module')
|
||||||
.then((m) => m.HealthPageRoutingModule)
|
.then((m) => m.HealthModule)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: ACCESS_CONTROL_MODULE_PATH,
|
path: ACCESS_CONTROL_MODULE_PATH,
|
||||||
|
32
src/app/health-page/health-data.service.ts
Normal file
32
src/app/health-page/health-data.service.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { map, switchMap } from 'rxjs/operators';
|
||||||
|
import { DspaceRestService } from '../core/dspace-rest/dspace-rest.service';
|
||||||
|
import { RawRestResponse } from '../core/dspace-rest/raw-rest-response.model';
|
||||||
|
import { HALEndpointService } from '../core/shared/hal-endpoint.service';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class HealthDataService {
|
||||||
|
constructor(protected halService: HALEndpointService,
|
||||||
|
protected restService: DspaceRestService) {
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @returns health data
|
||||||
|
*/
|
||||||
|
getHealth(): Observable<RawRestResponse> {
|
||||||
|
return this.halService.getEndpoint('/actuator').pipe(
|
||||||
|
map((restURL: string) => restURL + '/health'),
|
||||||
|
switchMap((endpoint: string) => this.restService.get(endpoint)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns information of server
|
||||||
|
*/
|
||||||
|
getInfo(): Observable<RawRestResponse> {
|
||||||
|
return this.halService.getEndpoint('/actuator').pipe(
|
||||||
|
map((restURL: string) => restURL + '/info'),
|
||||||
|
switchMap((endpoint: string) => this.restService.get(endpoint)));
|
||||||
|
}
|
||||||
|
}
|
@@ -1,12 +1,23 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { HealthPageRoutingModule } from './health.routing.module';
|
||||||
import { HealthComponent } from './health/health.component';
|
import { HealthComponent } from './health/health.component';
|
||||||
|
import { MatExpansionModule } from '@angular/material/expansion';
|
||||||
|
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
HealthPageRoutingModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
NgbModule,
|
||||||
|
TranslateModule
|
||||||
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
HealthComponent
|
HealthComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class HealthModule {
|
export class HealthModule {
|
||||||
|
|
||||||
}
|
}
|
@@ -2,12 +2,15 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
import { AuthenticatedGuard } from '../core/auth/authenticated.guard';
|
||||||
import { HealthComponent } from './health/health.component';
|
import { HealthComponent } from './health/health.component';
|
||||||
|
import { I18nBreadcrumbResolver } from '../core/breadcrumbs/i18n-breadcrumb.resolver';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
RouterModule.forChild([
|
RouterModule.forChild([
|
||||||
{
|
{
|
||||||
path: '',
|
path: '',
|
||||||
|
resolve: { breadcrumb: I18nBreadcrumbResolver },
|
||||||
|
data: { breadcrumbKey: 'health' },
|
||||||
canActivate: [AuthenticatedGuard],
|
canActivate: [AuthenticatedGuard],
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
|
@@ -1 +1,39 @@
|
|||||||
<p>health works!</p>
|
<div class="container">
|
||||||
|
<ul ngbNav #nav="ngbNav" [(activeId)]="activeId" class="nav-tabs">
|
||||||
|
<li [ngbNavItem]="'Health'">
|
||||||
|
<a ngbNavLink>{{'health-page.health' | translate}}</a>
|
||||||
|
<ng-template ngbNavContent>
|
||||||
|
<div id="health">
|
||||||
|
{{'health-page.status' | translate}} : <i [ngClass]="getHealthIconClass(healthGlobalStatus)"> </i>
|
||||||
|
<mat-accordion multi="true">
|
||||||
|
<mat-expansion-panel *ngFor="let health of healthArr">
|
||||||
|
<mat-expansion-panel-header>
|
||||||
|
<mat-panel-title> {{ health.components }} <i [ngClass]="getHealthIconClass(health.status)"></i></mat-panel-title>
|
||||||
|
</mat-expansion-panel-header>
|
||||||
|
<ng-template matExpansionPanelContent>
|
||||||
|
<div *ngFor="let item of health.details | keyvalue">
|
||||||
|
{{ item.key }} : {{item.value}}
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</mat-expansion-panel>
|
||||||
|
</mat-accordion>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</li>
|
||||||
|
<li [ngbNavItem]="'Info'">
|
||||||
|
<a ngbNavLink>{{'health-page.info' | translate}}</a>
|
||||||
|
<ng-template ngbNavContent>
|
||||||
|
<div id="info">
|
||||||
|
<small [ngStyle]="serverInfo.style" *ngFor="let serverInfo of serverInfoArr; let index = index">
|
||||||
|
{{ serverInfo.value }} <br>
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</ng-template>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div [ngbNavOutlet]="nav" class="mt-2"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -0,0 +1,8 @@
|
|||||||
|
.mat-expansion-panel-header {
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.circle-red {
|
||||||
|
color:red;
|
||||||
|
align-items: center;
|
||||||
|
}
|
@@ -1,14 +1,138 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { MatExpansionModule } from '@angular/material/expansion';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { of } from 'rxjs';
|
||||||
|
import { TranslateLoaderMock } from '../../shared/mocks/translate-loader.mock';
|
||||||
|
import { HealthDataService } from '../health-data.service';
|
||||||
|
import { HealthPageRoutingModule } from '../health.routing.module';
|
||||||
import { HealthComponent } from './health.component';
|
import { HealthComponent } from './health.component';
|
||||||
|
|
||||||
describe('HealthComponent', () => {
|
function getHealth() {
|
||||||
|
return of({
|
||||||
|
'payload':{
|
||||||
|
'status':'UP_WITH_ISSUES',
|
||||||
|
'components':{
|
||||||
|
'db':{
|
||||||
|
'status':'UP',
|
||||||
|
'components':{
|
||||||
|
'dataSource':{
|
||||||
|
'status':'UP',
|
||||||
|
'details':{
|
||||||
|
'database':'PostgreSQL',
|
||||||
|
'result':1,
|
||||||
|
'validationQuery':'SELECT 1'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'dspaceDataSource':{
|
||||||
|
'status':'UP',
|
||||||
|
'details':{
|
||||||
|
'database':'PostgreSQL',
|
||||||
|
'result':1,
|
||||||
|
'validationQuery':'SELECT 1'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'geoIp':{
|
||||||
|
'status':'UP_WITH_ISSUES',
|
||||||
|
'details':{
|
||||||
|
'reason':'The GeoLite Database file is missing (/var/lib/GeoIP/GeoLite2-City.mmdb)! Solr Statistics cannot generate location based reports! Please see the DSpace installation instructions for instructions to install this file.'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'solrOaiCore':{
|
||||||
|
'status':'UP',
|
||||||
|
'details':{
|
||||||
|
'status':0,
|
||||||
|
'detectedPathType':'particular core'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'solrSearchCore':{
|
||||||
|
'status':'UP',
|
||||||
|
'details':{
|
||||||
|
'status':0,
|
||||||
|
'detectedPathType':'particular core'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'solrStatisticsCore':{
|
||||||
|
'status':'UP',
|
||||||
|
'details':{
|
||||||
|
'status':0,
|
||||||
|
'detectedPathType':'particular core'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'statusCode':200,
|
||||||
|
'statusText':'OK'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getInfo() {
|
||||||
|
return of({
|
||||||
|
'payload':{
|
||||||
|
'app':{
|
||||||
|
'name':'DSpace at My University',
|
||||||
|
'version':'7.3',
|
||||||
|
'dir':'/Users/pratikrajkotiya/Documents/Project/FrontEnd/dspace-cris-install',
|
||||||
|
'url':'http://localhost:8080/server',
|
||||||
|
'db':'jdbc:postgresql://localhost:5432/4science',
|
||||||
|
'solr':{
|
||||||
|
'server':'http://localhost:8983/solr',
|
||||||
|
'prefix':''
|
||||||
|
},
|
||||||
|
'mail':{
|
||||||
|
'server':'smtp.example.com',
|
||||||
|
'from-address':'dspace-noreply@myu.edu',
|
||||||
|
'feedback-recipient':'dspace-help@myu.edu',
|
||||||
|
'mail-admin':'dspace-help@myu.edu',
|
||||||
|
'mail-helpdesk':'dspace-help@myu.edu',
|
||||||
|
'alert-recipient':'dspace-help@myu.edu'
|
||||||
|
},
|
||||||
|
'cors':{
|
||||||
|
'allowed-origins':'http://localhost:4000'
|
||||||
|
},
|
||||||
|
'ui':{
|
||||||
|
'url':'http://localhost:4000'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'statusCode':200,
|
||||||
|
'statusText':'OK'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMockHealthDataService() {
|
||||||
|
return jasmine.createSpyObj('healthDataService', {
|
||||||
|
getHealth: getHealth(),
|
||||||
|
getInfo: getInfo()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fdescribe('HealthComponent', () => {
|
||||||
let component: HealthComponent;
|
let component: HealthComponent;
|
||||||
let fixture: ComponentFixture<HealthComponent>;
|
let fixture: ComponentFixture<HealthComponent>;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
declarations: [ HealthComponent ]
|
imports: [
|
||||||
|
NgbNavModule,
|
||||||
|
CommonModule,
|
||||||
|
HealthPageRoutingModule,
|
||||||
|
MatExpansionModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useClass: TranslateLoaderMock
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
declarations: [ HealthComponent ],
|
||||||
|
providers:[{ provide: HealthDataService, useValue: getMockHealthDataService() }]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
});
|
});
|
||||||
@@ -22,4 +146,15 @@ describe('HealthComponent', () => {
|
|||||||
it('should create', () => {
|
it('should create', () => {
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should render health tab.', () => {
|
||||||
|
const healthTab = fixture.debugElement.query(By.css('#health'));
|
||||||
|
expect(healthTab).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render info tab.', () => {
|
||||||
|
const infoTab = fixture.debugElement.query(By.css('#info'));
|
||||||
|
expect(infoTab).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@@ -1,26 +1,100 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { DspaceRestService } from '../../core/dspace-rest/dspace-rest.service';
|
import { HealthDataService } from '../health-data.service';
|
||||||
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
|
|
||||||
|
|
||||||
|
|
||||||
|
enum HealthStatus {
|
||||||
|
UP = 'UP',
|
||||||
|
UP_WITH_ISSUES = 'UP_WITH_ISSUES',
|
||||||
|
DOWN = 'DOWN'
|
||||||
|
}
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-health',
|
selector: 'ds-health',
|
||||||
templateUrl: './health.component.html',
|
templateUrl: './health.component.html',
|
||||||
styleUrls: ['./health.component.scss']
|
styleUrls: ['./health.component.scss']
|
||||||
})
|
})
|
||||||
export class HealthComponent implements OnInit {
|
export class HealthComponent implements OnInit {
|
||||||
|
healthArr: string[];
|
||||||
constructor(protected halService: HALEndpointService,
|
serverInfoArr: string[];
|
||||||
protected restService: DspaceRestService) {
|
healthGlobalStatus: string;
|
||||||
}
|
activeId ='Health';
|
||||||
|
constructor(private healthDataService: HealthDataService) { }
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.halService.getRootHref();
|
this.healthDataService.getHealth().subscribe((data) => {
|
||||||
console.log('this.halService.getRootHref()',);
|
this.healthArr = this.getHealth(data.payload.components);
|
||||||
this.restService.get(this.halService.getRootHref() + '/actuator' + '/health').subscribe((data)=>{
|
this.healthGlobalStatus = data.payload.status;
|
||||||
console.log(data);
|
});
|
||||||
|
|
||||||
})
|
this.healthDataService.getInfo().subscribe((data) => {
|
||||||
|
this.serverInfoArr = this.getInfo(data.payload, null, []);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param obj represents a info
|
||||||
|
* @param key represents a nested key of info
|
||||||
|
* @param arr represents a key value pair or only key
|
||||||
|
* @returns {{arr}} of key value pair or only key
|
||||||
|
*/
|
||||||
|
getInfo(obj, key, arr) {
|
||||||
|
if (typeof obj === 'object' && key !== null) {
|
||||||
|
arr.push({style: {'font-weight': 'bold' ,'font-size.px': key === 'app' ? '30' : '20' }, value: key});
|
||||||
|
}
|
||||||
|
if (typeof obj !== 'object') {
|
||||||
|
arr.push({style: {'font-size.px': '15'}, value: `${key} = ${obj}`});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
// tslint:disable-next-line: forin
|
||||||
|
for (const objKey in obj) {
|
||||||
|
this.getInfo(obj[objKey], objKey, arr);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param subCompObj represent nested sub component
|
||||||
|
* @param superCompkey represents a key of super component
|
||||||
|
* @returns linear components array
|
||||||
|
*/
|
||||||
|
getHealthSubComponents(subCompObj, superCompkey) {
|
||||||
|
const subCompArr = [];
|
||||||
|
// tslint:disable-next-line: forin
|
||||||
|
for (const key in subCompObj) {
|
||||||
|
subCompArr.push({ ...subCompObj[key], components: superCompkey + '.' + key });
|
||||||
|
}
|
||||||
|
return subCompArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param componentsObj represent health data
|
||||||
|
* @returns linear components array
|
||||||
|
*/
|
||||||
|
getHealth(componentsObj) {
|
||||||
|
let componentsArr = [];
|
||||||
|
for (const key in componentsObj) {
|
||||||
|
if (componentsObj[key].hasOwnProperty('components')) {
|
||||||
|
componentsArr.push({ ...componentsObj[key], components: key });
|
||||||
|
// tslint:disable-next-line: no-string-literal
|
||||||
|
componentsArr = [...componentsArr, ...this.getHealthSubComponents(componentsObj[key]['components'], key)];
|
||||||
|
} else {
|
||||||
|
componentsArr.push({ ...componentsObj[key], components: key });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return componentsArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param status of perticular block
|
||||||
|
* @returns {{ string }} class respective status
|
||||||
|
*/
|
||||||
|
getHealthIconClass(status: string): string {
|
||||||
|
if (status === HealthStatus.UP) {
|
||||||
|
return 'fa fa-check-circle text-success ml-2 mt-1';
|
||||||
|
} else if (status === HealthStatus.UP_WITH_ISSUES) {
|
||||||
|
return 'fa fa-exclamation-triangle text-warning ml-2 mt-1';
|
||||||
|
} else {
|
||||||
|
return 'fa fa-times-circle circle-red ml-2 mt-1';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -2909,7 +2909,13 @@
|
|||||||
|
|
||||||
"profile.title": "Update Profile",
|
"profile.title": "Update Profile",
|
||||||
|
|
||||||
|
"health.breadcrumbs": "Health",
|
||||||
|
|
||||||
|
"health-page.health" : "Health",
|
||||||
|
|
||||||
|
"health-page.info" : "Info",
|
||||||
|
|
||||||
|
"health-page.status" : "Status",
|
||||||
|
|
||||||
"project.listelement.badge": "Research Project",
|
"project.listelement.badge": "Research Project",
|
||||||
|
|
||||||
|
@@ -397,6 +397,11 @@
|
|||||||
glob "7.1.2"
|
glob "7.1.2"
|
||||||
yargs "^16.2.0"
|
yargs "^16.2.0"
|
||||||
|
|
||||||
|
"@angular/material@9.2.0":
|
||||||
|
version "9.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@angular/material/-/material-9.2.0.tgz#1b6f0a2e115f93885d7fc2dc4b258d8c9cf6821f"
|
||||||
|
integrity sha512-KKzEIVh6/m56m+Ao8p4PK0SyEr0574l3VP2swj1qPag3u+FYgemmXCGTaChrKdDsez+zeTCPXImBGXzE6NQ80Q==
|
||||||
|
|
||||||
"@angular/platform-browser-dynamic@~11.2.14":
|
"@angular/platform-browser-dynamic@~11.2.14":
|
||||||
version "11.2.14"
|
version "11.2.14"
|
||||||
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.2.14.tgz#3c7fff1a1daacba5390acf033d28c377ec281166"
|
resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.2.14.tgz#3c7fff1a1daacba5390acf033d28c377ec281166"
|
||||||
|
Reference in New Issue
Block a user