GUI2 Yang GUI Added in capability to drag and drop YANG files

Can also select files by clicking YANG icon
Both methods allow YANG, ZIP and JAR files containing YANG files
Also improved error reporting on drag and drop
Added Search box in table
Added sorting to columns in table

Change-Id: I1b25076ee26a3597d3f62c6b9e1d314dcc611f57
diff --git a/apps/yang-gui/yang-gui2-lib/lib/yangtable/yangtable.component.html b/apps/yang-gui/yang-gui2-lib/lib/yangtable/yangtable.component.html
index 6c76b39..689445f 100644
--- a/apps/yang-gui/yang-gui2-lib/lib/yangtable/yangtable.component.html
+++ b/apps/yang-gui/yang-gui2-lib/lib/yangtable/yangtable.component.html
@@ -15,8 +15,8 @@
 -->
 
 <!-- YANG Model partial HTML -->
-<div id="ov-yang-model">
-<!--     yangfiledrop on-file-drop="yangDropped()">-->
+<div id="ov-yang-model" (dragover)="onDragOver($event)" (dragleave)="onDragLeave($event)" (drop)="onDrop($event)">
+    <onos-flash id="appMsgFlash" message="{{ alertMsg }}" dwell="5000" warning="true" (closed)="alertMsg = ''"></onos-flash>
     <onos-loading [theme]="'light'" [running]="loadingIconShown"></onos-loading>
     <div class="tabular-header">
         <h2>YANG Models ({{tableData.length}} total)</h2>
@@ -27,16 +27,28 @@
                        [toolTip]="autoRefreshTip"></onos-icon>
             </div>
             <div class="separator"></div>
-
-<!--            <form id="inputYangFileForm">-->
-<!--                <input id="uploadYangFile"-->
-<!--                       type="file" size="50" accept=".zip, *.jar, *.yang"-->
-<!--                       yang-file-model="yangFile">-->
-<!--            </form>-->
             <div class="active"  (click)="triggerForm()">
-                <onos-icon classes="{{ 'active-rect upload' }}" [iconSize]="36"
-                           [iconId]="'nav_yang'" [toolTip]="'Upload a YANG file (.yang)'"></onos-icon>
+                <onos-icon [classes]="'details-icon upload'" [iconSize]="36"
+                           [iconId]="'nav_yang'" [toolTip]="'Upload a YANG file (.zip,.jar,.yang)'"></onos-icon>
             </div>
+            <form #inputYangFileForm="ngForm">
+                <input id="uploadYangFile" hidden
+                       type="file" size="50" accept=".zip,.jar,.yang"
+                       name="yangFile" (change)="fileEvent($event)">
+            </form>
+        </div>
+
+        <div class="search">
+            <input id="searchinput" [(ngModel)]="tableDataFilter.queryStr" type="search" #search placeholder="Search"/>
+            <!--(keyup)="onSearch(search.value)" (search)="onSearch(search.value)"/>-->
+            <select [(ngModel)]="tableDataFilter.queryBy">
+                <!--(change)="onSearchBy($event)" (change)="search.value = ''">-->
+                <option value="" disabled>Search By</option>
+                <option value="$">All Fields</option>
+                <option value="id">ModelId</option>
+                <option value="module">Module</option>
+                <option value="revision">Revision</option>
+            </select>
         </div>
     </div>
 
@@ -44,9 +56,15 @@
         <div class="table-header">
             <table>
                 <tr>
-                    <td colId="modelId">Model ID</td>
-                    <td colId="id">Module</td>
-                    <td colId="revision">Revision</td>
+                    <th colId="modelId" [ngStyle]="{width: '32px'}" class="table-icon" (click)="onSort('modelId')">Model ID
+                        <onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('modelId')"></onos-icon>
+                    </th>
+                    <th colId="id" [ngStyle]="{width: '32px'}" class="table-icon" (click)="onSort('id')">Module
+                        <onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('id')"></onos-icon>
+                    </th>
+                    <th colId="revision" [ngStyle]="{width: '32px'}" class="table-icon" (click)="onSort('revision')">Revision
+                        <onos-icon classes="active-sort" [iconSize]="10" [iconId]="sortIcon('revision')"></onos-icon>
+                    </th>
                 </tr>
             </table>
         </div>
diff --git a/apps/yang-gui/yang-gui2-lib/lib/yangtable/yangtable.component.ts b/apps/yang-gui/yang-gui2-lib/lib/yangtable/yangtable.component.ts
index 31a84f5..f47ec87 100644
--- a/apps/yang-gui/yang-gui2-lib/lib/yangtable/yangtable.component.ts
+++ b/apps/yang-gui/yang-gui2-lib/lib/yangtable/yangtable.component.ts
@@ -14,8 +14,9 @@
  * limitations under the License.
  */
 
-import {Component, OnInit, OnDestroy} from '@angular/core';
-import {WebSocketService, LogService, FnService, SortDir, TableBaseImpl, TableResponse} from 'gui2-fw-lib/public_api';
+import {Component, OnInit, OnDestroy, Inject} from '@angular/core';
+import {WebSocketService, LogService, FnService, SortDir, TableBaseImpl} from 'gui2-fw-lib/public_api';
+import { HttpClient } from '@angular/common/http';
 
 /**
  * Model of the response from the WebSocket
@@ -30,6 +31,12 @@
     yangModels: YangModel[];
 }
 
+/** Prefix to access the REST service for applications */
+const YANGURLPREFIX = '../yang/models/';
+const YANGMODELID = '?modelId=';
+const DRAGDROPMSG1 = 'Drag and drop one file at a time';
+const DRAGDROPMSGEXT = 'Only files ending in .yang, .zip or .jar can be loaded';
+
 @Component({
     selector: 'onos-yangtable',
     templateUrl: './yangtable.component.html',
@@ -40,11 +47,13 @@
 })
 export class YangTableComponent extends TableBaseImpl implements OnInit, OnDestroy {
     selectedModel: YangModel = undefined;
+    alertMsg: string;
 
     constructor(
         protected log: LogService,
         protected fs: FnService,
         protected wss: WebSocketService,
+        protected httpClient: HttpClient,
     ) {
         super(fs, log, wss, 'yangModel');
         this.log.debug('YangTableComponent constructed');
@@ -72,7 +81,7 @@
      * When the upload button is clicked pass this on to the file input (hidden)
      */
     triggerForm() {
-        document.getElementById('uploadFile')
+        document.getElementById('uploadYangFile')
             .dispatchEvent(new MouseEvent('click'));
     }
 
@@ -82,4 +91,65 @@
     rowSelectionParent(event: any, selRow: any) {
         this.selectedModel = selRow;
     }
+
+    onDrop(event: DragEvent) {
+        event.preventDefault();
+        event.stopPropagation();
+
+        const dt = event.dataTransfer;
+        const droppedFiles = dt.files;
+
+        this.log.debug(droppedFiles.length, 'File(s) dropped');
+        if (droppedFiles.length !== 1) {
+            this.log.error(DRAGDROPMSG1, droppedFiles.length, 'were dropped');
+            this.alertMsg = DRAGDROPMSG1;
+            return;
+        }
+
+        const fileEvent = {
+            target: {
+                files: droppedFiles
+            }
+        };
+        this.fileEvent(fileEvent);
+    }
+
+    onDragOver(evt) {
+        evt.preventDefault();
+        evt.stopPropagation();
+    }
+
+    onDragLeave(evt) {
+        evt.preventDefault();
+        evt.stopPropagation();
+    }
+
+    /**
+     * When the file is selected this fires
+     * It passes the file on to the server through a POST request
+     * If there is an error its logged and raised to the user through Flash Component
+     */
+    fileEvent(event: any): void {
+        const file: File = event.target.files[0];
+        const suffix: string = file.name.slice(file.name.lastIndexOf('.'));
+        this.log.debug('File event for', file.name, file.type, file.size, suffix);
+        if (suffix !== '.yang' && suffix !== '.jar' && suffix !== '.zip') {
+            this.log.error(DRAGDROPMSGEXT, file.name, 'rejected');
+            this.alertMsg = DRAGDROPMSGEXT;
+            return;
+        }
+
+        const formData = new FormData();
+        formData.append('file', file);
+
+        this.httpClient
+            .post<any>(YANGURLPREFIX + YANGMODELID + file.name, formData)
+            .subscribe(
+                data => this.log.debug(data),
+                err => {
+                    this.log.warn(err.error.message, err.status);
+                    this.alertMsg = err.error.message; // This will activate flash msg
+                }
+            );
+    }
 }