Commit 376e8f5a authored by Marcin Prusinski's avatar Marcin Prusinski
Browse files

Merge pull request #48 in MEL/melodic-frontend from...

Merge pull request #48 in MEL/melodic-frontend from feature/MEL-979-add-validation-for-node-group-in to rc3.0

* commit '1ee5f4b9':
  move blocking UI to menu component
  node group validation, block UI during uploading/deleting xmi and saving secure variables
parents 61a27d80 1ee5f4b9
......@@ -6681,6 +6681,14 @@
"integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
"dev": true
},
"ng-block-ui": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/ng-block-ui/-/ng-block-ui-2.1.8.tgz",
"integrity": "sha512-BBcjUn9b/m3+wPlXkYExuy6ko+5oK7pte79gGUVo6a3HqpLnvPQXFgKV1kUpIM97NYfKKtR/+dPj7Xhh/GSV4w==",
"requires": {
"tslib": "^1.9.0"
}
},
"nice-try": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
......
......@@ -5,6 +5,7 @@ import {AngularMaterialModule} from '../angular-material/angular-material.module
import {RouterModule} from '@angular/router';
import {ConfirmationDialogComponent} from './confirmation-dialog/confirmation-dialog.component';
import {AutoFocusDirective} from './directive/auto-focus.directive';
import {BlockUIModule} from 'ng-block-ui';
@NgModule({
declarations: [
......@@ -15,7 +16,8 @@ import {AutoFocusDirective} from './directive/auto-focus.directive';
imports: [
CommonModule,
AngularMaterialModule,
RouterModule
RouterModule,
BlockUIModule.forRoot()
],
exports: [
MenuComponent,
......
<div class="menu-container" [class.menu-is-mobile]="mobileQuery.matches">
<mat-toolbar color="primary" class="element-dark-primary">
<mat-toolbar-row class="mat-elevation-z6">
<button mat-icon-button *ngIf="isMenuVisible()" (click)="snav.toggle()">
<mat-icon>menu</mat-icon>
</button>
<h1 class="menu-app-name">Melodic</h1>
</mat-toolbar-row>
<block-ui>
<div class="menu-container" [class.menu-is-mobile]="mobileQuery.matches">
<mat-toolbar color="primary" class="element-dark-primary">
<mat-toolbar-row class="mat-elevation-z6">
<button mat-icon-button *ngIf="isMenuVisible()" (click)="snav.toggle()">
<mat-icon>menu</mat-icon>
</button>
<h1 class="menu-app-name">Melodic</h1>
</mat-toolbar-row>
<mat-toolbar-row>
<p class="view-text">{{getCurrentViewTitle()}}</p>
</mat-toolbar-row>
</mat-toolbar>
<mat-toolbar-row>
<p class="view-text">{{getCurrentViewTitle()}}</p>
</mat-toolbar-row>
</mat-toolbar>
<mat-sidenav-container class="menu-sidenav-container"
[style.marginTop.px]="mobileQuery.matches ? 56 : 0">
<mat-sidenav-container class="menu-sidenav-container"
[style.marginTop.px]="mobileQuery.matches ? 56 : 0">
<mat-sidenav #snav [mode]="mobileQuery.matches ? 'over' : 'side'"
[fixedInViewport]="mobileQuery.matches" fixedTopGap="56">
<mat-nav-list>
<a mat-list-item routerLink="/deploying">New deployment</a>
<a mat-list-item routerLink="/process">Process view</a>
<a mat-list-item routerLink="/application">Your application</a>
<a mat-list-item routerLink="/process/details/deployment">Deployed artifacts</a>
<a mat-list-item routerLink=".">Melodic components</a>
<a mat-list-item routerLink="/provider/cloud-definition">Providers settings</a>
<a mat-list-item routerLink="/process/details/offer">Offers</a>
<a *ngIf="isAdmin()" mat-list-item routerLink="/user">Manage users</a>
<a mat-list-item routerLink="/user/password">Change password</a>
<a mat-list-item (click)="onLogOutClick(); snav.close()">Log out</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav #snav [mode]="mobileQuery.matches ? 'over' : 'side'"
[fixedInViewport]="mobileQuery.matches" fixedTopGap="56">
<mat-nav-list>
<a mat-list-item routerLink="/deploying">New deployment</a>
<a mat-list-item routerLink="/process">Process view</a>
<a mat-list-item routerLink="/application">Your application</a>
<a mat-list-item routerLink="/process/details/deployment">Deployed artifacts</a>
<a mat-list-item routerLink=".">Melodic components</a>
<a mat-list-item routerLink="/provider/cloud-definition">Providers settings</a>
<a mat-list-item routerLink="/process/details/offer">Offers</a>
<a *ngIf="isAdmin()" mat-list-item routerLink="/user">Manage users</a>
<a mat-list-item routerLink="/user/password">Change password</a>
<a mat-list-item (click)="onLogOutClick(); snav.close()">Log out</a>
</mat-nav-list>
</mat-sidenav>
<mat-sidenav-content>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
</div>
<mat-sidenav-content>
<router-outlet></router-outlet>
</mat-sidenav-content>
</mat-sidenav-container>
</div>
</block-ui>
......@@ -2,6 +2,7 @@ import {AfterViewChecked, ChangeDetectorRef, Component, OnDestroy} from '@angula
import {MediaMatcher} from '@angular/cdk/layout';
import {UserService} from '../../user/service/user.service';
import {UserRole} from '../../user/model/user-role.enum';
import {BlockUI, NgBlockUI} from 'ng-block-ui';
@Component({
selector: 'app-menu',
......@@ -10,6 +11,7 @@ import {UserRole} from '../../user/model/user-role.enum';
})
export class MenuComponent implements OnDestroy, AfterViewChecked {
@BlockUI() blockUI: NgBlockUI;
mobileQuery: MediaQueryList;
private readonly mobileQueryListener: () => void;
......@@ -50,4 +52,12 @@ export class MenuComponent implements OnDestroy, AfterViewChecked {
return false;
}
}
blockUIView(message: string) {
this.blockUI.start(message);
}
unblockUIView() {
this.blockUI.stop();
}
}
<div id="xmi-uploader-div">
<mat-card>
<mat-card>
<mat-card-header>
<mat-card-title>
XMI uploading
</mat-card-title>
</mat-card-header>
<mat-card-header>
<mat-card-title>
XMI uploading
</mat-card-title>
</mat-card-header>
<mat-card-content>
<div [fileUploadInputFor]="fileUploadQueue" class="upload-drop-zone">
Just drag and drop xmi files here
</div>
<mat-card-content>
<div [fileUploadInputFor]="fileUploadQueue" class="upload-drop-zone">
Just drag and drop xmi files here
</div>
<mat-progress-bar *ngIf="uploadInProgress" color="primary" mode="indeterminate"></mat-progress-bar>
<mat-progress-bar *ngIf="uploadInProgress" color="primary" mode="indeterminate"></mat-progress-bar>
<div class="uploaded-files-list">
<app-files-queue #fileUploadQueue
[fileExtension]="'xmi'"
[notAllowedNames]="uploadedModels"
multiple>
<app-file-uploader (uploadEvent)="onFileUploadEvent($event)"
*ngFor="let file of fileUploadQueue.files; let i = index" [fileAlias]="'file'"
[file]="file"
[httpUrl]="uploadXmiUrl"
[id]="i">
</app-file-uploader>
</app-files-queue>
</div>
<div class="uploaded-files-list">
<app-files-queue #fileUploadQueue
[fileExtension]="'xmi'"
[notAllowedNames]="uploadedModels"
multiple>
<app-file-uploader (uploadEvent)="onFileUploadEvent($event)"
*ngFor="let file of fileUploadQueue.files; let i = index" [fileAlias]="'file'"
[file]="file"
[httpUrl]="uploadXmiUrl"
[id]="i">
</app-file-uploader>
</app-files-queue>
</div>
</mat-card-content>
</mat-card>
</mat-card-content>
</mat-card>
<mat-card>
<mat-card-header>
<mat-card-subtitle>
<h3>Your uploaded models</h3>
</mat-card-subtitle>
</mat-card-header>
<mat-card>
<mat-card-header>
<mat-card-subtitle>
<h3>Your uploaded models</h3>
</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<mat-progress-bar *ngIf="deletingInProgress || secureVariablesUploadingInProgress"
mode="indeterminate"></mat-progress-bar>
<mat-card-content>
<mat-progress-bar *ngIf="deletingInProgress || secureVariablesUploadingInProgress"
mode="indeterminate"></mat-progress-bar>
<mat-spinner *ngIf="downloadingModelsListInProgress"></mat-spinner>
<mat-spinner *ngIf="downloadingModelsListInProgress"></mat-spinner>
<div *ngFor="let model of uploadedModels">
<mat-card>
<mat-card-title>
<span>{{model.name}}</span>
<button (click)="onDefineSecureVariablesClick(model)" *ngIf="variablesForDefiningExist(model)"
[disabled]="deletingInProgress" class="model-button"
color="primary" mat-raised-button>
Define secure variables
</button>
<button (click)="onDeleteModelClick(model)" [disabled]="deletingInProgress" color="warn" mat-button
class="model-button">
<mat-icon>cancel</mat-icon>
</button>
</mat-card-title>
<mat-card-subtitle>{{getInformationForModel(model)}}</mat-card-subtitle>
</mat-card>
</div>
<div *ngFor="let model of uploadedModels">
<mat-card>
<mat-card-title>
<span>{{model.name}}</span>
<button (click)="onDefineSecureVariablesClick(model)" *ngIf="variablesForDefiningExist(model)"
[disabled]="deletingInProgress" class="model-button"
color="primary" mat-raised-button>
Define secure variables
</button>
<button (click)="onDeleteModelClick(model)" [disabled]="deletingInProgress" color="warn" mat-button
class="model-button">
<mat-icon>cancel</mat-icon>
</button>
</mat-card-title>
<mat-card-subtitle>{{getInformationForModel(model)}}</mat-card-subtitle>
</mat-card>
</div>
</mat-card-content>
</mat-card>
</mat-card-content>
</mat-card>
</div>
......@@ -6,6 +6,7 @@ import {SecureVariablesExistence, UploadedModel} from '../model/uploaded-model';
import {SensitiveVariablesDialogComponent} from '../../file-uploader/sensitive-variables-dialog/sensitive-variables-dialog.component';
import {SecureVariablesService} from '../../file-uploader/service/secure-variables.service';
import {SecureVariable} from '../../file-uploader/model/secure-variable';
import {MenuComponent} from '../../common-template/menu/menu.component';
@Component({
......@@ -29,7 +30,8 @@ export class UploaderXmiComponent implements OnInit {
constructor(private snackBar: MatSnackBar,
private dialog: MatDialog,
private deploymentService: DeploymentService,
private secureVariablesService: SecureVariablesService) {
private secureVariablesService: SecureVariablesService,
private menuComponent: MenuComponent) {
}
ngOnInit() {
......@@ -38,7 +40,6 @@ export class UploaderXmiComponent implements OnInit {
onFileUploadEvent(event) {
this.uploadInProgress = true;
this.snackBar.open(`Validating of your model from ${event.file.name} and storing in database in progress ...`, 'Close');
console.log('Received event: ', event);
if (event.event.hasOwnProperty('status')) {
console.log(`Response from uploading status: ${event.event.status}`);
......@@ -92,14 +93,16 @@ export class UploaderXmiComponent implements OnInit {
const modelName = model.name;
console.log(`Delete click for model with name: ${modelName}`);
this.deletingInProgress = true;
this.snackBar.open(`Deleting your model ${modelName} from CDO in progress...`, 'Close');
this.menuComponent.blockUIView(`Deleting model ${modelName} in progress...`);
this.deploymentService.deleteModel(modelName).subscribe(() => {
this.menuComponent.unblockUIView();
console.log(`Model ${modelName} successfully deleted`);
this.snackBar.open(`Model ${modelName} successfully deleted from CDO.`, 'Close', {duration: 10000});
this.deletingInProgress = false;
this.getUploadedModelsList();
},
error1 => {
this.menuComponent.unblockUIView();
console.log(`Problem by deleting model ${modelName}`);
this.snackBar.open(`Problem by deleting model ${modelName} from CDO.`, 'Close', {duration: 10000});
this.deletingInProgress = false;
......@@ -148,19 +151,24 @@ export class UploaderXmiComponent implements OnInit {
this.informAboutSavingSecureVariables(modelName);
this.secureVariablesService.saveSecureVariables(result).subscribe(value => {
console.log(`Sensitive variables successfully stored`);
this.secureVariablesUploadingInProgress = false;
this.setSavingSecureVariablesAsFinished();
this.snackBar.open(`Sensitive variables successfully saved. Your model is ready for deployment`, 'Close');
this.secureVariablesService.removeSecureExistenceFromModelInLocalStorage(modelName);
this.getUploadedModelsList();
},
error1 => {
this.secureVariablesUploadingInProgress = false;
this.setSavingSecureVariablesAsFinished();
this.snackBar.open(`${error1.error.message}`, 'Close');
});
}
informAboutSavingSecureVariables(modelName: string) {
this.secureVariablesUploadingInProgress = true;
this.snackBar.open(`Saving secure variables for model ${modelName} in progress...`, 'Close');
this.menuComponent.blockUIView(`Saving secure variables for model ${modelName} in progress...`);
}
setSavingSecureVariablesAsFinished() {
this.secureVariablesUploadingInProgress = false;
this.menuComponent.unblockUIView();
}
}
......@@ -101,9 +101,9 @@ export class FilesQueueComponent implements OnDestroy, AfterViewInit {
this.fileUploads.first.getUploadedModelsList();
if (successfullyUploadedFilesNames.length !== 0 && failedUploadedFilesMessage.length === 0) {
successfullyUploadedFilesNames = successfullyUploadedFilesNames.substring(0, successfullyUploadedFilesNames.length - 2);
this.snackBar.open(`Files: ${successfullyUploadedFilesNames} uploaded successfully`, 'Close');
this.snackBar.open(`Models: ${successfullyUploadedFilesNames} uploaded successfully`, 'Close');
} else if (successfullyUploadedFilesNames.length !== 0 && failedUploadedFilesMessage.length !== 0) {
this.snackBar.open(`Files: ${successfullyUploadedFilesNames} uploaded successfully, but uploading of other files failed:
this.snackBar.open(`Models: ${successfullyUploadedFilesNames} uploaded successfully, but uploading of other files failed:
${failedUploadedFilesMessage}`, 'Close');
} else {
this.snackBar.open(`No file has been uploaded successfully: ${failedUploadedFilesMessage}`, 'Close');
......
......@@ -6,6 +6,7 @@ import {SecureVariablesService} from '../service/secure-variables.service';
import {UploaderXmiComponent} from '../../deploying-application/uploader-xmi/uploader-xmi.component';
import {DeploymentService} from '../../deploying-application/service/deployment.service';
import {SecureVariable} from '../model/secure-variable';
import {MenuComponent} from '../../common-template/menu/menu.component';
@Component({
selector: 'app-file-uploader',
......@@ -22,7 +23,8 @@ export class FileUploaderComponent implements OnDestroy {
private secureVariablesService: SecureVariablesService,
private deploymentService: DeploymentService,
private snackBar: MatSnackBar,
private uploaderXmiComponent: UploaderXmiComponent
private uploaderXmiComponent: UploaderXmiComponent,
private menuComponent: MenuComponent
) {
}
......@@ -64,42 +66,45 @@ export class FileUploaderComponent implements OnDestroy {
public upload(): void {
this.isUploading = true;
this.menuComponent.blockUIView(`Validating of your model from ${this.uploadingFile.name} and storing in database in progress ...`);
const formData = new FormData();
formData.set(this.fileAlias, this.uploadingFile, this.uploadingFile.name);
this.fileUploadSubscription = this.deploymentService.uploadModel(formData)
.subscribe((event: any) => {
if (event.type === HttpEventType.UploadProgress) {
this.progressPercentage = Math.floor(event.loaded * 100 / event.total);
this.loaded = event.loaded;
this.total = event.total;
}
this.uploadEvent.emit({file: this.uploadingFile, event});
if (event.type === HttpEventType.Response) {
const secureVariables: Array<SecureVariable> = event.body.secureVariables;
console.log('File uploaded, removing from uploading list', event.body.secureVariables);
const modelName = this.uploadingFile.name.slice(0, this.uploadingFile.name.length - 4);
this.secureVariablesService.addUploadedModelToLocalStorage(modelName, secureVariables);
if (secureVariables.length !== 0) {
console.log(`Open dialog for sensitive variables. The first one: ${secureVariables[0]}`);
this.openDialog(secureVariables, modelName);
} else {
console.log(`List of sensitive variables for ${modelName} is empty`);
if (event.type === HttpEventType.UploadProgress) {
this.progressPercentage = Math.floor(event.loaded * 100 / event.total);
this.loaded = event.loaded;
this.total = event.total;
}
this.remove();
}
}, (error: any) => {
console.log(`Error by uploading`, error);
console.log(`Error message: ${error.error.message}`);
if (this.fileUploadSubscription) {
this.fileUploadSubscription.unsubscribe();
}
this.progressPercentage = 0;
this.loaded = 0;
this.isUploading = false;
this.uploadEvent.emit({file: this.uploadingFile, event: error});
console.log(`After emit error event`, error);
});
this.uploadEvent.emit({file: this.uploadingFile, event});
if (event.type === HttpEventType.Response) {
const secureVariables: Array<SecureVariable> = event.body.secureVariables;
console.log('File uploaded, removing from uploading list', event.body.secureVariables);
const modelName = event.body.modelName;
this.secureVariablesService.addUploadedModelToLocalStorage(modelName, secureVariables);
if (secureVariables.length !== 0) {
console.log(`Open dialog for sensitive variables. The first one: ${secureVariables[0]}`);
this.openDialog(secureVariables, modelName);
} else {
console.log(`List of sensitive variables for ${modelName} is empty`);
}
this.setUploadingAsFinished();
this.remove();
}
}, (error: any) => {
this.setUploadingAsFinished();
console.log(`Error by uploading`, error);
console.log(`Error message: ${error.error.message}`);
if (this.fileUploadSubscription) {
this.fileUploadSubscription.unsubscribe();
}
this.progressPercentage = 0;
this.loaded = 0;
this.isUploading = false;
this.uploadEvent.emit({file: this.uploadingFile, event: error});
console.log(`After emit error event`, error);
});
}
public remove(): void {
......@@ -135,37 +140,21 @@ export class FileUploaderComponent implements OnDestroy {
this.sensitiveVariablesDialog.afterClosed().subscribe(result => {
console.log(`Secure variables dialog closed with result`, result);
this.saveSensitiveVariables(result, modelName);
this.uploaderXmiComponent.saveSensitiveVariables(result, modelName);
});
}
saveSensitiveVariables(result: any, modelName: string) {
this.uploaderXmiComponent.informAboutSavingSecureVariables(modelName);
this.secureVariablesService.saveSecureVariables(result).subscribe(value => {
console.log(`Sensitive variables successfully stored`);
this.uploaderXmiComponent.uploadInProgress = false;
this.uploaderXmiComponent.secureVariablesUploadingInProgress = false;
this.snackBar.open(`Sensitive variables successfully saved. Your model is ready for deployment`, 'Close');
this.secureVariablesService.removeSecureExistenceFromModelInLocalStorage(modelName);
this.uploaderXmiComponent.getUploadedModelsList();
},
error1 => {
this.isUploading = false;
this.uploaderXmiComponent.secureVariablesUploadingInProgress = false;
this.uploaderXmiComponent.uploadInProgress = false;
this.snackBar.open(`${error1.error.message}`, 'Close');
});
}
public getUploadedModelsList() {
this.uploaderXmiComponent.getUploadedModelsList();
}
public setUploadingInProgress() {
this.uploaderXmiComponent.uploadInProgress = true;
this.menuComponent.blockUIView('Validating of your models and storing in database in progress ..');
}
public setUploadingAsFinished() {
this.uploaderXmiComponent.uploadInProgress = false;
this.menuComponent.unblockUIView();
}
}
......@@ -73,11 +73,16 @@
<mat-card>
<form [formGroup]="cloudConfigurationForm">
<mat-form-field>
<input matInput formControlName="nodeGroup" placeholder="node group" required>
<input matInput formControlName="nodeGroup" placeholder="node group" required
pattern="{{this.nodeGroupPattern}}">
<mat-error
*ngIf="cloudConfigurationFormControl.nodeGroup.hasError('required') && (cloudConfigurationFormControl.nodeGroup.dirty || cloudConfigurationFormControl.nodeGroup.touched)">
{{getRequiredMsg()}}
</mat-error>
<mat-error
*ngIf="cloudConfigurationFormControl.nodeGroup.hasError('pattern') && (cloudConfigurationFormControl.nodeGroup.dirty || cloudConfigurationFormControl.nodeGroup.touched)">
{{getNodeGroupPatternMsg()}}
</mat-error>
</mat-form-field>
<mat-card-subtitle><h3>Cloud properties</h3></mat-card-subtitle>
......
......@@ -33,6 +33,7 @@ export class CloudDefinitionFormComponent implements OnInit {
cloudType = CloudType.PUBLIC;
cloudTypeEnum = CloudType;
providerNames = ['aws-ec2', 'openstack4j', 'azure', 'oktawave'];
nodeGroupPattern = '^[a-z0-9]{3,}$';
data: MatTableDataSource<ParentProperty>;
displayedColumns: string[] = ['name', 'properties', 'edit', 'delete'];
......@@ -67,7 +68,7 @@ export class CloudDefinitionFormComponent implements OnInit {
this.cloudConfigurationForm = this.formBuilder.group({
id: this.cloudData ? this.cloudData.cloudConfiguration.id : null,
nodeGroup: this.cloudData ? new FormControl({value: this.cloudData.cloudConfiguration.nodeGroup, disabled: this.isReadMode})
: ['', Validators.required],
: ['', [Validators.required, Validators.pattern(this.nodeGroupPattern)]],
properties: this.cloudData ? this.cloudData.cloudConfiguration.properties : this.cloudProperties
});
this.cloudDefinitionForm = this.formBuilder.group({
......@@ -121,6 +122,10 @@ export class CloudDefinitionFormComponent implements OnInit {
return 'Required field for private provider';
}
getNodeGroupPatternMsg() {
return 'Required min 3 sings length. Only lowercase and digits are allowed.';
}
saveCloudDefinition(cloudForm: NgForm) {
this.cloudConfigurationFormControl.properties.setValue(this.cloudProperties);
const cloudDefinition = <CloudDefinition> cloudForm.value;
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment