feat: As a user I want to see my profile in the header and log myself out manually

This commit is contained in:
Marcel Haag 2023-03-08 11:28:16 +01:00 committed by Cel
parent 3be43fa96e
commit a37e06f8ca
20 changed files with 186 additions and 93 deletions

View File

@ -12,7 +12,7 @@ import {
NbSelectModule, NbSelectModule,
NbThemeModule, NbThemeModule,
NbOverlayContainerAdapter, NbOverlayContainerAdapter,
NbDialogModule, NbDialogModule, NbMenuModule,
} from '@nebular/theme'; } from '@nebular/theme';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {HttpClient, HttpClientModule} from '@angular/common/http'; import {HttpClient, HttpClientModule} from '@angular/common/http';
@ -58,6 +58,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
FontAwesomeModule, FontAwesomeModule,
BrowserAnimationsModule, BrowserAnimationsModule,
ThemeModule.forRoot(), ThemeModule.forRoot(),
NbMenuModule.forRoot(),
NbSelectModule, NbSelectModule,
NgxsModule.forRoot([SessionState, ProjectState], {developmentMode: !environment.production}), NgxsModule.forRoot([SessionState, ProjectState], {developmentMode: !environment.production}),
NgxsLoggerPluginModule.forRoot({developmentMode: !environment.production}), NgxsLoggerPluginModule.forRoot({developmentMode: !environment.production}),

View File

@ -7,7 +7,7 @@ import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
import {FlexLayoutModule, FlexModule} from '@angular/flex-layout'; import {FlexLayoutModule, FlexModule} from '@angular/flex-layout';
import {MomentModule} from 'ngx-moment'; import {MomentModule} from 'ngx-moment';
import {NotificationService} from '@shared/services/toaster-service/notification.service'; import {NotificationService} from '@shared/services/toaster-service/notification.service';
import {NbOverlayContainerAdapter, NbSpinnerModule, NbToastrModule} from '@nebular/theme'; import {NbMenuModule, NbOverlayContainerAdapter, NbSpinnerModule, NbToastrModule} from '@nebular/theme';
import {ThemeModule} from '@assets/@theme/theme.module'; import {ThemeModule} from '@assets/@theme/theme.module';
import {LoadingSpinnerComponent} from '@shared/widgets/loading-spinner/loading-spinner.component'; import {LoadingSpinnerComponent} from '@shared/widgets/loading-spinner/loading-spinner.component';
@ -26,6 +26,7 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
FontAwesomeModule, FontAwesomeModule,
FlexLayoutModule, FlexLayoutModule,
ThemeModule.forRoot(), ThemeModule.forRoot(),
NbMenuModule.forRoot(),
FlexModule, FlexModule,
HttpClientModule, HttpClientModule,
TranslateModule.forChild({ TranslateModule.forChild({

View File

@ -11,6 +11,7 @@
<div class="filler"></div> <div class="filler"></div>
<div fxLayoutGap="4rem"> <div fxLayoutGap="4rem">
<nb-actions size="medium"> <nb-actions size="medium">
<!--Theme Action-->
<nb-action class="toggle-theme"> <nb-action class="toggle-theme">
<button nbButton <button nbButton
(click)="onClickGoToLink('https://owasp.org/www-project-web-security-testing-guide/v42/')"> (click)="onClickGoToLink('https://owasp.org/www-project-web-security-testing-guide/v42/')">
@ -19,7 +20,6 @@
<span class="owasp-redirect-button">OWASP</span> <span class="owasp-redirect-button">OWASP</span>
</button> </button>
</nb-action> </nb-action>
<nb-action class="toggle-theme"> <nb-action class="toggle-theme">
<button nbButton <button nbButton
(click)="onClickSwitchTheme()"> (click)="onClickSwitchTheme()">
@ -30,16 +30,29 @@
</ng-template> </ng-template>
</button> </button>
</nb-action> </nb-action>
<!--Language Action-->
<nb-action class="language-menu">
<div *ngIf="selectedLanguage && languages" class="languageContainer">
<nb-select selected="{{selectedLanguage}}" fullWidth>
<nb-option *ngFor="let language of languages"
value="{{language}}" (click)="onClickLanguage(language)" fxLayoutAlign="start center">
<img src="../../assets/images/flags/{{language}}.svg" class="flag" width="25rem" height="16rem" alt="">
<span fxFlexOffset="0.5rem"> {{'languageKeys.' + language | translate}} </span>
</nb-option>
</nb-select>
</div>
</nb-action>
<!--User Action-->
<nb-action class="user">
<!--<fa-icon [icon]="fa.faUser" class="user-icon">
</fa-icon>-->
<nb-user [nbContextMenu]="userMenu"
[picture]="FALLBACK_IMG"
name="{{user?.getValue()?.username}}"
title="Pentester">
</nb-user>
</nb-action>
</nb-actions> </nb-actions>
</div> </div>
<div *ngIf="selectedLanguage && languages" class="languageContainer">
<nb-select selected="{{selectedLanguage}}" fullWidth>
<nb-option *ngFor="let language of languages"
value="{{language}}" (click)="onClickLanguage(language)" fxLayoutAlign="start center">
<img src="../../assets/images/flags/{{language}}.svg" class="flag" width="25rem" height="16rem" alt="">
<span fxFlexOffset="0.5rem"> {{'languageKeys.' + language | translate}} </span>
</nb-option>
</nb-select>
</div>
</div> </div>

View File

@ -3,17 +3,30 @@ import {ComponentFixture, TestBed} from '@angular/core/testing';
import {HeaderComponent} from './header.component'; import {HeaderComponent} from './header.component';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {FontAwesomeTestingModule} from '@fortawesome/angular-fontawesome/testing'; import {FontAwesomeTestingModule} from '@fortawesome/angular-fontawesome/testing';
import {NbActionsModule, NbSelectModule} from '@nebular/theme'; import {NbActionsModule, NbMenuModule, NbMenuService, NbSelectModule} from '@nebular/theme';
import {ThemeModule} from '@assets/@theme/theme.module'; import {ThemeModule} from '@assets/@theme/theme.module';
import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
import {HttpLoaderFactory} from '../common-app.module'; import {HttpLoaderFactory} from '../common-app.module';
import {HttpClient} from '@angular/common/http'; import {HttpClient} from '@angular/common/http';
import {RouterTestingModule} from '@angular/router/testing'; import {RouterTestingModule} from '@angular/router/testing';
import {HttpClientTestingModule} from '@angular/common/http/testing'; import {HttpClientTestingModule} from '@angular/common/http/testing';
import {NgxsModule, Store} from '@ngxs/store';
import {KeycloakService} from 'keycloak-angular';
import {SESSION_STATE_NAME, SessionState, SessionStateModel} from '@shared/stores/session-state/session-state';
import {User} from '@shared/models/user.model';
const DESIRED_STORE_STATE_SESSION: SessionStateModel = {
userAccount: {
...new User('ttt', 'test', 'user', 'default.user@test.de', 'en-US'),
id: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
},
isAuthenticated: true
};
describe('HeaderComponent', () => { describe('HeaderComponent', () => {
let component: HeaderComponent; let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>; let fixture: ComponentFixture<HeaderComponent>;
let store: Store;
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
@ -26,6 +39,7 @@ describe('HeaderComponent', () => {
NbSelectModule, NbSelectModule,
FontAwesomeTestingModule, FontAwesomeTestingModule,
HttpClientTestingModule, HttpClientTestingModule,
NbMenuModule,
ThemeModule.forRoot(), ThemeModule.forRoot(),
TranslateModule.forRoot({ TranslateModule.forRoot({
loader: { loader: {
@ -34,14 +48,23 @@ describe('HeaderComponent', () => {
deps: [HttpClient] deps: [HttpClient]
} }
}), }),
RouterTestingModule.withRoutes([]) RouterTestingModule.withRoutes([]),
NgxsModule.forRoot([SessionState])
],
providers: [
NbMenuService,
KeycloakService
] ]
}) }).compileComponents();
.compileComponents();
}); });
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(HeaderComponent); fixture = TestBed.createComponent(HeaderComponent);
store = TestBed.inject(Store);
store.reset({
...store.snapshot(),
[SESSION_STATE_NAME]: DESIRED_STORE_STATE_SESSION
});
component = fixture.componentInstance; component = fixture.componentInstance;
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -1,19 +1,29 @@
import {Component, OnDestroy, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import * as FA from '@fortawesome/free-solid-svg-icons'; import * as FA from '@fortawesome/free-solid-svg-icons';
import {NbThemeService} from '@nebular/theme'; import {NbMenuItem, NbMenuService, NbThemeService} from '@nebular/theme';
import {map} from 'rxjs/operators'; import {map} from 'rxjs/operators';
import {GlobalTitlesVariables} from '@shared/config/global-variables'; import {GlobalTitlesVariables} from '@shared/config/global-variables';
import {TranslateService} from '@ngx-translate/core'; import {TranslateService} from '@ngx-translate/core';
import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy'; import {UntilDestroy, untilDestroyed} from '@ngneat/until-destroy';
import {KeycloakService} from 'keycloak-angular';
import {Store} from '@ngxs/store';
import {ResetSession} from '@shared/stores/session-state/session-state.actions';
import {UserService} from '@shared/services/user-service/user.service';
import {User} from '@shared/models/user.model';
import {BehaviorSubject} from 'rxjs';
import {Route} from '@shared/models/route.enum';
import {environment} from '../../environments/environment';
import {Router} from '@angular/router';
@UntilDestroy()
@Component({ @Component({
selector: 'app-header', selector: 'app-header',
templateUrl: './header.component.html', templateUrl: './header.component.html',
styleUrls: ['./header.component.scss'] styleUrls: ['./header.component.scss']
}) })
export class HeaderComponent implements OnInit{ @UntilDestroy()
export class HeaderComponent implements OnInit {
// HTML only
readonly fa = FA; readonly fa = FA;
readonly SECURITYC4PO_TITLE: string = GlobalTitlesVariables.SECURITYC4PO_TITLE; readonly SECURITYC4PO_TITLE: string = GlobalTitlesVariables.SECURITYC4PO_TITLE;
@ -21,16 +31,59 @@ export class HeaderComponent implements OnInit{
languages = ['en-US', 'de-DE']; languages = ['en-US', 'de-DE'];
selectedLanguage = ''; selectedLanguage = '';
constructor(private themeService: NbThemeService, private translateService: TranslateService) { } // User Menu Properties
userPictureOnly = false;
user: BehaviorSubject<User> = new BehaviorSubject<User>(null);
userMenu: NbMenuItem[] = [{title: '', pathMatch: 'prefix'}];
readonly FALLBACK_IMG = 'assets/images/demo/anon-user-icon.png';
constructor(
private store: Store,
private router: Router,
private themeService: NbThemeService,
private translateService: TranslateService,
private menuService: NbMenuService,
private userService: UserService,
protected keycloakService: KeycloakService) {
}
ngOnInit(): void { ngOnInit(): void {
// Handle theme selection
this.themeService.onThemeChange() this.themeService.onThemeChange()
.pipe( .pipe(
map(({ name }) => name), map(({name}) => name),
untilDestroyed(this), untilDestroyed(this),
) ).subscribe(themeName => this.currentTheme = themeName);
.subscribe(themeName => this.currentTheme = themeName);
this.selectedLanguage = this.translateService.currentLang; this.selectedLanguage = this.translateService.currentLang;
// Load user profile
this.userService.loadUserProfile().pipe(
untilDestroyed(this)
).subscribe({
next: (user: User) => {
this.user.next(user);
},
error: err => {
console.error(err);
}
});
// Handle user profile manu selection
this.menuService.onItemClick()
.pipe(
untilDestroyed(this)
)
.subscribe((menuBag) => {
if (menuBag.item.pathMatch === 'prefix') {
this.onClickLogOut();
}
});
// Setup stream to translate menu item
this.translateService.stream('global.action.logout')
.pipe(
untilDestroyed(this)
).subscribe((text: string) => {
this.userMenu[0].title = text;
});
} }
// HTML only // HTML only
@ -46,6 +99,22 @@ export class HeaderComponent implements OnInit{
} }
} }
onClickLogOut(): void {
// ToDo: Redirect user to Landing page from Issue #142 https://github.com/Marcel-Haag/security-c4po/issues/143
// ToDo: Fix Redirect URI in Keycloak Setting
this.keycloakService.logout(`http://auth-server/realms/${environment.keycloakclientId}/protocol/openid-connect/logout`).then(() => {
// Route user back to default page
this.router.navigate([Route.HOME]).then(() => {
// Reset User props from store
this.store.dispatch(new ResetSession());
}, err => {
console.error(err);
});
}, err => {
console.error(err);
});
}
onClickLanguage(language: string): void { onClickLanguage(language: string): void {
this.translateService.use(language); this.translateService.use(language);
} }

View File

@ -1,7 +1,14 @@
import {NgModule} from '@angular/core'; import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {HeaderComponent} from './header.component'; import {HeaderComponent} from './header.component';
import {NbActionsModule, NbButtonModule, NbCardModule, NbSelectModule} from '@nebular/theme'; import {
NbActionsModule,
NbButtonModule,
NbCardModule,
NbContextMenuModule, NbMenuModule,
NbSelectModule,
NbUserModule
} from '@nebular/theme';
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
import {FlexLayoutModule} from '@angular/flex-layout'; import {FlexLayoutModule} from '@angular/flex-layout';
import {TranslateModule} from '@ngx-translate/core'; import {TranslateModule} from '@ngx-translate/core';
@ -13,16 +20,20 @@ import {TranslateModule} from '@ngx-translate/core';
exports: [ exports: [
HeaderComponent HeaderComponent
], ],
imports: [ imports: [
CommonModule, CommonModule,
NbButtonModule, NbButtonModule,
FontAwesomeModule, FontAwesomeModule,
NbCardModule, NbCardModule,
NbActionsModule, NbActionsModule,
FlexLayoutModule, FlexLayoutModule,
NbSelectModule, NbSelectModule,
TranslateModule TranslateModule,
] NbUserModule,
NbContextMenuModule
],
providers: [
]
}) })
export class HeaderModule { export class HeaderModule {
} }

View File

@ -81,7 +81,6 @@ describe('LoginComponent', () => {
...store.snapshot(), ...store.snapshot(),
[SESSION_STATE_NAME]: DESIRED_STORE_STATE_SESSION [SESSION_STATE_NAME]: DESIRED_STORE_STATE_SESSION
}); });
fixture = TestBed.createComponent(LoginComponent); fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
httpMock = TestBed.inject(HttpTestingController); httpMock = TestBed.inject(HttpTestingController);

View File

@ -1,5 +1,5 @@
<div class="pentest-categories"> <div class="pentest-categories">
<nb-menu class="menu-style" tag="menu" [items]="categories"></nb-menu> <nb-menu id="category-menu" class="menu-style" tag="menu" [items]="categories"></nb-menu>
</div> </div>

View File

@ -54,10 +54,10 @@ export class ObjectiveCategoriesComponent implements OnInit, OnDestroy {
category.selected = false; category.selected = false;
}); });
menuBag.item.selected = true; menuBag.item.selected = true;
this.store.dispatch(new ChangeCategory(this.selectedCategory)); if (this.selectedCategory) {
this.store.dispatch(new ChangeCategory(this.selectedCategory));
}
}); });
} }
private initTranslation(): void { private initTranslation(): void {

View File

@ -37,7 +37,6 @@ import {CommentWidgetModule} from '@shared/widgets/comment-widget/comment-widget
CommonAppModule, CommonAppModule,
NbLayoutModule, NbLayoutModule,
NbCardModule, NbCardModule,
NbMenuModule.forRoot(),
NbButtonModule, NbButtonModule,
// nbTooltip crashes app right now if used in component, // nbTooltip crashes app right now if used in component,
// workaround: use title in html for now // workaround: use title in html for now
@ -46,7 +45,6 @@ import {CommentWidgetModule} from '@shared/widgets/comment-widget/comment-widget
TranslateModule, TranslateModule,
StatusTagModule, StatusTagModule,
RouterModule, RouterModule,
NbMenuModule,
FormsModule, FormsModule,
NbListModule, NbListModule,
FontAwesomeModule, FontAwesomeModule,
@ -57,7 +55,8 @@ import {CommentWidgetModule} from '@shared/widgets/comment-widget/comment-widget
ObjectiveOverviewRoutingModule, ObjectiveOverviewRoutingModule,
// Table Widgets // Table Widgets
FindigWidgetModule, FindigWidgetModule,
CommentWidgetModule CommentWidgetModule,
NbMenuModule
], ],
exports: [ exports: [
ObjectiveHeaderComponent, ObjectiveHeaderComponent,

View File

@ -25,7 +25,7 @@
status="success" status="success"
[disabled]="!pentestStatusChanged() || !pentestHasFindingsOrComments()" [disabled]="!pentestStatusChanged() || !pentestHasFindingsOrComments()"
title="{{ 'global.action.save' | translate }}" title="{{ 'global.action.save' | translate }}"
(click)="onClickCompletePentestAndRouteBack()"> (click)="onClickCompletePentest()">
<fa-icon [icon]="fa.faSquare"></fa-icon> <fa-icon [icon]="fa.faSquare"></fa-icon>
<span class="action-element-text"> {{ 'global.action.complete' | translate }} </span> <span class="action-element-text"> {{ 'global.action.complete' | translate }} </span>
</button> </button>

View File

@ -95,7 +95,7 @@ export class PentestHeaderComponent implements OnInit, OnDestroy {
).finally(); ).finally();
} }
onClickCompletePentestAndRouteBack(): void { onClickCompletePentest(): void {
// Update existing Pentest // Update existing Pentest
this.pentest$.next({...this.pentest$.getValue(), status: PentestStatus.COMPLETED, timeSpent: this.currentTimeSpent}); this.pentest$.next({...this.pentest$.getValue(), status: PentestStatus.COMPLETED, timeSpent: this.currentTimeSpent});
this.updatePentest(); this.updatePentest();
@ -107,11 +107,11 @@ export class PentestHeaderComponent implements OnInit, OnDestroy {
next: (pentest: Pentest) => { next: (pentest: Pentest) => {
this.store.dispatch(new ChangePentest(pentest)); this.store.dispatch(new ChangePentest(pentest));
this.initialTimeSpent = pentest.timeSpent; this.initialTimeSpent = pentest.timeSpent;
this.notificationService.showPopup('pentest.popup.update.success', PopupType.SUCCESS); this.notificationService.showPopup('pentest.popup.complete.success', PopupType.SUCCESS);
}, },
error: err => { error: err => {
console.log(err); console.log(err);
this.notificationService.showPopup('pentest.popup.update.failed', PopupType.FAILURE); this.notificationService.showPopup('pentest.popup.complete.failed', PopupType.FAILURE);
} }
}); });
} }

View File

@ -1,6 +1,7 @@
{ {
"global": { "global": {
"action.login": "Einloggen", "action.login": "Einloggen",
"action.logout": "Ausloggen",
"action.retry": "Erneut Versuchen", "action.retry": "Erneut Versuchen",
"action.info": "Info", "action.info": "Info",
"action.save": "Speichern", "action.save": "Speichern",
@ -233,6 +234,8 @@
"initial.save.failed": "Initialer Pentest konnte nicht aufgesetzt werden", "initial.save.failed": "Initialer Pentest konnte nicht aufgesetzt werden",
"save.success": "Pentest erfolgreich gespeichert", "save.success": "Pentest erfolgreich gespeichert",
"save.failed": "Pentest konnte nicht gespeichert werden", "save.failed": "Pentest konnte nicht gespeichert werden",
"complete.success": "Pentest erfolgreich vervollständigt",
"complete.failed": "Pentest konnte nicht vervollständigt werden",
"update.success": "Pentest erfolgreich aktualisiert", "update.success": "Pentest erfolgreich aktualisiert",
"update.failed": "Pentest konnte nicht aktualisiert werden", "update.failed": "Pentest konnte nicht aktualisiert werden",
"delete.success": "Pentest erfolgreich gelöscht", "delete.success": "Pentest erfolgreich gelöscht",

View File

@ -1,6 +1,7 @@
{ {
"global": { "global": {
"action.login": "Login", "action.login": "Login",
"action.logout": "Logout",
"action.retry": "Try again", "action.retry": "Try again",
"action.info": "Info", "action.info": "Info",
"action.confirm": "Confirm", "action.confirm": "Confirm",
@ -233,6 +234,8 @@
"initial.save.failed": "Initial Pentest could not be setup", "initial.save.failed": "Initial Pentest could not be setup",
"save.success": "Pentest saved successfully", "save.success": "Pentest saved successfully",
"save.failed": "Pentest could not be saved", "save.failed": "Pentest could not be saved",
"complete.success": "Pentest completed successfully",
"complete.failed": "Pentest could not be completed",
"update.success": "Pentest updated successfully", "update.success": "Pentest updated successfully",
"update.failed": "Pentest could not be updated", "update.failed": "Pentest could not be updated",
"delete.success": "Pentest deleted successfully", "delete.success": "Pentest deleted successfully",

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -133,8 +133,7 @@ export const createSpyObj = (baseName, methodNames): { [key: string]: Mock<any>
export const mockComment: Comment = { export const mockComment: Comment = {
id: '11-22-33', id: '11-22-33',
title: 'Test Finding', title: 'Test Finding',
description: 'Test Description', description: 'Test Description'
relatedFindings: ['68c47c56-3bcd-45f1-a05b-c197dbd33224']
}; };
export const mockedCommentDialogData = { export const mockedCommentDialogData = {
@ -164,19 +163,6 @@ export const mockedCommentDialogData = {
errors: [ errors: [
{errorCode: 'required', translationKey: 'comment.validationMessage.descriptionRequired'} {errorCode: 'required', translationKey: 'comment.validationMessage.descriptionRequired'}
] ]
},
commentRelatedFindings: {
fieldName: 'commentRelatedFindings',
type: 'text',
labelKey: 'comment.relatedFindings.label',
placeholder: 'comment.relatedFindingsPlaceholder',
controlsConfig: [
{value: mockComment ? mockComment.relatedFindings : [], disabled: false},
[]
],
errors: [
{errorCode: 'required', translationKey: 'finding.validationMessage.relatedFindings'}
]
} }
}, },
options: [ options: [

View File

@ -2,7 +2,7 @@ import {CommentDialogService} from '@shared/modules/comment-dialog/service/comme
import {ComponentType} from '@angular/cdk/overlay'; import {ComponentType} from '@angular/cdk/overlay';
import {NbDialogConfig} from '@nebular/theme'; import {NbDialogConfig} from '@nebular/theme';
import {Observable, of} from 'rxjs'; import {Observable, of} from 'rxjs';
import {Comment, RelatedFindingOption} from '@shared/models/comment.model'; import {Comment} from '@shared/models/comment.model';
export class CommentDialogServiceMock implements Required<CommentDialogService> { export class CommentDialogServiceMock implements Required<CommentDialogService> {
@ -11,7 +11,6 @@ export class CommentDialogServiceMock implements Required<CommentDialogService>
openCommentDialog( openCommentDialog(
componentOrTemplateRef: ComponentType<any>, componentOrTemplateRef: ComponentType<any>,
findingIds: [], findingIds: [],
relatedFindings: RelatedFindingOption[],
comment: Comment | undefined, comment: Comment | undefined,
config: Partial<NbDialogConfig<Partial<any> | string>> | undefined): Observable<any> { config: Partial<NbDialogConfig<Partial<any> | string>> | undefined): Observable<any> {
return of(undefined); return of(undefined);

View File

@ -18,7 +18,7 @@ export class TimerDurationPipe implements PipeTransform {
let seconds: string | number = 0; let seconds: string | number = 0;
if (time) { if (time) {
// tslint:disable-next-line:variable-name // tslint:disable-next-line:variable-name
const sec_num = parseInt(time, 10); // don't forget the second param const sec_num = parseInt(time, 10);
hours = Math.floor(sec_num / 3600); hours = Math.floor(sec_num / 3600);
minutes = Math.floor((sec_num - (hours * 3600)) / 60); minutes = Math.floor((sec_num - (hours * 3600)) / 60);
seconds = sec_num - (hours * 3600) - (minutes * 60); seconds = sec_num - (hours * 3600) - (minutes * 60);

View File

@ -81,9 +81,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING) PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING)
.description("The title of the requested comment"), .description("The title of the requested comment"),
PayloadDocumentation.fieldWithPath("[].description").type(JsonFieldType.STRING) PayloadDocumentation.fieldWithPath("[].description").type(JsonFieldType.STRING)
.description("The description number of the comment"), .description("The description number of the comment")
PayloadDocumentation.fieldWithPath("[].relatedFindings").type(JsonFieldType.ARRAY)
.description("List of related Findings of the comment")
) )
) )
) )
@ -93,7 +91,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
id = "ab62d365-1b1d-4da1-89bc-5496616e220f", id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
title = "Found Bug", title = "Found Bug",
description = "OTG-INFO-002 Bug", description = "OTG-INFO-002 Bug",
relatedFindings = emptyList() attachments = emptyList()
) )
private fun getCommentsResponse() = listOf( private fun getCommentsResponse() = listOf(
@ -133,9 +131,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING) PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
.description("The title of the requested comment"), .description("The title of the requested comment"),
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING) PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
.description("The description number of the comment"), .description("The description number of the comment")
PayloadDocumentation.fieldWithPath("relatedFindings").type(JsonFieldType.ARRAY)
.description("List of related findings of the comment")
) )
) )
) )
@ -145,7 +141,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
id = "ab62d365-1b1d-4da1-89bc-5496616e220f", id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
title = "Found Bug", title = "Found Bug",
description = "OTG-INFO-002 Bug", description = "OTG-INFO-002 Bug",
relatedFindings = emptyList() attachments = emptyList()
) )
} }
@ -182,9 +178,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING) PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
.description("The title of the comment"), .description("The title of the comment"),
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING) PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
.description("The description of the comment"), .description("The description of the comment")
PayloadDocumentation.fieldWithPath("relatedFindings").type(JsonFieldType.ARRAY)
.description("List of related findings of the comment")
) )
) )
) )
@ -192,8 +186,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
private val commentBody = CommentRequestBody( private val commentBody = CommentRequestBody(
title = "Found another Bug", title = "Found another Bug",
description = "Another OTG-INFO-002 Bug", description = "Another OTG-INFO-002 Bug"
relatedFindings = emptyList()
) )
} }
@ -230,9 +223,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING) PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
.description("The title of the requested comment"), .description("The title of the requested comment"),
PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING) PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING)
.description("The description number of the comment"), .description("The description number of the comment")
PayloadDocumentation.fieldWithPath("relatedFindings").type(JsonFieldType.ARRAY)
.description("List of related findings of the comment")
) )
) )
) )
@ -240,8 +231,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
private val commentBody = CommentRequestBody( private val commentBody = CommentRequestBody(
title = "Updated Comment", title = "Updated Comment",
description = "Updated Description", description = "Updated Description"
relatedFindings = emptyList()
) )
} }
@ -330,7 +320,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() {
id = "ab62d365-1b1d-4da1-89bc-5496616e220f", id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
title = "Found Bug", title = "Found Bug",
description = "OTG-INFO-002 Bug", description = "OTG-INFO-002 Bug",
relatedFindings = emptyList() attachments = emptyList()
) )
// persist test data in database // persist test data in database
mongoTemplate.save(ProjectEntity(projectOne)) mongoTemplate.save(ProjectEntity(projectOne))

View File

@ -77,7 +77,7 @@ class CommentControllerIntTest : BaseIntTest() {
id = "ab62d365-1b1d-4da1-89bc-5496616e220f", id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
title = "Found Bug", title = "Found Bug",
description = "OTG-INFO-002 Bug", description = "OTG-INFO-002 Bug",
relatedFindings = emptyList() attachments = emptyList()
) )
private fun getComments() = listOf( private fun getComments() = listOf(
@ -103,7 +103,7 @@ class CommentControllerIntTest : BaseIntTest() {
id = "ab62d365-1b1d-4da1-89bc-5496616e220f", id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
title = "Found Bug", title = "Found Bug",
description = "OTG-INFO-002 Bug", description = "OTG-INFO-002 Bug",
relatedFindings = emptyList() attachments = emptyList()
) )
} }
@ -122,13 +122,11 @@ class CommentControllerIntTest : BaseIntTest() {
.expectBody() .expectBody()
.jsonPath("$.title").isEqualTo("Found another Bug") .jsonPath("$.title").isEqualTo("Found another Bug")
.jsonPath("$.description").isEqualTo("Another OTG-INFO-002 Bug") .jsonPath("$.description").isEqualTo("Another OTG-INFO-002 Bug")
.jsonPath("$.relatedFindings").isEmpty
} }
private val commentBody = CommentRequestBody( private val commentBody = CommentRequestBody(
title = "Found another Bug", title = "Found another Bug",
description = "Another OTG-INFO-002 Bug", description = "Another OTG-INFO-002 Bug"
relatedFindings = emptyList()
) )
} }
@ -147,13 +145,11 @@ class CommentControllerIntTest : BaseIntTest() {
.expectBody() .expectBody()
.jsonPath("$.title").isEqualTo("Updated Comment") .jsonPath("$.title").isEqualTo("Updated Comment")
.jsonPath("$.description").isEqualTo("Updated Description") .jsonPath("$.description").isEqualTo("Updated Description")
.jsonPath("$.relatedFindings").isEmpty
} }
private val commentBody = CommentRequestBody( private val commentBody = CommentRequestBody(
title = "Updated Comment", title = "Updated Comment",
description = "Updated Description", description = "Updated Description"
relatedFindings = emptyList()
) )
} }
@ -221,7 +217,7 @@ class CommentControllerIntTest : BaseIntTest() {
id = "ab62d365-1b1d-4da1-89bc-5496616e220f", id = "ab62d365-1b1d-4da1-89bc-5496616e220f",
title = "Found Bug", title = "Found Bug",
description = "OTG-INFO-002 Bug", description = "OTG-INFO-002 Bug",
relatedFindings = emptyList() attachments = emptyList()
) )
// persist test data in database // persist test data in database
mongoTemplate.save(ProjectEntity(projectOne)) mongoTemplate.save(ProjectEntity(projectOne))