diff options
Diffstat (limited to 'mod2/ui/src/app/comp-spec-add')
4 files changed, 364 insertions, 0 deletions
diff --git a/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.css b/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.css new file mode 100644 index 0000000..54806ab --- /dev/null +++ b/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.css @@ -0,0 +1,43 @@ +/* + * # ============LICENSE_START======================================================= + * # Copyright (c) 2020 AT&T Intellectual Property. All rights reserved. + * # ================================================================================ + * # Licensed under the Apache License, Version 2.0 (the "License"); + * # you may not use this file except in compliance with the License. + * # You may obtain a copy of the License at + * # + * # http://www.apache.org/licenses/LICENSE-2.0 + * # + * # Unless required by applicable law or agreed to in writing, software + * # distributed under the License is distributed on an "AS IS" BASIS, + * # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * # See the License for the specific language governing permissions and + * # limitations under the License. + * # ============LICENSE_END========================================================= + */ + +.input { + padding-top: 10px; +} + +.inputLabel { + font-weight: 600; + margin-left: 20px; + width: 135px; +} + +.inputFieldSm { + width: 200px; + height: 35px; + padding-left: 6px; +} +.inputFieldMed { + width: 300px; + height: 35px; + padding-left: 6px; +} +.inputFieldLg { + width: 400px; + height: 35px; + padding-left: 6px; +} diff --git a/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.html b/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.html new file mode 100644 index 0000000..f15f19b --- /dev/null +++ b/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.html @@ -0,0 +1,64 @@ +<!-- + # ============LICENSE_START======================================================= + # Copyright (c) 2020 AT&T Intellectual Property. All rights reserved. + # ================================================================================ + # Licensed under the Apache License, Version 2.0 (the "License"); + # you may not use this file except in compliance with the License. + # You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + # ============LICENSE_END========================================================= + --> + +<p-dialog *ngIf="visible" header="Component Spec ADD" [(visible)]="visible" appendTo="body" [modal]="true" [transitionOptions]="'300ms'" + [closeOnEscape]="false" [closable]="false" [style]="{width: '650px'}" (onHide)="closeDialog()"> + + <!-- * * * * * Input fields * * * * * --> + <form [formGroup]="csAddForm" (ngSubmit)="saveCs()" class="bg-faded"> + <!-- * * * Type * * * --> + <div class="input"> + <label class="inputLabel">Type<span style="color:red">*</span></label> + <p-dropdown [options]="types" placeholder="Select Type" optionLabel="type" formControlName="type"></p-dropdown> + </div> + <!-- * * * Labels * * * --> + <div class="input"> + <label class="inputLabel">Labels</label> + <input class="inputFieldLg" type="text" pInputText formControlName="labels" /> + </div> + <span style="padding: 9px 0px 0px 158px; font-size: 13px;">(Separate labels with a space)</span> + <!-- * * * Notes * * * --> + <div class="input"> + <label class="inputLabel" style="vertical-align: top">Notes</label> + <textarea class="inputFieldLg" [rows]="1" [cols]="30" pInputTextarea autoResize="autoResize" formControlName="notes"></textarea> + </div> + <!-- * * * Comp Spec File Select * * * --> + <div class="input"> + <label class="inputLabel">Component Spec<span style="color:red">*</span></label> + + <input type="file" style="width: 460px; color:blue; font-style: italic;" (input)="onCompSpecUpload($event)" name="myfile" id="myfile" accept=".json"> + </div> + + <!-- * * * Policy File Select * * * --> + <div class="input"> + <label class="inputLabel">Policy</label> + + <input type="file" style="width: 460px; color:blue; font-style: italic;" (input)="onPolicyUpload($event)" + name="myPolicyFile" id="myPolicyFile" accept=".json"> + </div> + + <!-- * * * ADD and Cancel buttons * * * --> + <div style="float: right; padding: 20px 45px"> + <button pButton type="button" (click)="closeDialog()" label="Cancel"></button> + <button pButton type="submit" class="ui-button-success" label="Add" [disabled]="!csAddForm.valid || !compSpecSelected" style="width: 70px"></button> + </div> + </form> + + <p-toast key="jsonError" [style]="{width: '430px'}"></p-toast> + +</p-dialog> diff --git a/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.spec.ts b/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.spec.ts new file mode 100644 index 0000000..e29a110 --- /dev/null +++ b/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.spec.ts @@ -0,0 +1,77 @@ +/* + * # ============LICENSE_START======================================================= + * # Copyright (c) 2020 AT&T Intellectual Property. All rights reserved. + * # ================================================================================ + * # Licensed under the Apache License, Version 2.0 (the "License"); + * # you may not use this file except in compliance with the License. + * # You may obtain a copy of the License at + * # + * # http://www.apache.org/licenses/LICENSE-2.0 + * # + * # Unless required by applicable law or agreed to in writing, software + * # distributed under the License is distributed on an "AS IS" BASIS, + * # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * # See the License for the specific language governing permissions and + * # limitations under the License. + * # ============LICENSE_END========================================================= + */ + +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterTestingModule } from '@angular/router/testing'; +import { JwtHelperService, JWT_OPTIONS } from '@auth0/angular-jwt'; +import { MessageService } from 'primeng/api'; +import { ButtonModule } from 'primeng/button'; +import { DialogModule } from 'primeng/dialog'; +import { DropdownModule } from 'primeng/dropdown'; +import { ToastModule } from 'primeng/toast'; + + +import { CompSpecAddComponent } from './comp-spec-add.component'; + +describe('CompSpecAddComponent', () => { + let component: CompSpecAddComponent; + let fixture: ComponentFixture<CompSpecAddComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [CompSpecAddComponent], + imports: [ + DialogModule, + DropdownModule, + ToastModule, + FormsModule, + ReactiveFormsModule, + ButtonModule, + HttpClientTestingModule, + RouterTestingModule + ], + providers: [ + MessageService, + { provide: JWT_OPTIONS, useValue: JWT_OPTIONS }, + JwtHelperService + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(CompSpecAddComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it(`should invalidate spec JSON structure`, async(() => { + const fixture = TestBed.createComponent(CompSpecAddComponent); + const app = fixture.debugElement.componentInstance; + let mockErrorJson = "test: 'test}" + app.compSpecContent = mockErrorJson + expect(() => app.validateJsonStructure()).toThrowError('JSON Structure error, quit!') + })); + +}); diff --git a/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.ts b/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.ts new file mode 100644 index 0000000..7564852 --- /dev/null +++ b/mod2/ui/src/app/comp-spec-add/comp-spec-add.component.ts @@ -0,0 +1,180 @@ +/* + * # ============LICENSE_START======================================================= + * # Copyright (c) 2020 AT&T Intellectual Property. All rights reserved. + * # ================================================================================ + * # Licensed under the Apache License, Version 2.0 (the "License"); + * # you may not use this file except in compliance with the License. + * # You may obtain a copy of the License at + * # + * # http://www.apache.org/licenses/LICENSE-2.0 + * # + * # Unless required by applicable law or agreed to in writing, software + * # distributed under the License is distributed on an "AS IS" BASIS, + * # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * # See the License for the specific language governing permissions and + * # limitations under the License. + * # ============LICENSE_END========================================================= + */ + +import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; +import { InputTextModule } from 'primeng/inputtext'; +import { DropdownModule } from 'primeng/dropdown'; +import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms'; +import { AuthService } from '../services/auth.service'; +import { MessageService } from 'primeng/api'; + +interface Type { + type: string; +} + +@Component({ + selector: 'app-comp-spec-add', + templateUrl: './comp-spec-add.component.html', + styleUrls: ['./comp-spec-add.component.css'] +}) + +export class CompSpecAddComponent implements OnInit { + + compSpecSelected: any; + compSpecContent: any; + + policySelected: any; + policyContent: any; + + csAddForm: FormGroup; + // The loggged in user + username: string; + + // Input form fields + type: string; + labels: string; + notes: string; + specFile: any; + policyJson: any; + + // Dropdowns + types: Type[]; + + // Build JSON as a string + csAddString: any; + // Return JSON to parent component + csAddJson: any; + + constructor(private fb: FormBuilder, private authService: AuthService, private messageService: MessageService) { + } + + @Input() visible: boolean; + @Output() handler: EventEmitter<any> = new EventEmitter(); + + ngOnInit() { + // The logged in user + this.username = this.authService.getUser().username; + + this.csAddForm = new FormGroup({ + type: new FormControl(), + labels: new FormControl(), + notes: new FormControl(), + specFile: new FormControl(), + policyJson: new FormControl() + }); + + // FORM fields and validations + this.csAddForm = this.fb.group({ + type: ['', [Validators.required]], + labels: ['', []], + notes: ['', []], + specFile: ['', []], + policyJson: ['', []] + }); + + // TYPE Dropdown + this.types = [ + { type: 'DOCKER' }, + { type: 'K8S' } + ]; + } + + saveCs() { + this.createOutputJson(); + this.csAddJson = JSON.stringify(this.csAddString); + this.handler.emit(this.csAddJson); + this.closeDialog(); + } + + // Create the JSON to be sent to the parent component + // The "labels" functions below take into account leading/trailing spaces, multiple spaces between labels, and conversion into an array + createOutputJson() { + this.validateJsonStructure(); + + let policy; + if(this.policyContent !== undefined){ + policy = JSON.parse(this.policyContent) + } else { + policy = null + } + + this.csAddString = { + specContent: JSON.parse(this.compSpecContent), + policyJson: policy, + type: this.csAddForm.value['type'].type, + metadata: { + labels: this.csAddForm.value['labels'].trim().replace(/\s{2,}/g, ' ').split(" "), + notes: this.csAddForm.value['notes'] + }, + user: this.username + }; + } + + // Validate, catch, display JSON structure error, and quit! + validateJsonStructure() { + try { + JSON.parse(this.compSpecContent); + } catch (error) { + this.messageService.add({ key: 'jsonError', severity: 'error', summary: 'Invalid Component Spec JSON', detail: error, sticky: true }); + throw new Error('JSON Structure error, quit!'); + } + + if(this.policyContent !== undefined){ + try { + JSON.parse(this.policyContent); + } catch (error) { + this.messageService.add({ key: 'jsonError', severity: 'error', summary: 'Invalid Policy JSON', detail: error, sticky: true }); + throw new Error('JSON Structure error, quit!'); + } + } + } + + // Read the selected Component Spec JSON file + onCompSpecUpload(event) { + this.compSpecSelected = event.target.files[0]; + this.readCsFileContent(this.compSpecSelected); + } + //Read the selected Component Spec JSON file + onPolicyUpload(event) { + this.policySelected = event.target.files[0]; + this.readPolicyFileContent(this.policySelected); + } + + readCsFileContent(file) { + if (file) { + let fileReader = new FileReader(); + fileReader.onload = (e) => { this.compSpecContent = fileReader.result; }; + fileReader.readAsText(file); + } + } + + readPolicyFileContent(file) { + if (file) { + let fileReader = new FileReader(); + fileReader.onload = (e) => { this.policyContent = fileReader.result; }; + fileReader.readAsText(file); + } + } + + // The handler emits 'null' back to parent to close dialog and make it available again when clicked + closeDialog() { + this.visible = false; + this.handler.emit(null); + } + +} |