From a37e06f8caf16508a6467a1c060e6f0f2d1985c2 Mon Sep 17 00:00:00 2001 From: Marcel Haag Date: Wed, 8 Mar 2023 11:28:16 +0100 Subject: [PATCH] feat: As a user I want to see my profile in the header and log myself out manually --- security-c4po-angular/src/app/app.module.ts | 3 +- .../src/app/common-app.module.ts | 3 +- .../src/app/header/header.component.html | 33 ++++--- .../src/app/header/header.component.spec.ts | 31 ++++++- .../src/app/header/header.component.ts | 85 ++++++++++++++++-- .../src/app/header/header.module.ts | 33 ++++--- .../src/app/login/login.component.spec.ts | 1 - .../objective-categories.component.html | 2 +- .../objective-categories.component.ts | 6 +- .../objective-overview.module.ts | 5 +- .../pentest-header.component.html | 2 +- .../pentest-header.component.ts | 6 +- .../src/assets/i18n/de-DE.json | 3 + .../src/assets/i18n/en-US.json | 3 + .../src/assets/images/demo/anon-user-icon.png | Bin 0 -> 14142 bytes .../comment-dialog.component.spec.ts | 16 +--- .../service/comment-dialog.service.mock.ts | 3 +- .../src/shared/pipes/timer-duration.pipe.ts | 2 +- .../CommentControllerDocumentationTest.kt | 28 ++---- .../comment/CommentControllerIntTest.kt | 14 ++- 20 files changed, 186 insertions(+), 93 deletions(-) create mode 100644 security-c4po-angular/src/assets/images/demo/anon-user-icon.png diff --git a/security-c4po-angular/src/app/app.module.ts b/security-c4po-angular/src/app/app.module.ts index 3d73de7..bc93689 100644 --- a/security-c4po-angular/src/app/app.module.ts +++ b/security-c4po-angular/src/app/app.module.ts @@ -12,7 +12,7 @@ import { NbSelectModule, NbThemeModule, NbOverlayContainerAdapter, - NbDialogModule, + NbDialogModule, NbMenuModule, } from '@nebular/theme'; import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; import {HttpClient, HttpClientModule} from '@angular/common/http'; @@ -58,6 +58,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms'; FontAwesomeModule, BrowserAnimationsModule, ThemeModule.forRoot(), + NbMenuModule.forRoot(), NbSelectModule, NgxsModule.forRoot([SessionState, ProjectState], {developmentMode: !environment.production}), NgxsLoggerPluginModule.forRoot({developmentMode: !environment.production}), diff --git a/security-c4po-angular/src/app/common-app.module.ts b/security-c4po-angular/src/app/common-app.module.ts index f9b32a8..6415f6c 100644 --- a/security-c4po-angular/src/app/common-app.module.ts +++ b/security-c4po-angular/src/app/common-app.module.ts @@ -7,7 +7,7 @@ import {FontAwesomeModule} from '@fortawesome/angular-fontawesome'; import {FlexLayoutModule, FlexModule} from '@angular/flex-layout'; import {MomentModule} from 'ngx-moment'; 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 {LoadingSpinnerComponent} from '@shared/widgets/loading-spinner/loading-spinner.component'; @@ -26,6 +26,7 @@ export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader { FontAwesomeModule, FlexLayoutModule, ThemeModule.forRoot(), + NbMenuModule.forRoot(), FlexModule, HttpClientModule, TranslateModule.forChild({ diff --git a/security-c4po-angular/src/app/header/header.component.html b/security-c4po-angular/src/app/header/header.component.html index 2b64594..27306f8 100644 --- a/security-c4po-angular/src/app/header/header.component.html +++ b/security-c4po-angular/src/app/header/header.component.html @@ -11,6 +11,7 @@
+ - + + +
+ + + + {{'languageKeys.' + language | translate}} + + +
+
+ + + + + +
-
- - - - {{'languageKeys.' + language | translate}} - - -
diff --git a/security-c4po-angular/src/app/header/header.component.spec.ts b/security-c4po-angular/src/app/header/header.component.spec.ts index cf48777..02544a2 100644 --- a/security-c4po-angular/src/app/header/header.component.spec.ts +++ b/security-c4po-angular/src/app/header/header.component.spec.ts @@ -3,17 +3,30 @@ import {ComponentFixture, TestBed} from '@angular/core/testing'; import {HeaderComponent} from './header.component'; import {CommonModule} from '@angular/common'; 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 {TranslateLoader, TranslateModule} from '@ngx-translate/core'; import {HttpLoaderFactory} from '../common-app.module'; import {HttpClient} from '@angular/common/http'; import {RouterTestingModule} from '@angular/router/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', () => { let component: HeaderComponent; let fixture: ComponentFixture; + let store: Store; beforeEach(async () => { await TestBed.configureTestingModule({ @@ -26,6 +39,7 @@ describe('HeaderComponent', () => { NbSelectModule, FontAwesomeTestingModule, HttpClientTestingModule, + NbMenuModule, ThemeModule.forRoot(), TranslateModule.forRoot({ loader: { @@ -34,14 +48,23 @@ describe('HeaderComponent', () => { deps: [HttpClient] } }), - RouterTestingModule.withRoutes([]) + RouterTestingModule.withRoutes([]), + NgxsModule.forRoot([SessionState]) + ], + providers: [ + NbMenuService, + KeycloakService ] - }) - .compileComponents(); + }).compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(HeaderComponent); + store = TestBed.inject(Store); + store.reset({ + ...store.snapshot(), + [SESSION_STATE_NAME]: DESIRED_STORE_STATE_SESSION + }); component = fixture.componentInstance; fixture.detectChanges(); }); diff --git a/security-c4po-angular/src/app/header/header.component.ts b/security-c4po-angular/src/app/header/header.component.ts index 23ece2a..bd5ccd6 100644 --- a/security-c4po-angular/src/app/header/header.component.ts +++ b/security-c4po-angular/src/app/header/header.component.ts @@ -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 {NbThemeService} from '@nebular/theme'; +import {NbMenuItem, NbMenuService, NbThemeService} from '@nebular/theme'; import {map} from 'rxjs/operators'; import {GlobalTitlesVariables} from '@shared/config/global-variables'; import {TranslateService} from '@ngx-translate/core'; 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({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.scss'] }) -export class HeaderComponent implements OnInit{ +@UntilDestroy() +export class HeaderComponent implements OnInit { + // HTML only readonly fa = FA; readonly SECURITYC4PO_TITLE: string = GlobalTitlesVariables.SECURITYC4PO_TITLE; @@ -21,16 +31,59 @@ export class HeaderComponent implements OnInit{ languages = ['en-US', 'de-DE']; selectedLanguage = ''; - constructor(private themeService: NbThemeService, private translateService: TranslateService) { } + // User Menu Properties + userPictureOnly = false; + user: BehaviorSubject = new BehaviorSubject(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 { + // Handle theme selection this.themeService.onThemeChange() .pipe( - map(({ name }) => name), + map(({name}) => name), untilDestroyed(this), - ) - .subscribe(themeName => this.currentTheme = themeName); + ).subscribe(themeName => this.currentTheme = themeName); + 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 @@ -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 { this.translateService.use(language); } diff --git a/security-c4po-angular/src/app/header/header.module.ts b/security-c4po-angular/src/app/header/header.module.ts index 99eb429..a310865 100644 --- a/security-c4po-angular/src/app/header/header.module.ts +++ b/security-c4po-angular/src/app/header/header.module.ts @@ -1,7 +1,14 @@ import {NgModule} from '@angular/core'; import {CommonModule} from '@angular/common'; 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 {FlexLayoutModule} from '@angular/flex-layout'; import {TranslateModule} from '@ngx-translate/core'; @@ -13,16 +20,20 @@ import {TranslateModule} from '@ngx-translate/core'; exports: [ HeaderComponent ], - imports: [ - CommonModule, - NbButtonModule, - FontAwesomeModule, - NbCardModule, - NbActionsModule, - FlexLayoutModule, - NbSelectModule, - TranslateModule - ] + imports: [ + CommonModule, + NbButtonModule, + FontAwesomeModule, + NbCardModule, + NbActionsModule, + FlexLayoutModule, + NbSelectModule, + TranslateModule, + NbUserModule, + NbContextMenuModule + ], + providers: [ + ] }) export class HeaderModule { } diff --git a/security-c4po-angular/src/app/login/login.component.spec.ts b/security-c4po-angular/src/app/login/login.component.spec.ts index 121a25d..fd2ee06 100644 --- a/security-c4po-angular/src/app/login/login.component.spec.ts +++ b/security-c4po-angular/src/app/login/login.component.spec.ts @@ -81,7 +81,6 @@ describe('LoginComponent', () => { ...store.snapshot(), [SESSION_STATE_NAME]: DESIRED_STORE_STATE_SESSION }); - fixture = TestBed.createComponent(LoginComponent); component = fixture.componentInstance; httpMock = TestBed.inject(HttpTestingController); diff --git a/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.html b/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.html index 66c9acb..6c51964 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.html +++ b/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.html @@ -1,5 +1,5 @@
- +
diff --git a/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.ts b/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.ts index 6089dbb..6350c31 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.ts +++ b/security-c4po-angular/src/app/objective-overview/objective-categories/objective-categories.component.ts @@ -54,10 +54,10 @@ export class ObjectiveCategoriesComponent implements OnInit, OnDestroy { category.selected = false; }); menuBag.item.selected = true; - this.store.dispatch(new ChangeCategory(this.selectedCategory)); + if (this.selectedCategory) { + this.store.dispatch(new ChangeCategory(this.selectedCategory)); + } }); - - } private initTranslation(): void { diff --git a/security-c4po-angular/src/app/objective-overview/objective-overview.module.ts b/security-c4po-angular/src/app/objective-overview/objective-overview.module.ts index 84946c4..1528af1 100644 --- a/security-c4po-angular/src/app/objective-overview/objective-overview.module.ts +++ b/security-c4po-angular/src/app/objective-overview/objective-overview.module.ts @@ -37,7 +37,6 @@ import {CommentWidgetModule} from '@shared/widgets/comment-widget/comment-widget CommonAppModule, NbLayoutModule, NbCardModule, - NbMenuModule.forRoot(), NbButtonModule, // nbTooltip crashes app right now if used in component, // workaround: use title in html for now @@ -46,7 +45,6 @@ import {CommentWidgetModule} from '@shared/widgets/comment-widget/comment-widget TranslateModule, StatusTagModule, RouterModule, - NbMenuModule, FormsModule, NbListModule, FontAwesomeModule, @@ -57,7 +55,8 @@ import {CommentWidgetModule} from '@shared/widgets/comment-widget/comment-widget ObjectiveOverviewRoutingModule, // Table Widgets FindigWidgetModule, - CommentWidgetModule + CommentWidgetModule, + NbMenuModule ], exports: [ ObjectiveHeaderComponent, diff --git a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.html b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.html index 128d54c..fa5dd94 100644 --- a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.html +++ b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.html @@ -25,7 +25,7 @@ status="success" [disabled]="!pentestStatusChanged() || !pentestHasFindingsOrComments()" title="{{ 'global.action.save' | translate }}" - (click)="onClickCompletePentestAndRouteBack()"> + (click)="onClickCompletePentest()"> {{ 'global.action.complete' | translate }} diff --git a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.ts b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.ts index 3841a15..d1e4a15 100644 --- a/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.ts +++ b/security-c4po-angular/src/app/pentest/pentest-header/pentest-header.component.ts @@ -95,7 +95,7 @@ export class PentestHeaderComponent implements OnInit, OnDestroy { ).finally(); } - onClickCompletePentestAndRouteBack(): void { + onClickCompletePentest(): void { // Update existing Pentest this.pentest$.next({...this.pentest$.getValue(), status: PentestStatus.COMPLETED, timeSpent: this.currentTimeSpent}); this.updatePentest(); @@ -107,11 +107,11 @@ export class PentestHeaderComponent implements OnInit, OnDestroy { next: (pentest: Pentest) => { this.store.dispatch(new ChangePentest(pentest)); this.initialTimeSpent = pentest.timeSpent; - this.notificationService.showPopup('pentest.popup.update.success', PopupType.SUCCESS); + this.notificationService.showPopup('pentest.popup.complete.success', PopupType.SUCCESS); }, error: err => { console.log(err); - this.notificationService.showPopup('pentest.popup.update.failed', PopupType.FAILURE); + this.notificationService.showPopup('pentest.popup.complete.failed', PopupType.FAILURE); } }); } diff --git a/security-c4po-angular/src/assets/i18n/de-DE.json b/security-c4po-angular/src/assets/i18n/de-DE.json index ecaf71c..161496a 100644 --- a/security-c4po-angular/src/assets/i18n/de-DE.json +++ b/security-c4po-angular/src/assets/i18n/de-DE.json @@ -1,6 +1,7 @@ { "global": { "action.login": "Einloggen", + "action.logout": "Ausloggen", "action.retry": "Erneut Versuchen", "action.info": "Info", "action.save": "Speichern", @@ -233,6 +234,8 @@ "initial.save.failed": "Initialer Pentest konnte nicht aufgesetzt werden", "save.success": "Pentest erfolgreich gespeichert", "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.failed": "Pentest konnte nicht aktualisiert werden", "delete.success": "Pentest erfolgreich gelöscht", diff --git a/security-c4po-angular/src/assets/i18n/en-US.json b/security-c4po-angular/src/assets/i18n/en-US.json index afd7e66..a77c870 100644 --- a/security-c4po-angular/src/assets/i18n/en-US.json +++ b/security-c4po-angular/src/assets/i18n/en-US.json @@ -1,6 +1,7 @@ { "global": { "action.login": "Login", + "action.logout": "Logout", "action.retry": "Try again", "action.info": "Info", "action.confirm": "Confirm", @@ -233,6 +234,8 @@ "initial.save.failed": "Initial Pentest could not be setup", "save.success": "Pentest saved successfully", "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.failed": "Pentest could not be updated", "delete.success": "Pentest deleted successfully", diff --git a/security-c4po-angular/src/assets/images/demo/anon-user-icon.png b/security-c4po-angular/src/assets/images/demo/anon-user-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..991d63b1c87343f51c5b0433b8028fa09c9e6a6f GIT binary patch literal 14142 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 literal 0 HcmV?d00001 diff --git a/security-c4po-angular/src/shared/modules/comment-dialog/comment-dialog.component.spec.ts b/security-c4po-angular/src/shared/modules/comment-dialog/comment-dialog.component.spec.ts index 0b919bb..b185913 100644 --- a/security-c4po-angular/src/shared/modules/comment-dialog/comment-dialog.component.spec.ts +++ b/security-c4po-angular/src/shared/modules/comment-dialog/comment-dialog.component.spec.ts @@ -133,8 +133,7 @@ export const createSpyObj = (baseName, methodNames): { [key: string]: Mock export const mockComment: Comment = { id: '11-22-33', title: 'Test Finding', - description: 'Test Description', - relatedFindings: ['68c47c56-3bcd-45f1-a05b-c197dbd33224'] + description: 'Test Description' }; export const mockedCommentDialogData = { @@ -164,19 +163,6 @@ export const mockedCommentDialogData = { errors: [ {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: [ diff --git a/security-c4po-angular/src/shared/modules/comment-dialog/service/comment-dialog.service.mock.ts b/security-c4po-angular/src/shared/modules/comment-dialog/service/comment-dialog.service.mock.ts index aa90573..853f3ec 100644 --- a/security-c4po-angular/src/shared/modules/comment-dialog/service/comment-dialog.service.mock.ts +++ b/security-c4po-angular/src/shared/modules/comment-dialog/service/comment-dialog.service.mock.ts @@ -2,7 +2,7 @@ import {CommentDialogService} from '@shared/modules/comment-dialog/service/comme import {ComponentType} from '@angular/cdk/overlay'; import {NbDialogConfig} from '@nebular/theme'; 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 { @@ -11,7 +11,6 @@ export class CommentDialogServiceMock implements Required openCommentDialog( componentOrTemplateRef: ComponentType, findingIds: [], - relatedFindings: RelatedFindingOption[], comment: Comment | undefined, config: Partial | string>> | undefined): Observable { return of(undefined); diff --git a/security-c4po-angular/src/shared/pipes/timer-duration.pipe.ts b/security-c4po-angular/src/shared/pipes/timer-duration.pipe.ts index 991567d..45d3363 100644 --- a/security-c4po-angular/src/shared/pipes/timer-duration.pipe.ts +++ b/security-c4po-angular/src/shared/pipes/timer-duration.pipe.ts @@ -18,7 +18,7 @@ export class TimerDurationPipe implements PipeTransform { let seconds: string | number = 0; if (time) { // 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); minutes = Math.floor((sec_num - (hours * 3600)) / 60); seconds = sec_num - (hours * 3600) - (minutes * 60); diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerDocumentationTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerDocumentationTest.kt index d76f4df..13be660 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerDocumentationTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerDocumentationTest.kt @@ -81,9 +81,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING) .description("The title of the requested comment"), PayloadDocumentation.fieldWithPath("[].description").type(JsonFieldType.STRING) - .description("The description number of the comment"), - PayloadDocumentation.fieldWithPath("[].relatedFindings").type(JsonFieldType.ARRAY) - .description("List of related Findings of the comment") + .description("The description number of the comment") ) ) ) @@ -93,7 +91,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { id = "ab62d365-1b1d-4da1-89bc-5496616e220f", title = "Found Bug", description = "OTG-INFO-002 Bug", - relatedFindings = emptyList() + attachments = emptyList() ) private fun getCommentsResponse() = listOf( @@ -133,9 +131,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING) .description("The title of the requested comment"), PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING) - .description("The description number of the comment"), - PayloadDocumentation.fieldWithPath("relatedFindings").type(JsonFieldType.ARRAY) - .description("List of related findings of the comment") + .description("The description number of the comment") ) ) ) @@ -145,7 +141,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { id = "ab62d365-1b1d-4da1-89bc-5496616e220f", title = "Found Bug", description = "OTG-INFO-002 Bug", - relatedFindings = emptyList() + attachments = emptyList() ) } @@ -182,9 +178,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING) .description("The title of the comment"), PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING) - .description("The description of the comment"), - PayloadDocumentation.fieldWithPath("relatedFindings").type(JsonFieldType.ARRAY) - .description("List of related findings of the comment") + .description("The description of the comment") ) ) ) @@ -192,8 +186,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { private val commentBody = CommentRequestBody( title = "Found another Bug", - description = "Another OTG-INFO-002 Bug", - relatedFindings = emptyList() + description = "Another OTG-INFO-002 Bug" ) } @@ -230,9 +223,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING) .description("The title of the requested comment"), PayloadDocumentation.fieldWithPath("description").type(JsonFieldType.STRING) - .description("The description number of the comment"), - PayloadDocumentation.fieldWithPath("relatedFindings").type(JsonFieldType.ARRAY) - .description("List of related findings of the comment") + .description("The description number of the comment") ) ) ) @@ -240,8 +231,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { private val commentBody = CommentRequestBody( title = "Updated Comment", - description = "Updated Description", - relatedFindings = emptyList() + description = "Updated Description" ) } @@ -330,7 +320,7 @@ class CommentControllerDocumentationTest : BaseDocumentationIntTest() { id = "ab62d365-1b1d-4da1-89bc-5496616e220f", title = "Found Bug", description = "OTG-INFO-002 Bug", - relatedFindings = emptyList() + attachments = emptyList() ) // persist test data in database mongoTemplate.save(ProjectEntity(projectOne)) diff --git a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerIntTest.kt b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerIntTest.kt index 0d64ffe..2cbd1f2 100644 --- a/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerIntTest.kt +++ b/security-c4po-api/src/test/kotlin/com/securityc4po/api/pentest/comment/CommentControllerIntTest.kt @@ -77,7 +77,7 @@ class CommentControllerIntTest : BaseIntTest() { id = "ab62d365-1b1d-4da1-89bc-5496616e220f", title = "Found Bug", description = "OTG-INFO-002 Bug", - relatedFindings = emptyList() + attachments = emptyList() ) private fun getComments() = listOf( @@ -103,7 +103,7 @@ class CommentControllerIntTest : BaseIntTest() { id = "ab62d365-1b1d-4da1-89bc-5496616e220f", title = "Found Bug", description = "OTG-INFO-002 Bug", - relatedFindings = emptyList() + attachments = emptyList() ) } @@ -122,13 +122,11 @@ class CommentControllerIntTest : BaseIntTest() { .expectBody() .jsonPath("$.title").isEqualTo("Found another Bug") .jsonPath("$.description").isEqualTo("Another OTG-INFO-002 Bug") - .jsonPath("$.relatedFindings").isEmpty } private val commentBody = CommentRequestBody( title = "Found another Bug", - description = "Another OTG-INFO-002 Bug", - relatedFindings = emptyList() + description = "Another OTG-INFO-002 Bug" ) } @@ -147,13 +145,11 @@ class CommentControllerIntTest : BaseIntTest() { .expectBody() .jsonPath("$.title").isEqualTo("Updated Comment") .jsonPath("$.description").isEqualTo("Updated Description") - .jsonPath("$.relatedFindings").isEmpty } private val commentBody = CommentRequestBody( title = "Updated Comment", - description = "Updated Description", - relatedFindings = emptyList() + description = "Updated Description" ) } @@ -221,7 +217,7 @@ class CommentControllerIntTest : BaseIntTest() { id = "ab62d365-1b1d-4da1-89bc-5496616e220f", title = "Found Bug", description = "OTG-INFO-002 Bug", - relatedFindings = emptyList() + attachments = emptyList() ) // persist test data in database mongoTemplate.save(ProjectEntity(projectOne))