Commit 5ffafbfa authored by Vitali Stupin's avatar Vitali Stupin
Browse files

Merge pull request #19 in XTSS/xtss-catalogue from develop to release

* commit '3c07eac4':
  Version bump and dependencies update
  Adding support for REST services
parents 58da243a 3c07eac4
This diff is collapsed.
{
"name": "xtss-catalogue",
"version": "0.3.0",
"version": "0.4.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
......@@ -14,36 +14,36 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^8.2.0",
"@angular/common": "^8.2.0",
"@angular/compiler": "^8.2.0",
"@angular/core": "^8.2.0",
"@angular/forms": "^8.2.0",
"@angular/platform-browser": "^8.2.0",
"@angular/platform-browser-dynamic": "^8.2.0",
"@angular/router": "^8.2.0",
"@angular/animations": "^8.2.14",
"@angular/common": "^8.2.14",
"@angular/compiler": "^8.2.14",
"@angular/core": "^8.2.14",
"@angular/forms": "^8.2.14",
"@angular/platform-browser": "^8.2.14",
"@angular/platform-browser-dynamic": "^8.2.14",
"@angular/router": "^8.2.14",
"@ngx-translate/core": "^11.0.1",
"@ngx-translate/http-loader": "^4.0.0",
"bootstrap": "^4.3.1",
"core-js": "^2.5.4",
"rxjs": "~6.5.2",
"tslib": "^1.9.0",
"bootstrap": "^4.4.1",
"core-js": "^2.6.11",
"rxjs": "~6.5.3",
"tslib": "^1.10.0",
"zone.js": "~0.9.1"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.802.0",
"@angular/cli": "~8.2.0",
"@angular/compiler-cli": "^8.2.0",
"@angular/language-service": "^8.2.0",
"@angular-devkit/build-angular": "~0.803.20",
"@angular/cli": "~8.3.20",
"@angular/compiler-cli": "^8.2.14",
"@angular/language-service": "^8.2.14",
"@types/jasmine": "~2.8.8",
"@types/jasminewd2": "~2.0.3",
"@types/jasminewd2": "^2.0.8",
"@types/node": "~8.9.4",
"codelyzer": "^5.0.1",
"codelyzer": "^5.2.0",
"jasmine-core": "~2.99.1",
"jasmine-spec-reporter": "~4.2.1",
"karma": "^4.0.1",
"karma": "^4.4.1",
"karma-chrome-launcher": "~2.2.0",
"karma-coverage-istanbul-reporter": "~2.0.1",
"karma-coverage-istanbul-reporter": "^2.1.1",
"karma-jasmine": "~1.1.2",
"karma-jasmine-html-reporter": "^0.2.2",
"protractor": "~5.4.0",
......
export class Service {
status: string;
serviceCode: string;
openapi: string;
fullServiceName: string;
}
<div class="card">
<div class="card-header pointerCursor" (click)="showDetail()">
{{subsystem.fullSubsystemName}}
<span class="badge badge-secondary" *ngIf="!subsystem.methods.length && subsystem.subsystemStatus == 'OK'">{{'subsystem.statusEmpty' | translate}}</span>
<span class="badge badge-secondary" *ngIf="!subsystem.methods.length && !subsystem.services.length && subsystem.subsystemStatus == 'OK'">{{'subsystem.statusEmpty' | translate}}</span>
<span class="badge badge-danger" *ngIf="subsystem.subsystemStatus == 'ERROR'">{{'subsystem.statusError' | translate}}</span>
</div>
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'ERROR'">
<p>{{'subsystem.statusErrorInfo' | translate}}</p>
</div>
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'OK' && !subsystem.methods.length">
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'OK' && !subsystem.methods.length && !subsystem.services.length">
<p>{{'subsystem.statusEmptyInfo' | translate}}</p>
</div>
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'OK' && subsystem.methods.length">
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'OK' && (subsystem.methods.length || subsystem.services.length)">
<h6 *ngIf="subsystem.methods.length" class="card-subtitle text-muted">SOAP</h6>
<p *ngFor="let method of getMethodsPreview()">
{{method.fullMethodName}}
<a href="{{getApiUrlBase()}}{{method.wsdl}}" class="badge badge-success"
*ngIf="method.wsdl" [target]="'_blank'">WSDL</a>
<span class="badge badge-info" *ngIf="method.methodStatus == 'REST'">REST</span>
<span class="badge badge-danger" *ngIf="method.methodStatus == 'ERROR'">{{'subsystem.statusWsdlError' | translate}}</span>
<span class="badge badge-danger" *ngIf="method.methodStatus == 'TIMEOUT'">{{'subsystem.statusWsdlTimeout' | translate}}</span>
<span class="badge badge-warning" *ngIf="method.methodStatus == 'SKIPPED'">{{'subsystem.statusWsdlSkipped' | translate}}</span>
</p>
<p *ngIf="getNotInPreview() > 0" class="pointerCursor" (click)="showDetail()">
{{'subsystem.moreMethods' | translate:{"count": getNotInPreview()} }}
</p>
<p *ngIf="getMethodsNotInPreview() > 0" class="pointerCursor" (click)="showDetail()">
{{'subsystem.moreMethods' | translate:{"count": getMethodsNotInPreview()} }}
</p>
<h6 *ngIf="subsystem.services.length" class="card-subtitle text-muted">REST</h6>
<p *ngFor="let service of getServicesPreview()">
{{service.fullServiceName}}
<a href="{{getApiUrlBase()}}{{service.openapi}}" class="badge badge-success"
*ngIf="service.openapi" [target]="'_blank'">OpenAPI</a>
<span class="badge badge-danger" *ngIf="service.status == 'ERROR'">{{'subsystem.statusOpenapiError' | translate}}</span>
<span class="badge badge-danger" *ngIf="service.status == 'TIMEOUT'">{{'subsystem.statusOpenapiTimeout' | translate}}</span>
<span class="badge badge-warning" *ngIf="service.status == 'SKIPPED'">{{'subsystem.statusOpenapiSkipped' | translate}}</span>
</p>
<p *ngIf="getServicesNotInPreview() > 0" class="pointerCursor" (click)="showDetail()">
{{'subsystem.moreMethods' | translate:{"count": getServicesNotInPreview()} }}
</p>
</div>
</div>
......@@ -8,6 +8,7 @@ import { Router } from '@angular/router';
import { Method } from 'src/app/method';
import { AppConfigMock } from 'src/app/app.config-mock';
import { AppConfig } from 'src/app/app.config';
import { Service } from 'src/app/service';
const PREVIEW_SIZE = 5;
......@@ -48,8 +49,10 @@ describe('SubsystemItemComponent', () => {
memberCode: 'CODE',
subsystemCode: 'SUB',
subsystemStatus: 'OK',
servicesStatus: 'OK',
fullSubsystemName: 'INST/CLASS/CODE/SUB',
methods: []
methods: [],
services: []
};
fixture.detectChanges();
});
......@@ -71,12 +74,28 @@ describe('SubsystemItemComponent', () => {
expect(component.getMethodsPreview().length).toBe(PREVIEW_SIZE);
});
it('should preview services', () => {
expect(component.getServicesPreview().length).toBe(0);
for (let i = 0; i < PREVIEW_SIZE + 10; i++) {
component.subsystem.services.push(new Service());
}
expect(component.getServicesPreview().length).toBe(PREVIEW_SIZE);
});
it('should calculate methods not in preview', () => {
expect(component.getNotInPreview()).toBe(0);
expect(component.getMethodsNotInPreview()).toBe(0);
for (let i = 0; i < PREVIEW_SIZE + 10; i++) {
component.subsystem.methods.push(new Method());
}
expect(component.getNotInPreview()).toBe(10);
expect(component.getMethodsNotInPreview()).toBe(10);
});
it('should calculate services not in preview', () => {
expect(component.getServicesNotInPreview()).toBe(0);
for (let i = 0; i < PREVIEW_SIZE + 10; i++) {
component.subsystem.services.push(new Service());
}
expect(component.getServicesNotInPreview()).toBe(10);
});
it('should go to detail view', () => {
......
import { Component, OnInit, Input } from '@angular/core';
import { Subsystem } from '../../subsystem';
import { Method } from '../../method';
import { Service } from '../../service';
import { SubsystemsService } from '../../subsystems.service';
import { Router } from '@angular/router';
import { AppConfig } from '../../app.config';
......@@ -27,13 +28,24 @@ export class SubsystemItemComponent implements OnInit {
return this.subsystem.methods.length ? this.subsystem.methods.slice(0, this.config.getConfig('PREVIEW_SIZE')) : [];
}
getNotInPreview(): number {
getServicesPreview(): Service[] {
return this.subsystem.services.length ? this.subsystem.services.slice(0, this.config.getConfig('PREVIEW_SIZE')) : [];
}
getMethodsNotInPreview(): number {
if (this.subsystem.methods.length - this.config.getConfig('PREVIEW_SIZE') < 0) {
return 0;
}
return this.subsystem.methods.length - this.config.getConfig('PREVIEW_SIZE');
}
getServicesNotInPreview(): number {
if (this.subsystem.services.length - this.config.getConfig('PREVIEW_SIZE') < 0) {
return 0;
}
return this.subsystem.services.length - this.config.getConfig('PREVIEW_SIZE');
}
showDetail() {
this.router.navigateByUrl(
'/' + this.subsystem.xRoadInstance
......
import { Method } from './method';
import { Service } from './service';
export class Subsystem {
memberClass: string;
subsystemCode: string;
xRoadInstance: string;
subsystemStatus: string;
servicesStatus: string;
memberCode: string;
fullSubsystemName: string;
methods: Method[];
services: Service[];
}
......@@ -11,30 +11,39 @@
<div class="card">
<div class="card-header">
{{subsystem.fullSubsystemName}}
<span class="badge badge-secondary" *ngIf="!subsystem.methods.length && subsystem.subsystemStatus == 'OK'">{{'subsystem.statusEmpty' | translate}}</span>
<span class="badge badge-secondary" *ngIf="!subsystem.methods.length && !subsystem.services.length && subsystem.subsystemStatus == 'OK'">{{'subsystem.statusEmpty' | translate}}</span>
<span class="badge badge-danger" *ngIf="subsystem.subsystemStatus == 'ERROR'">{{'subsystem.statusError' | translate}}</span>
</div>
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'ERROR'">
<p>{{'subsystem.statusErrorInfo' | translate}}</p>
</div>
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'OK' && !subsystem.methods.length">
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'OK' && !subsystem.methods.length && !subsystem.services.length">
<p>{{'subsystem.statusEmptyInfo' | translate}}</p>
</div>
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'OK' && subsystem.methods.length">
<div class="card-body" *ngIf="subsystem.subsystemStatus == 'OK' && (subsystem.methods.length || subsystem.services.length)">
<h6 *ngIf="subsystem.methods.length" class="card-subtitle text-muted">SOAP</h6>
<p *ngFor="let method of subsystem.methods">
{{method.fullMethodName}}
<a href="{{getApiUrlBase()}}{{method.wsdl}}" class="badge badge-success"
*ngIf="method.wsdl" [target]="'_blank'">WSDL</a>
<span class="badge badge-info" *ngIf="method.methodStatus == 'REST'">REST</span>
<span class="badge badge-danger" *ngIf="method.methodStatus == 'ERROR'">{{'subsystem.statusWsdlError' | translate}}</span>
<span class="badge badge-danger" *ngIf="method.methodStatus == 'TIMEOUT'">{{'subsystem.statusWsdlTimeout' | translate}}</span>
<span class="badge badge-warning" *ngIf="method.methodStatus == 'SKIPPED'">{{'subsystem.statusWsdlSkipped' | translate}}</span>
</p>
</p>
<h6 *ngIf="subsystem.services.length" class="card-subtitle text-muted">REST</h6>
<p *ngFor="let service of subsystem.services">
{{service.fullServiceName}}
<a href="{{getApiUrlBase()}}{{service.openapi}}" class="badge badge-success"
*ngIf="service.openapi" [target]="'_blank'">OpenAPI</a>
<span class="badge badge-danger" *ngIf="service.status == 'ERROR'">{{'subsystem.statusOpenapiError' | translate}}</span>
<span class="badge badge-danger" *ngIf="service.status == 'TIMEOUT'">{{'subsystem.statusOpenapiTimeout' | translate}}</span>
<span class="badge badge-warning" *ngIf="service.status == 'SKIPPED'">{{'subsystem.statusOpenapiSkipped' | translate}}</span>
</p>
</div>
</div>
</div>
<br>
<p *ngIf="subsystemSubject.value?.methods?.length">
<p *ngIf="subsystemSubject.value?.methods?.length || subsystemSubject.value?.services?.length">
<button type="button" [ngClass]="'btn btn-secondary'" (click)="scrollToTop()">{{'scrollToTop' | translate}}</button>
</p>
......@@ -68,9 +68,11 @@ describe('SubsystemComponent', () => {
subsystemCode: '',
xRoadInstance: '',
subsystemStatus: '',
servicesStatus: '',
memberCode: '',
fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
methods: []
methods: [],
services: []
}
]);
});
......@@ -114,9 +116,11 @@ describe('SubsystemComponent', () => {
subsystemCode: '',
xRoadInstance: '',
subsystemStatus: '',
servicesStatus: '',
memberCode: '',
fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
methods: []
methods: [],
services: []
}
]);
fixture = TestBed.createComponent(SubsystemComponent);
......@@ -223,9 +227,11 @@ describe('SubsystemComponent (with instance version)', () => {
subsystemCode: '',
xRoadInstance: '',
subsystemStatus: '',
servicesStatus: '',
memberCode: '',
fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
methods: []
methods: [],
services: []
}
]);
});
......
......@@ -2,6 +2,7 @@ import { SubsystemsService } from './subsystems.service';
import { of, defer } from 'rxjs';
import { Subsystem } from './subsystem';
import { Method } from './method';
import { Service } from './service';
import { HttpErrorResponse } from '@angular/common/http';
import { tick, fakeAsync } from '@angular/core/testing';
import { AppConfigMock } from './app.config-mock';
......@@ -46,6 +47,22 @@ describe('SubsystemsService', () => {
subsystemStatus: 'OK',
memberCode: 'MEMBER2',
methods: []
},
{
memberClass: 'CLASS',
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'OK',
memberCode: 'MEMBER3',
methods: [],
services: [
{
status: 'OK',
serviceCode: 'SERVICE',
openapi: 'URL'
}
]
}
];
const expectedSubsystems = [
......@@ -54,6 +71,7 @@ describe('SubsystemsService', () => {
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'ERROR',
memberCode: 'MEMBER',
fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
methods: [
......@@ -64,16 +82,37 @@ describe('SubsystemsService', () => {
serviceVersion: 'VER',
fullMethodName: 'INST/CLASS/MEMBER/SYSTEM/SERVICE/VER'
}
]
],
services: []
},
{
memberClass: 'CLASS',
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'ERROR',
memberCode: 'MEMBER2',
fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
methods: []
methods: [],
services: []
},
{
memberClass: 'CLASS',
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'OK',
memberCode: 'MEMBER3',
fullSubsystemName: 'INST/CLASS/MEMBER3/SYSTEM',
methods: [],
services: [
{
status: 'OK',
serviceCode: 'SERVICE',
openapi: 'URL',
fullServiceName: 'INST/CLASS/MEMBER3/SYSTEM/SERVICE'
}
]
}
];
httpClientSpy.get.and.returnValue(of(sourceSubsystems));
......@@ -184,19 +223,34 @@ describe('SubsystemsService', () => {
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'ERROR',
memberCode: 'MEMBER',
fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
methods: [{} as Method]
methods: [{} as Method],
services: []
},
{
memberClass: 'CLASS',
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'ERROR',
memberCode: 'MEMBER2',
fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
methods: []
}
methods: [],
services: []
},
{
memberClass: 'CLASS',
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'OK',
memberCode: 'MEMBER3',
fullSubsystemName: 'INST/CLASS/MEMBER3/SYSTEM',
methods: [],
services: [{} as Service]
},
];
const expectedSubsystems = [
{
......@@ -204,9 +258,22 @@ describe('SubsystemsService', () => {
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'ERROR',
memberCode: 'MEMBER',
fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
methods: [{} as Method]
methods: [{} as Method],
services: []
},
{
memberClass: 'CLASS',
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'OK',
memberCode: 'MEMBER3',
fullSubsystemName: 'INST/CLASS/MEMBER3/SYSTEM',
methods: [],
services: [{} as Service]
}
];
service.subsystemsSubject.next(sourceSubsystems);
......@@ -221,6 +288,7 @@ describe('SubsystemsService', () => {
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'ERROR',
memberCode: 'MEMBER',
fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
methods: [
......@@ -238,16 +306,43 @@ describe('SubsystemsService', () => {
serviceVersion: 'VER',
fullMethodName: 'INST/CLASS/MEMBER/SYSTEM/SERVICE2/VER'
}
]
],
services: []
},
{
memberClass: 'CLASS',
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'ERROR',
memberCode: 'MEMBER2',
fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
methods: []
methods: [],
services: []
},
{
memberClass: 'CLASS',
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'OK',
memberCode: 'MEMBER3',
fullSubsystemName: 'INST/CLASS/MEMBER3/SYSTEM',
methods: [],
services: [
{
status: 'OK',
serviceCode: 'SERVICE',
openapi: 'URL',
fullServiceName: 'INST/CLASS/MEMBER3/SYSTEM/RESTSRV'
},
{
status: 'OK',
serviceCode: 'SERVICE2',
openapi: 'URL',
fullServiceName: 'INST/CLASS/MEMBER3/SYSTEM/RESTSRV2'
}
]
}
];
const expectedSubsystems1 = [
......@@ -256,9 +351,11 @@ describe('SubsystemsService', () => {
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'ERROR',
memberCode: 'MEMBER2',
fullSubsystemName: 'INST/CLASS/MEMBER2/SYSTEM',
methods: []
methods: [],
services: []
}
];
const expectedSubsystems2 = [
......@@ -267,6 +364,7 @@ describe('SubsystemsService', () => {
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'ERROR',
memberCode: 'MEMBER',
fullSubsystemName: 'INST/CLASS/MEMBER/SYSTEM',
methods: [
......@@ -277,6 +375,27 @@ describe('SubsystemsService', () => {
serviceVersion: 'VER',
fullMethodName: 'INST/CLASS/MEMBER/SYSTEM/SERVICE2/VER'
}
],
services: []
}
];
const expectedSubsystems3 = [
{
memberClass: 'CLASS',
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'OK',
memberCode: 'MEMBER3',
fullSubsystemName: 'INST/CLASS/MEMBER3/SYSTEM',
methods: [],
services: [
{
status: 'OK',
serviceCode: 'SERVICE2',
openapi: 'URL',
fullServiceName: 'INST/CLASS/MEMBER3/SYSTEM/RESTSRV2'
}
]
}
];
......@@ -294,6 +413,12 @@ describe('SubsystemsService', () => {
tick(config.getConfig('FILTER_DEBOUNCE'));
expect(service.filteredSubsystemsSubject.value).toEqual(expectedSubsystems2);
// Search member with multiple services
service.setFilter('RESTSRV2');
// Waiting for a debounce time to apply filter
tick(config.getConfig('FILTER_DEBOUNCE'));
expect(service.filteredSubsystemsSubject.value).toEqual(expectedSubsystems3);
// Search with limit
const sourceSubsystems2 = [];
for (let i = 0; i < config.getConfig('DEFAULT_LIMIT') + 1; i++) {
......@@ -303,9 +428,11 @@ describe('SubsystemsService', () => {
subsystemCode: 'SYSTEM',
xRoadInstance: 'INST',
subsystemStatus: 'OK',
servicesStatus: 'OK',
memberCode: 'MEMBER' + i,
fullSubsystemName: 'INST/CLASS/MEMBER' + i + '/SYSTEM',
methods: []
methods: [],
services: []
}
);
}
......
......@@ -4,6 +4,7 @@ import { of, BehaviorSubject, Subject } from 'rxjs';
import { catchError, debounceTime, distinctUntilChanged } from 'rxjs/operators';
import { Subsystem } from './subsystem';
import { Method } from './method';
import { Service } from './service';
import { AppConfig } from './app.config';
import { InstanceVersion } from './instance-version';
......@@ -41,35 +42,43 @@ export class SubsystemsService {
const filtered: Subsystem[] = [];
let limit: number = this.limit;
for (let subsystem of this.subsystemsSubject.value) {
if (this.nonEmpty && !subsystem.methods.length) {
if (this.nonEmpty && !subsystem.methods.length && !subsystem.services.length) {
// Filtering out empty subsystems
continue;
}