feat: added dialog-service and new project dialog
This commit is contained in:
parent
a54f064692
commit
8dc287fc8a
|
@ -6,7 +6,7 @@ import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {
|
import {
|
||||||
NbLayoutModule,
|
NbLayoutModule,
|
||||||
NbToastrModule,
|
NbToastrModule,
|
||||||
NbIconModule, NbCardModule, NbButtonModule,
|
NbIconModule, NbCardModule, NbButtonModule, NbDialogService, NbDialogModule,
|
||||||
} from '@nebular/theme';
|
} from '@nebular/theme';
|
||||||
import {NbEvaIconsModule} from '@nebular/eva-icons';
|
import {NbEvaIconsModule} from '@nebular/eva-icons';
|
||||||
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
|
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
|
||||||
|
@ -26,6 +26,7 @@ import {HomeModule} from './home/home.module';
|
||||||
import {KeycloakService} from 'keycloak-angular';
|
import {KeycloakService} from 'keycloak-angular';
|
||||||
import {httpInterceptorProviders} from '@shared/interceptors';
|
import {httpInterceptorProviders} from '@shared/interceptors';
|
||||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||||
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -53,6 +54,7 @@ import {FlexLayoutModule} from '@angular/flex-layout';
|
||||||
deps: [HttpClient]
|
deps: [HttpClient]
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
NbDialogModule.forRoot(),
|
||||||
HeaderModule,
|
HeaderModule,
|
||||||
HomeModule,
|
HomeModule,
|
||||||
FlexLayoutModule
|
FlexLayoutModule
|
||||||
|
@ -67,7 +69,9 @@ import {FlexLayoutModule} from '@angular/flex-layout';
|
||||||
},
|
},
|
||||||
KeycloakService,
|
KeycloakService,
|
||||||
httpInterceptorProviders,
|
httpInterceptorProviders,
|
||||||
NotificationService
|
NotificationService,
|
||||||
|
NbDialogService,
|
||||||
|
DialogService
|
||||||
],
|
],
|
||||||
bootstrap: [
|
bootstrap: [
|
||||||
AppComponent
|
AppComponent
|
||||||
|
|
|
@ -48,7 +48,6 @@ describe('LoginComponent', () => {
|
||||||
BrowserAnimationsModule,
|
BrowserAnimationsModule,
|
||||||
ReactiveFormsModule,
|
ReactiveFormsModule,
|
||||||
NbInputModule,
|
NbInputModule,
|
||||||
NbCardModule,
|
|
||||||
NbButtonModule,
|
NbButtonModule,
|
||||||
NbLayoutModule,
|
NbLayoutModule,
|
||||||
ThemeModule.forRoot(),
|
ThemeModule.forRoot(),
|
||||||
|
|
|
@ -22,6 +22,8 @@ import {ProjectServiceMock} from '@shared/services/project.service.mock';
|
||||||
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';
|
||||||
import {KeycloakService} from 'keycloak-angular';
|
import {KeycloakService} from 'keycloak-angular';
|
||||||
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock';
|
||||||
|
|
||||||
describe('ProjectOverviewComponent', () => {
|
describe('ProjectOverviewComponent', () => {
|
||||||
let component: ProjectOverviewComponent;
|
let component: ProjectOverviewComponent;
|
||||||
|
@ -61,6 +63,7 @@ describe('ProjectOverviewComponent', () => {
|
||||||
providers: [
|
providers: [
|
||||||
KeycloakService,
|
KeycloakService,
|
||||||
{provide: ProjectService, useValue: new ProjectServiceMock()},
|
{provide: ProjectService, useValue: new ProjectServiceMock()},
|
||||||
|
{provide: DialogService, useClass: DialogServiceMock},
|
||||||
{provide: NotificationService, useValue: new NotificationServiceMock()}
|
{provide: NotificationService, useValue: new NotificationServiceMock()}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
import * as FA from '@fortawesome/free-solid-svg-icons';
|
import * as FA from '@fortawesome/free-solid-svg-icons';
|
||||||
import {Project} from '@shared/models/project.model';
|
import {Project, SaveProjectDialogBody} from '@shared/models/project.model';
|
||||||
import {BehaviorSubject, Observable} from 'rxjs';
|
import {BehaviorSubject, Observable} from 'rxjs';
|
||||||
import {untilDestroyed} from 'ngx-take-until-destroy';
|
import {untilDestroyed} from 'ngx-take-until-destroy';
|
||||||
import {ProjectService} from '@shared/services/project.service';
|
import {ProjectService} from '@shared/services/project.service';
|
||||||
import {NotificationService, PopupType} from '@shared/services/notification.service';
|
import {NotificationService, PopupType} from '@shared/services/notification.service';
|
||||||
import {tap} from 'rxjs/operators';
|
import {filter, mergeMap, tap} from 'rxjs/operators';
|
||||||
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
import {ProjectDialogComponent} from '@shared/modules/project-dialog/project-dialog.component';
|
||||||
|
import {NB_DIALOG_CONFIG} from '@nebular/theme/components/dialog/dialog-config';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-project-overview',
|
selector: 'app-project-overview',
|
||||||
|
@ -21,20 +24,21 @@ export class ProjectOverviewComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly projectService: ProjectService,
|
private readonly projectService: ProjectService,
|
||||||
|
private readonly dialogService: DialogService,
|
||||||
private readonly notificationService: NotificationService) {
|
private readonly notificationService: NotificationService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.getProjects();
|
this.loadProjects();
|
||||||
}
|
}
|
||||||
|
|
||||||
getProjects(): void {
|
loadProjects(): void {
|
||||||
this.projectService.getProjects()
|
this.projectService.getProjects()
|
||||||
.pipe(
|
.pipe(
|
||||||
untilDestroyed(this),
|
untilDestroyed(this),
|
||||||
tap(() => this.loading$.next(true))
|
tap(() => this.loading$.next(true))
|
||||||
)
|
)
|
||||||
.subscribe( {
|
.subscribe({
|
||||||
next: (projects) => {
|
next: (projects) => {
|
||||||
this.projects.next(projects);
|
this.projects.next(projects);
|
||||||
this.loading$.next(false);
|
this.loading$.next(false);
|
||||||
|
@ -48,7 +52,28 @@ export class ProjectOverviewComponent implements OnInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickAddProject(): void {
|
onClickAddProject(): void {
|
||||||
console.log('to be implemented...');
|
this.dialogService.openCustomDialog(
|
||||||
|
ProjectDialogComponent,
|
||||||
|
{
|
||||||
|
closeOnEsc: false,
|
||||||
|
hasScroll: false,
|
||||||
|
autoFocus: false,
|
||||||
|
closeOnBackdropClick: false
|
||||||
|
}
|
||||||
|
).onClose.pipe(
|
||||||
|
filter(value => !!value),
|
||||||
|
mergeMap((value: SaveProjectDialogBody) => this.projectService.saveProject(value)),
|
||||||
|
untilDestroyed(this)
|
||||||
|
).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.loadProjects();
|
||||||
|
this.notificationService.showPopup('project.popup.save.success', PopupType.SUCCESS);
|
||||||
|
},
|
||||||
|
error: error => {
|
||||||
|
console.error(error);
|
||||||
|
this.notificationService.showPopup('project.popup.save.failed', PopupType.FAILURE);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickEditProject(): void {
|
onClickEditProject(): void {
|
||||||
|
|
|
@ -2,12 +2,15 @@ import {NgModule} from '@angular/core';
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {ProjectOverviewComponent} from './project-overview.component';
|
import {ProjectOverviewComponent} from './project-overview.component';
|
||||||
import {ProjectOverviewRoutingModule} from './project-overview-routing.module';
|
import {ProjectOverviewRoutingModule} from './project-overview-routing.module';
|
||||||
import {NbButtonModule, NbCardModule, NbProgressBarModule} from '@nebular/theme';
|
import {NbButtonModule, NbCardModule, NbDialogService, NbProgressBarModule} from '@nebular/theme';
|
||||||
import {FlexLayoutModule} from '@angular/flex-layout';
|
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||||
import {TranslateModule} from '@ngx-translate/core';
|
import {TranslateModule} from '@ngx-translate/core';
|
||||||
import {DateTimeFormatPipe} from '@shared/pipes/date-time-format.pipe';
|
import {DateTimeFormatPipe} from '@shared/pipes/date-time-format.pipe';
|
||||||
import {ProjectModule} from './project';
|
import {ProjectModule} from './project';
|
||||||
|
import {ProjectDialogComponent} from '@shared/modules/project-dialog/project-dialog.component';
|
||||||
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
import {ProjectDialogModule} from '@shared/modules/project-dialog/project-dialog.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -23,7 +26,12 @@ import {ProjectModule} from './project';
|
||||||
FontAwesomeModule,
|
FontAwesomeModule,
|
||||||
TranslateModule,
|
TranslateModule,
|
||||||
NbProgressBarModule,
|
NbProgressBarModule,
|
||||||
ProjectModule
|
ProjectModule,
|
||||||
|
ProjectDialogModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
DialogService,
|
||||||
|
NbDialogService
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class ProjectOverviewModule {
|
export class ProjectOverviewModule {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
"global": {
|
"global": {
|
||||||
"action.login": "Einloggen",
|
"action.login": "Einloggen",
|
||||||
"action.retry": "Erneut Versuchen",
|
"action.retry": "Erneut Versuchen",
|
||||||
|
"action.save": "Speichern",
|
||||||
|
"action.cancel": "Abbrechen",
|
||||||
"username": "Nutzername",
|
"username": "Nutzername",
|
||||||
"password": "Passwort"
|
"password": "Passwort"
|
||||||
},
|
},
|
||||||
|
@ -28,9 +30,18 @@
|
||||||
"add.project": "Projekt hinzufügen",
|
"add.project": "Projekt hinzufügen",
|
||||||
"no.projects": "Keine Projekte verfügbar"
|
"no.projects": "Keine Projekte verfügbar"
|
||||||
},
|
},
|
||||||
"popup": {
|
"create": {
|
||||||
"not.found": "Keine Projekte gefunden"
|
"header": "Neues Projekt erstellen"
|
||||||
},
|
},
|
||||||
|
"popup": {
|
||||||
|
"not.found": "Keine Projekte gefunden",
|
||||||
|
"save.success": "Projekt erfolgreich gespeichert",
|
||||||
|
"save.failed": "Projekt konnte nicht gespeichert werden"
|
||||||
|
},
|
||||||
|
"title.label": "Projekt Titel",
|
||||||
|
"client.label": "Name des Auftraggebers",
|
||||||
|
"tester.label": "Name des Pentester",
|
||||||
|
"title": "Titel",
|
||||||
"client": "Klient",
|
"client": "Klient",
|
||||||
"tester": "Tester",
|
"tester": "Tester",
|
||||||
"createdAt": "Erstellt am"
|
"createdAt": "Erstellt am"
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
"global": {
|
"global": {
|
||||||
"action.login": "Login",
|
"action.login": "Login",
|
||||||
"action.retry": "Try again",
|
"action.retry": "Try again",
|
||||||
|
"action.save": "Save",
|
||||||
|
"action.cancel": "Cancel",
|
||||||
"username": "Username",
|
"username": "Username",
|
||||||
"password": "Password"
|
"password": "Password"
|
||||||
},
|
},
|
||||||
|
@ -28,9 +30,18 @@
|
||||||
"add.project": "Add project",
|
"add.project": "Add project",
|
||||||
"no.projects": "No projects available"
|
"no.projects": "No projects available"
|
||||||
},
|
},
|
||||||
"popup": {
|
"create": {
|
||||||
"not.found": "No projects found"
|
"header": "Create New Project"
|
||||||
},
|
},
|
||||||
|
"popup": {
|
||||||
|
"not.found": "No projects found",
|
||||||
|
"save.success": "Project saved successfully",
|
||||||
|
"save.failed": "Project could not be saved"
|
||||||
|
},
|
||||||
|
"title.label": "Project Title",
|
||||||
|
"client.label": "Name of Client",
|
||||||
|
"tester.label": "Name of Pentester",
|
||||||
|
"title": "Title",
|
||||||
"client": "Client",
|
"client": "Client",
|
||||||
"tester": "Tester",
|
"tester": "Tester",
|
||||||
"createdAt": "Created at"
|
"createdAt": "Created at"
|
||||||
|
|
|
@ -20,3 +20,9 @@ export class Project {
|
||||||
this.createdBy = createdBy;
|
this.createdBy = createdBy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SaveProjectDialogBody {
|
||||||
|
title: string;
|
||||||
|
client: string;
|
||||||
|
tester: string;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
<nb-card #dialog accent="primary" class="project-dialog">
|
||||||
|
<nb-card-header fxLayoutAlign="start center" class="project-dialog-header">
|
||||||
|
{{ 'project.create.header' | translate }}
|
||||||
|
</nb-card-header>
|
||||||
|
<nb-card-body>
|
||||||
|
<form [formGroup]="projectFormGroup" fxLayout="column" fxLayoutGap="1rem" fxLayoutAlign="start start">
|
||||||
|
<nb-form-field class="project-form-field">
|
||||||
|
<label for="projectTitleInput" class="label">
|
||||||
|
{{'project.title.label' | translate}}:
|
||||||
|
</label>
|
||||||
|
<input formControlName="projectTitle" required
|
||||||
|
id="projectTitleInput" nbInput
|
||||||
|
class="input" type="text" fullWidth
|
||||||
|
status="{{formCtrlStatus}}"
|
||||||
|
placeholder="{{'project.title' | translate}} *">
|
||||||
|
</nb-form-field>
|
||||||
|
|
||||||
|
<nb-form-field class="project-form-field">
|
||||||
|
<label for="projectClientInput" class="label">
|
||||||
|
{{'project.client.label' | translate}}:
|
||||||
|
</label>
|
||||||
|
<input formControlName="projectClient" required
|
||||||
|
id="projectClientInput" nbInput
|
||||||
|
class="input" type="text" fullWidth
|
||||||
|
status="{{formCtrlStatus}}"
|
||||||
|
placeholder="{{'project.client' | translate}} *">
|
||||||
|
</nb-form-field>
|
||||||
|
|
||||||
|
<nb-form-field class="project-form-field">
|
||||||
|
<label for="projectTesterInput" class="label">
|
||||||
|
{{'project.tester.label' | translate}}:
|
||||||
|
</label>
|
||||||
|
<input formControlName="projectTester" required
|
||||||
|
id="projectTesterInput" nbInput
|
||||||
|
class="input" type="text" fullWidth
|
||||||
|
status="{{formCtrlStatus}}"
|
||||||
|
placeholder="{{'project.tester' | translate}} *">
|
||||||
|
</nb-form-field>
|
||||||
|
</form>
|
||||||
|
</nb-card-body>
|
||||||
|
<nb-card-footer fxLayout="row" fxLayoutGap="1.5rem" fxLayoutAlign="end end">
|
||||||
|
<button nbButton status="success" [disabled]="formIsEmptyOrInvalid()" (click)="onClickSave(projectFormGroup.value)">
|
||||||
|
{{ 'global.action.save' | translate}}
|
||||||
|
</button>
|
||||||
|
<button nbButton status="danger" (click)="onClickClose()">
|
||||||
|
{{ 'global.action.cancel' | translate }}
|
||||||
|
</button>
|
||||||
|
</nb-card-footer>
|
||||||
|
</nb-card>
|
|
@ -0,0 +1,23 @@
|
||||||
|
.project-dialog {
|
||||||
|
width: 24rem;
|
||||||
|
height: 31rem;
|
||||||
|
|
||||||
|
.project-dialog-header {
|
||||||
|
height: 8vh;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
nb-form-field {
|
||||||
|
padding: 0.5rem 0 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input {
|
||||||
|
width: 15rem;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {ProjectDialogComponent} from './project-dialog.component';
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {NbButtonModule, NbCardModule, NbDialogRef, NbFormFieldModule, NbInputModule, NbLayoutModule} from '@nebular/theme';
|
||||||
|
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||||
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
|
import {ThemeModule} from '@assets/@theme/theme.module';
|
||||||
|
import {TranslateLoader, TranslateModule} from '@ngx-translate/core';
|
||||||
|
import {HttpLoaderFactory} from '../../../app/common-app.module';
|
||||||
|
import {HttpClient, HttpClientModule} from '@angular/common/http';
|
||||||
|
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||||
|
import {NotificationService} from '@shared/services/notification.service';
|
||||||
|
import {NotificationServiceMock} from '@shared/services/notification.service.mock';
|
||||||
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock';
|
||||||
|
import {ReactiveFormsModule} from '@angular/forms';
|
||||||
|
|
||||||
|
describe('ProjectDialogComponent', () => {
|
||||||
|
let component: ProjectDialogComponent;
|
||||||
|
let fixture: ComponentFixture<ProjectDialogComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
ProjectDialogComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
NbLayoutModule,
|
||||||
|
NbCardModule,
|
||||||
|
NbButtonModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
NbInputModule,
|
||||||
|
NbFormFieldModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
ThemeModule.forRoot(),
|
||||||
|
TranslateModule.forRoot({
|
||||||
|
loader: {
|
||||||
|
provide: TranslateLoader,
|
||||||
|
useFactory: HttpLoaderFactory,
|
||||||
|
deps: [HttpClient]
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
HttpClientModule,
|
||||||
|
HttpClientTestingModule
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{provide: NotificationService, useValue: new NotificationServiceMock()},
|
||||||
|
{provide: DialogService, useClass: DialogServiceMock},
|
||||||
|
{provide: NbDialogRef, useValue: {}}
|
||||||
|
]
|
||||||
|
}).compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(ProjectDialogComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,82 @@
|
||||||
|
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||||
|
import {NbDialogRef} from '@nebular/theme';
|
||||||
|
import {AbstractControl, FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||||
|
import {FieldStatus} from '@shared/models/form-field-status.model';
|
||||||
|
import {untilDestroyed} from 'ngx-take-until-destroy';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-project-dialog',
|
||||||
|
templateUrl: './project-dialog.component.html',
|
||||||
|
styleUrls: ['./project-dialog.component.scss']
|
||||||
|
})
|
||||||
|
export class ProjectDialogComponent implements OnInit, OnDestroy {
|
||||||
|
// form control elements
|
||||||
|
projectFormGroup: FormGroup;
|
||||||
|
projectTitleCtrl: AbstractControl;
|
||||||
|
projectClientCtrl: AbstractControl;
|
||||||
|
projectTesterCtrl: AbstractControl;
|
||||||
|
|
||||||
|
formCtrlStatus = FieldStatus.BASIC;
|
||||||
|
|
||||||
|
invalidProjectTitle: string;
|
||||||
|
invalidProjectClient: string;
|
||||||
|
invalidProjectTester: string;
|
||||||
|
|
||||||
|
readonly MIN_LENGTH: number = 2;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private fb: FormBuilder,
|
||||||
|
protected dialogRef: NbDialogRef<ProjectDialogComponent>
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.projectFormGroup = this.fb.group({
|
||||||
|
projectTitle: ['', [Validators.required, Validators.minLength(this.MIN_LENGTH)]],
|
||||||
|
projectClient: ['', [Validators.required, Validators.minLength(this.MIN_LENGTH)]],
|
||||||
|
projectTester: ['', [Validators.required, Validators.minLength(this.MIN_LENGTH)]]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.projectTitleCtrl = this.projectFormGroup.get('projectTitle');
|
||||||
|
this.projectClientCtrl = this.projectFormGroup.get('projectClient');
|
||||||
|
this.projectTesterCtrl = this.projectFormGroup.get('projectTester');
|
||||||
|
|
||||||
|
this.projectFormGroup.valueChanges
|
||||||
|
.pipe(untilDestroyed(this))
|
||||||
|
.subscribe(() => {
|
||||||
|
this.formCtrlStatus = FieldStatus.BASIC;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickSave(value): void {
|
||||||
|
this.dialogRef.close({
|
||||||
|
title: value.projectTitle,
|
||||||
|
client: value.projectClient,
|
||||||
|
tester: value.projectTester
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickClose(): void {
|
||||||
|
this.dialogRef.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
formIsEmptyOrInvalid(): boolean {
|
||||||
|
return this.isEmpty(this.projectTitleCtrl.value)
|
||||||
|
|| this.isEmpty(this.projectClientCtrl.value)
|
||||||
|
|| this.isEmpty(this.projectTesterCtrl.value)
|
||||||
|
|| this.projectTitleCtrl.invalid
|
||||||
|
|| this.projectClientCtrl.invalid
|
||||||
|
|| this.projectTesterCtrl.invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ctrlValue of type string
|
||||||
|
* @return if ctrlValue is empty or not
|
||||||
|
*/
|
||||||
|
isEmpty(ctrlValue: string): boolean {
|
||||||
|
return ctrlValue === '';
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { CommonModule } from '@angular/common';
|
||||||
|
import {ProjectDialogComponent} from '@shared/modules/project-dialog/project-dialog.component';
|
||||||
|
import {NbButtonModule, NbCardModule, NbDialogService, NbFormFieldModule, NbInputModule} from '@nebular/theme';
|
||||||
|
import {FlexLayoutModule} from '@angular/flex-layout';
|
||||||
|
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||||
|
import {TranslateModule} from '@ngx-translate/core';
|
||||||
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
import {ReactiveFormsModule} from '@angular/forms';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
ProjectDialogComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
NbCardModule,
|
||||||
|
NbButtonModule,
|
||||||
|
FlexLayoutModule,
|
||||||
|
FontAwesomeModule,
|
||||||
|
TranslateModule,
|
||||||
|
ReactiveFormsModule,
|
||||||
|
NbFormFieldModule,
|
||||||
|
NbInputModule,
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
DialogService,
|
||||||
|
NbDialogService
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
ProjectDialogComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class ProjectDialogModule { }
|
|
@ -0,0 +1,16 @@
|
||||||
|
import {DialogService} from '@shared/services/dialog-service/dialog.service';
|
||||||
|
import {ComponentType} from '@angular/cdk/overlay';
|
||||||
|
import {TemplateRef} from '@angular/core';
|
||||||
|
import {NbDialogConfig, NbDialogRef} from '@nebular/theme';
|
||||||
|
|
||||||
|
export class DialogServiceMock implements Required<DialogService> {
|
||||||
|
|
||||||
|
dialog: any;
|
||||||
|
|
||||||
|
openCustomDialog<T, D = any, R = any>(
|
||||||
|
componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
|
||||||
|
config?: Partial<NbDialogConfig<Partial<T> | string>>
|
||||||
|
): NbDialogRef<T> {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {DialogService} from './dialog.service';
|
||||||
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
|
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||||
|
import {NbDialogModule, NbDialogRef} from '@nebular/theme';
|
||||||
|
import {DialogServiceMock} from '@shared/services/dialog-service/dialog.service.mock';
|
||||||
|
|
||||||
|
describe('DialogService', () => {
|
||||||
|
let service: DialogService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [
|
||||||
|
HttpClientTestingModule,
|
||||||
|
BrowserAnimationsModule,
|
||||||
|
NbDialogModule.forRoot()
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{provide: DialogService, useClass: DialogServiceMock},
|
||||||
|
{provide: NbDialogRef, useValue: {}},
|
||||||
|
]
|
||||||
|
});
|
||||||
|
service = TestBed.inject(DialogService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,28 @@
|
||||||
|
import {Injectable, TemplateRef} from '@angular/core';
|
||||||
|
import {NbDialogConfig, NbDialogRef, NbDialogService} from '@nebular/theme';
|
||||||
|
import {ComponentType} from '@angular/cdk/overlay';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class DialogService {
|
||||||
|
|
||||||
|
constructor(private dialog: NbDialogService) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens a custom MatDialog
|
||||||
|
*/
|
||||||
|
openCustomDialog<T>(
|
||||||
|
componentOrTemplateRef: ComponentType<T> | TemplateRef<T>,
|
||||||
|
config?: Partial<NbDialogConfig<Partial<T> | string>>
|
||||||
|
): NbDialogRef<T> {
|
||||||
|
return this.dialog.open<T>(componentOrTemplateRef, {
|
||||||
|
context: config?.context || undefined,
|
||||||
|
closeOnEsc: config?.closeOnEsc || false,
|
||||||
|
hasScroll: config?.hasScroll || false,
|
||||||
|
autoFocus: config?.autoFocus || false,
|
||||||
|
closeOnBackdropClick: config?.closeOnBackdropClick || false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ export class NotificationService {
|
||||||
.subscribe((translationContainer) => {
|
.subscribe((translationContainer) => {
|
||||||
this.toastrService.show(
|
this.toastrService.show(
|
||||||
'',
|
'',
|
||||||
translationContainer[popupType] + ' ' + translationContainer[translationKey], {
|
translationContainer[translationKey] + ' ' + translationContainer[popupType], {
|
||||||
position: NbGlobalPhysicalPosition.BOTTOM_RIGHT,
|
position: NbGlobalPhysicalPosition.BOTTOM_RIGHT,
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
toastClass: createCssClassName(popupType)
|
toastClass: createCssClassName(popupType)
|
||||||
|
|
|
@ -11,4 +11,8 @@ export class ProjectServiceMock implements Required<ProjectService> {
|
||||||
getProjects(): Observable<Project[]> {
|
getProjects(): Observable<Project[]> {
|
||||||
return of([]);
|
return of([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
saveProject(): Observable<Project> {
|
||||||
|
return of();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
import { TestBed } from '@angular/core/testing';
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
import { ProjectService } from './project.service';
|
import {ProjectService} from './project.service';
|
||||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||||
import {KeycloakService} from 'keycloak-angular';
|
import {KeycloakService} from 'keycloak-angular';
|
||||||
|
import {Project, SaveProjectDialogBody} from '@shared/models/project.model';
|
||||||
|
import {environment} from '../../environments/environment';
|
||||||
|
|
||||||
describe('ProjectService', () => {
|
describe('ProjectService', () => {
|
||||||
let service: ProjectService;
|
let service: ProjectService;
|
||||||
|
let httpMock: HttpTestingController;
|
||||||
|
|
||||||
|
const apiBaseURL = `${environment.apiEndpoint}/projects`;
|
||||||
|
const dummyDate = new Date('2019-01-10T09:00:00');
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
|
@ -19,9 +25,89 @@ describe('ProjectService', () => {
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
service = TestBed.inject(ProjectService);
|
service = TestBed.inject(ProjectService);
|
||||||
|
httpMock = TestBed.inject(HttpTestingController);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getProjects', () => {
|
||||||
|
const mockProject: Project = {
|
||||||
|
id: '56c47c56-3bcd-45f1-a05b-c197dbd33111',
|
||||||
|
client: 'E Corp',
|
||||||
|
title: 'Some Mock API (v1.0) Scanning',
|
||||||
|
createdAt: dummyDate,
|
||||||
|
tester: 'Novatester',
|
||||||
|
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||||
|
};
|
||||||
|
|
||||||
|
const httpResponse = [{
|
||||||
|
id: '56c47c56-3bcd-45f1-a05b-c197dbd33111',
|
||||||
|
client: 'E Corp',
|
||||||
|
title: 'Some Mock API (v1.0) Scanning',
|
||||||
|
createdAt: dummyDate,
|
||||||
|
tester: 'Novatester',
|
||||||
|
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||||
|
}];
|
||||||
|
|
||||||
|
it('should get Projects', (done) => {
|
||||||
|
service.getProjects().subscribe((projects) => {
|
||||||
|
expect(projects[0].id).toEqual(mockProject.id);
|
||||||
|
expect(projects[0].client).toEqual(mockProject.client);
|
||||||
|
expect(projects[0].title).toEqual(mockProject.title);
|
||||||
|
expect(projects[0].createdAt).toBe(mockProject.createdAt);
|
||||||
|
expect(projects[0].tester).toEqual(mockProject.tester);
|
||||||
|
expect(projects[0].createdBy).toEqual(mockProject.createdBy);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockReq = httpMock.expectOne(`${apiBaseURL}`);
|
||||||
|
expect(mockReq.cancelled).toBe(false);
|
||||||
|
expect(mockReq.request.responseType).toEqual('json');
|
||||||
|
mockReq.flush(httpResponse);
|
||||||
|
|
||||||
|
httpMock.verify();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('saveProject', () => {
|
||||||
|
const mockSaveProjectDialogBody: SaveProjectDialogBody = {
|
||||||
|
client: 'E Corp',
|
||||||
|
title: 'Some Mock API (v1.0) Scanning',
|
||||||
|
tester: 'Novatester',
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockProject: Project = {
|
||||||
|
id: '56c47c56-3bcd-45f1-a05b-c197dbd33111',
|
||||||
|
client: 'E Corp',
|
||||||
|
title: 'Some Mock API (v1.0) Scanning',
|
||||||
|
createdAt: dummyDate,
|
||||||
|
tester: 'Novatester',
|
||||||
|
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||||
|
};
|
||||||
|
|
||||||
|
const httpResponse = {
|
||||||
|
id: '56c47c56-3bcd-45f1-a05b-c197dbd33111',
|
||||||
|
client: 'E Corp',
|
||||||
|
title: 'Some Mock API (v1.0) Scanning',
|
||||||
|
createdAt: dummyDate,
|
||||||
|
tester: 'Novatester',
|
||||||
|
createdBy: '11c47c56-3bcd-45f1-a05b-c197dbd33110'
|
||||||
|
};
|
||||||
|
|
||||||
|
it('should save project', (done) => {
|
||||||
|
|
||||||
|
service.saveProject(mockSaveProjectDialogBody).subscribe(
|
||||||
|
value => {
|
||||||
|
expect(value).toEqual(mockProject);
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
fail);
|
||||||
|
|
||||||
|
const req = httpMock.expectOne(`${apiBaseURL}`);
|
||||||
|
expect(req.request.method).toBe('POST');
|
||||||
|
req.flush(mockProject);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Injectable } from '@angular/core';
|
import {Injectable} from '@angular/core';
|
||||||
import {environment} from '../../environments/environment';
|
import {environment} from '../../environments/environment';
|
||||||
import {HttpClient} from '@angular/common/http';
|
import {HttpClient} from '@angular/common/http';
|
||||||
import {Project} from '../models/project.model';
|
import {Project, SaveProjectDialogBody} from '../models/project.model';
|
||||||
import {Observable} from 'rxjs';
|
import {Observable} from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
|
@ -17,4 +17,12 @@ export class ProjectService {
|
||||||
public getProjects(): Observable<Project[]> {
|
public getProjects(): Observable<Project[]> {
|
||||||
return this.http.get<Project[]>(`${this.apiBaseURL}`);
|
return this.http.get<Project[]>(`${this.apiBaseURL}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save Project
|
||||||
|
* @param project the information of the project
|
||||||
|
*/
|
||||||
|
public saveProject(project: SaveProjectDialogBody): Observable<Project> {
|
||||||
|
return this.http.post<Project>(`${this.apiBaseURL}`, project);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"info": {
|
"info": {
|
||||||
"_postman_id": "58021f5f-0ae9-4f64-990b-f09dcc2d3bc2",
|
"_postman_id": "a20516f0-5f7f-4d15-9f26-ada358993ff8",
|
||||||
"name": "security-c4po-api",
|
"name": "security-c4po-api",
|
||||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||||
},
|
},
|
||||||
|
@ -71,7 +71,7 @@
|
||||||
"header": [],
|
"header": [],
|
||||||
"body": {
|
"body": {
|
||||||
"mode": "raw",
|
"mode": "raw",
|
||||||
"raw": "{\n \"client\": \"Novatec\",\n \"title\": \"log4j Pentest\",\n \"tester\": \"Stipe\",\n \"createyBy\": \"10e06d7a-8dd0-4ecd-8963-056b45079c4f\"\n}",
|
"raw": "{\n \"client\": \"Novatec\",\n \"title\": \"log4j Pentest\",\n \"tester\": \"Stipe\"\n}",
|
||||||
"options": {
|
"options": {
|
||||||
"raw": {
|
"raw": {
|
||||||
"language": "json"
|
"language": "json"
|
||||||
|
|
|
@ -41,8 +41,7 @@ fun ProjectOverview.toProjectOverviewResponseBody(): ResponseBody {
|
||||||
data class ProjectRequestBody(
|
data class ProjectRequestBody(
|
||||||
val client: String,
|
val client: String,
|
||||||
val title: String,
|
val title: String,
|
||||||
val tester: String? = null,
|
val tester: String? = null
|
||||||
val createdBy: String
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun ProjectRequestBody.toProject(): Project {
|
fun ProjectRequestBody.toProject(): Project {
|
||||||
|
@ -52,6 +51,7 @@ fun ProjectRequestBody.toProject(): Project {
|
||||||
title = this.title,
|
title = this.title,
|
||||||
createdAt = Instant.now().toString(),
|
createdAt = Instant.now().toString(),
|
||||||
tester = this.tester,
|
tester = this.tester,
|
||||||
createdBy = this.createdBy
|
// ToDo: Should be changed to SUB from Token after adding AUTH Header
|
||||||
|
createdBy = UUID.randomUUID().toString()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.securityc4po.api.ResponseBody
|
||||||
import org.springframework.http.ResponseEntity
|
import org.springframework.http.ResponseEntity
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import reactor.core.publisher.Mono
|
import reactor.core.publisher.Mono
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/projects")
|
@RequestMapping("/projects")
|
||||||
|
|
|
@ -45,49 +45,60 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
@Test
|
@Test
|
||||||
fun getProjects() {
|
fun getProjects() {
|
||||||
webTestClient.get().uri("/projects")
|
webTestClient.get().uri("/projects")
|
||||||
.header("Authorization", "Bearer $tokenAdmin")
|
.header("Authorization", "Bearer $tokenAdmin")
|
||||||
.exchange()
|
.exchange()
|
||||||
.expectStatus().isOk
|
.expectStatus().isOk
|
||||||
.expectHeader().doesNotExist("")
|
.expectHeader().doesNotExist("")
|
||||||
.expectBody().json(Json.write(getProjectsResponse()))
|
.expectBody().json(Json.write(getProjectsResponse()))
|
||||||
.consumeWith(WebTestClientRestDocumentation.document("{methodName}",
|
.consumeWith(
|
||||||
Preprocessors.preprocessRequest(Preprocessors.prettyPrint(),
|
WebTestClientRestDocumentation.document(
|
||||||
Preprocessors.modifyUris().removePort(),
|
"{methodName}",
|
||||||
Preprocessors.removeHeaders("Host", "Content-Length")),
|
Preprocessors.preprocessRequest(
|
||||||
Preprocessors.preprocessResponse(
|
Preprocessors.prettyPrint(),
|
||||||
Preprocessors.prettyPrint()
|
Preprocessors.modifyUris().removePort(),
|
||||||
),
|
Preprocessors.removeHeaders("Host", "Content-Length")
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
),
|
||||||
PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.STRING).description("The id of the requested project"),
|
Preprocessors.preprocessResponse(
|
||||||
PayloadDocumentation.fieldWithPath("[].client").type(JsonFieldType.STRING).description("The name of the client of the requested project"),
|
Preprocessors.prettyPrint()
|
||||||
PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING).description("The title of the requested project"),
|
),
|
||||||
PayloadDocumentation.fieldWithPath("[].createdAt").type(JsonFieldType.STRING).description("The date where the project was created at"),
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
PayloadDocumentation.fieldWithPath("[].tester").type(JsonFieldType.STRING).description("The user that is assigned as a tester in the project"),
|
PayloadDocumentation.fieldWithPath("[].id").type(JsonFieldType.STRING)
|
||||||
PayloadDocumentation.fieldWithPath("[].createdBy").type(JsonFieldType.STRING).description("The id of the user that created the project")
|
.description("The id of the requested project"),
|
||||||
)
|
PayloadDocumentation.fieldWithPath("[].client").type(JsonFieldType.STRING)
|
||||||
))
|
.description("The name of the client of the requested project"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].title").type(JsonFieldType.STRING)
|
||||||
|
.description("The title of the requested project"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].createdAt").type(JsonFieldType.STRING)
|
||||||
|
.description("The date where the project was created at"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].tester").type(JsonFieldType.STRING)
|
||||||
|
.description("The user that is assigned as a tester in the project"),
|
||||||
|
PayloadDocumentation.fieldWithPath("[].createdBy").type(JsonFieldType.STRING)
|
||||||
|
.description("The id of the user that created the project")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val projectOne = Project(
|
val projectOne = Project(
|
||||||
id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9",
|
id = "4f6567a8-76fd-487b-8602-f82d0ca4d1f9",
|
||||||
client = "E Corp",
|
client = "E Corp",
|
||||||
title = "Some Mock API (v1.0) Scanning",
|
title = "Some Mock API (v1.0) Scanning",
|
||||||
createdAt = "2021-01-10T18:05:00Z",
|
createdAt = "2021-01-10T18:05:00Z",
|
||||||
tester = "Novatester",
|
tester = "Novatester",
|
||||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||||
)
|
)
|
||||||
val projectTwo = Project(
|
val projectTwo = Project(
|
||||||
id = "61360a47-796b-4b3f-abf9-c46c668596c5",
|
id = "61360a47-796b-4b3f-abf9-c46c668596c5",
|
||||||
client = "Allsafe",
|
client = "Allsafe",
|
||||||
title = "CashMyData (iOS)",
|
title = "CashMyData (iOS)",
|
||||||
createdAt = "2021-01-10T18:05:00Z",
|
createdAt = "2021-01-10T18:05:00Z",
|
||||||
tester = "Elliot",
|
tester = "Elliot",
|
||||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
||||||
)
|
)
|
||||||
|
|
||||||
private fun getProjectsResponse() = listOf(
|
private fun getProjectsResponse() = listOf(
|
||||||
projectOne.toProjectResponseBody(),
|
projectOne.toProjectResponseBody(),
|
||||||
projectTwo.toProjectResponseBody()
|
projectTwo.toProjectResponseBody()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,29 +113,39 @@ class ProjectControllerDocumentationTest : BaseDocumentationIntTest() {
|
||||||
.expectStatus().isAccepted
|
.expectStatus().isAccepted
|
||||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||||
.expectBody().json(Json.write(project))
|
.expectBody().json(Json.write(project))
|
||||||
.consumeWith(WebTestClientRestDocumentation.document("{methodName}",
|
.consumeWith(
|
||||||
Preprocessors.preprocessRequest(Preprocessors.prettyPrint(),
|
WebTestClientRestDocumentation.document(
|
||||||
Preprocessors.modifyUris().removePort(),
|
"{methodName}",
|
||||||
Preprocessors.removeHeaders("Host", "Content-Length")),
|
Preprocessors.preprocessRequest(
|
||||||
Preprocessors.preprocessResponse(
|
Preprocessors.prettyPrint(),
|
||||||
Preprocessors.prettyPrint()
|
Preprocessors.modifyUris().removePort(),
|
||||||
),
|
Preprocessors.removeHeaders("Host", "Content-Length")
|
||||||
PayloadDocumentation.relaxedResponseFields(
|
),
|
||||||
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING).description("The id of the requested project"),
|
Preprocessors.preprocessResponse(
|
||||||
PayloadDocumentation.fieldWithPath("client").type(JsonFieldType.STRING).description("The name of the client of the requested project"),
|
Preprocessors.prettyPrint()
|
||||||
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING).description("The title of the requested project"),
|
),
|
||||||
PayloadDocumentation.fieldWithPath("createdAt").type(JsonFieldType.STRING).description("The date where the project was created at"),
|
PayloadDocumentation.relaxedResponseFields(
|
||||||
PayloadDocumentation.fieldWithPath("tester").type(JsonFieldType.STRING).description("The user that is assigned as a tester in the project"),
|
PayloadDocumentation.fieldWithPath("id").type(JsonFieldType.STRING)
|
||||||
PayloadDocumentation.fieldWithPath("createdBy").type(JsonFieldType.STRING).description("The id of the user that created the project")
|
.description("The id of the requested project"),
|
||||||
|
PayloadDocumentation.fieldWithPath("client").type(JsonFieldType.STRING)
|
||||||
|
.description("The name of the client of the requested project"),
|
||||||
|
PayloadDocumentation.fieldWithPath("title").type(JsonFieldType.STRING)
|
||||||
|
.description("The title of the requested project"),
|
||||||
|
PayloadDocumentation.fieldWithPath("createdAt").type(JsonFieldType.STRING)
|
||||||
|
.description("The date where the project was created at"),
|
||||||
|
PayloadDocumentation.fieldWithPath("tester").type(JsonFieldType.STRING)
|
||||||
|
.description("The user that is assigned as a tester in the project"),
|
||||||
|
PayloadDocumentation.fieldWithPath("createdBy").type(JsonFieldType.STRING)
|
||||||
|
.description("The id of the user that created the project")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val project = ProjectRequestBody(
|
val project = ProjectRequestBody(
|
||||||
client = "Novatec",
|
client = "Novatec",
|
||||||
title = "log4j Pentest",
|
title = "log4j Pentest",
|
||||||
tester = "Stipe",
|
tester = "Stipe"
|
||||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,11 +98,12 @@ class ProjectControllerIntTest : BaseIntTest() {
|
||||||
.exchange()
|
.exchange()
|
||||||
.expectStatus().isAccepted
|
.expectStatus().isAccepted
|
||||||
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
.expectHeader().valueEquals("Application-Name", "SecurityC4PO")
|
||||||
.expectBody().json(Json.write(project))
|
.expectBody()
|
||||||
.jsonPath("$.client").isEqualTo("Novatec")
|
.jsonPath("$.client").isEqualTo("Novatec")
|
||||||
.jsonPath("$.title").isEqualTo("log4j Pentest")
|
.jsonPath("$.title").isEqualTo("log4j Pentest")
|
||||||
.jsonPath("$.tester").isEqualTo("Stipe")
|
.jsonPath("$.tester").isEqualTo("Stipe")
|
||||||
.jsonPath("$.createdBy").isEqualTo("f8aab31f-4925-4242-a6fa-f98135b4b032")
|
// ToDo: Should be changed to SUB from Token after adding AUTH Header
|
||||||
|
/*.jsonPath("$.createdBy").isEqualTo("f8aab31f-4925-4242-a6fa-f98135b4b032")*/
|
||||||
}
|
}
|
||||||
|
|
||||||
val project = Project(
|
val project = Project(
|
||||||
|
@ -111,7 +112,7 @@ class ProjectControllerIntTest : BaseIntTest() {
|
||||||
title = "log4j Pentest",
|
title = "log4j Pentest",
|
||||||
createdAt = "2021-04-10T18:05:00Z",
|
createdAt = "2021-04-10T18:05:00Z",
|
||||||
tester = "Stipe",
|
tester = "Stipe",
|
||||||
createdBy = "f8aab31f-4925-4242-a6fa-f98135b4b032"
|
createdBy = "a8891ad2-5cf5-4519-a89e-9ef8eec9e10c"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue