Merge "Added Widget-Onboarding and dependent Services"
diff --git a/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.html b/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.html
new file mode 100644
index 0000000..bd87e69
--- /dev/null
+++ b/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.html
@@ -0,0 +1,120 @@
+<!--
+  ============LICENSE_START==========================================
+  ONAP Portal
+  ===================================================================
+  Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+  ===================================================================
+ 
+  Unless otherwise specified, all software contained herein is licensed
+  under the Apache License, Version 2.0 (the "License");
+  you may not use this software 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.
+ 
+  Unless otherwise specified, all documentation contained herein is licensed
+  under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+  you may not use this documentation except in compliance with the License.
+  You may obtain a copy of the License at
+ 
+              https://creativecommons.org/licenses/by/4.0/
+ 
+  Unless required by applicable law or agreed to in writing, documentation
+  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============================================
+ 
+  
+  -->
+
+<div class="container">
+  <!--Modal Headers-->
+  <div class="modal-header">
+    <h4 class="modal-title">Widget Details</h4>
+    <button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross')">
+        <span aria-hidden="true">&times;</span>
+    </button>
+  </div>
+
+  <!--Modal Body goes here-->
+  <form id="widgets-details-form" name="widgetForm" [formGroup] = "uploadForm" (ngSubmit)="saveChanges()"
+          enctype="multipart/form-data" novalidate autocomplete="off">
+  <div class="modal-body">
+      <div class="widget-model-body">
+          <div class="item-label">Widget Name</div>
+          <input id="widgets-details-input-name" class="widget-name-field"
+              type="text" formControlName="widgetName" [(ngModel)]="widget.name"
+              ng-pattern="/^[\w -]*$/" maxlength="100"
+              ng-disabled="widgetOnboardingDetails.isEditMode" required />
+
+          <div class="item-label" style="padding-top: 20px">Widget Description</div>
+          
+          <textarea b2b-reset b2b-reset-textarea class="widgets-details-input-desc"
+            formControlName="description" [(ngModel)]="widget.desc" type="text" maxlength="200">
+          </textarea>
+            
+          <div class="table-dropdown">
+            <mat-form-field class="widget-service-select">
+                <mat-label> Service Endpoint </mat-label>
+                <mat-select name="widget-service-select" id="serviceEndPoint" 
+                  formControlName="serviceEndPoint" [(ngModel)]="widget.serviceURL" [disabled]="isEditMode">
+                  <mat-option *ngFor="let rowData of availableMicroServices" [value]="rowData.id" >{{rowData.option}}</mat-option>
+                </mat-select>
+            </mat-form-field>
+          </div>
+
+          <div class="property-label checkbox-label" style="padding-top: 20px">
+            <mat-checkbox  formControlName="allowAllUser" type="checkbox" [(ngModel)]="widget.allUser"
+                id="allow-all-user">
+                Allow all user access
+            </mat-checkbox>
+          </div> 
+
+          <div [hidden]="widget.allUser">
+            <div class="table-dropdown">
+              <mat-form-field class="widget-applications-select">
+                  <mat-label> Application Name </mat-label>
+                  <mat-select multiple name="widget-application-select" id="application" 
+                    formControlName="applicationName" [(ngModel)]="widget.appName">
+                    <mat-option *ngFor="let rowData of availableApps" [value]="rowData.id" >{{rowData.name}}</mat-option>
+                  </mat-select>
+              </mat-form-field>
+            </div>
+          </div>
+
+          <div [hidden]="widget.allUser">
+            <div class="table-dropdown">
+              <mat-form-field class="widget-roles-select">
+                  <mat-label> User Role Name </mat-label>
+                  <mat-select multiple name="widget-role-select" id="roles" 
+                    formControlName="applicationRole" [(ngModel)]="widget.widgetRoles">
+                    <mat-option *ngFor="let appRole of availableApps.roles" [value]="appRole.id" >{{appRole.name}}</mat-option>
+                  </mat-select>
+              </mat-form-field>
+            </div>
+          </div>
+
+          <div class="item-label widget-upload">Upload Widget</div>
+          <div>
+            <input id="widget-onboarding-details-upload-file" 
+              name="profile" type="file" 
+              class="widget-onboarding-details-upload-file ht"
+              (change)="onFileSelect($event)"/>
+          </div>
+        </div>
+      </div> 
+      <div class="modal-footer">
+        <button type="submit" class="btn btn-primary"[disabled]="(isFileNotSelected && !isEditMode)">Save</button>
+        <button type="button" class="btn btn-primary" (click)="activeModal.close('Close')">Cancel</button>
+    </div>
+  </form> 
+</div>
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.scss b/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.scss
new file mode 100644
index 0000000..7f5b871
--- /dev/null
+++ b/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.scss
@@ -0,0 +1,82 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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============================================
+ *
+ * 
+ */
+
+::ng-deep .modal-dialog { 
+    max-width: 700px;
+    width: 619px;
+    overflow-x: auto;
+    overflow-y: auto;
+}
+
+::ng-deep .widget-applications-select {
+   width: 34em;
+}
+
+::ng-deep .widget-roles-select {
+    width: 34em;
+ }
+
+::ng-deep .widget-service-select {
+    width: 34em;
+ }
+
+.widget-name-field{
+    width: 22em;
+    margin-top: 5px;
+}
+
+.widgets-details-input-desc {
+    overflow: auto;
+    resize: vertical;
+    width: 34em;
+    margin-top: 5px;
+}
+
+.widget-upload{
+    margin-top: 16px;
+}
+
+#applicationName{
+    width: 100%;
+    margin-top: 5px;
+}
+
+#serviceEndPoint{
+    width: 100%;
+    margin-top: 5px;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.spec.ts b/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.spec.ts
new file mode 100644
index 0000000..411da1e
--- /dev/null
+++ b/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.spec.ts
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { WidgetDetailsDialogComponent } from './widget-details-dialog.component';
+
+describe('WidgetDetailsDialogComponent', () => {
+  let component: WidgetDetailsDialogComponent;
+  let fixture: ComponentFixture<WidgetDetailsDialogComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ WidgetDetailsDialogComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(WidgetDetailsDialogComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.ts b/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.ts
new file mode 100644
index 0000000..a6d6115
--- /dev/null
+++ b/portal-FE-common/src/app/pages/widget-onboarding/widget-details-dialog/widget-details-dialog.component.ts
@@ -0,0 +1,429 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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 { IWidget } from 'src/app/shared/model/widget-onboarding/widget';
+import { NgbActiveModal, NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { WidgetOnboardingService, MicroserviceService, AdminsService, ApplicationsService } from 'src/app/shared/services';
+import { FormBuilder, FormGroup, FormControl } from '@angular/forms';
+import { IMircroservies } from 'src/app/shared/model/microservice-onboarding/microservices';
+import { ConfirmationModalComponent } from 'src/app/modals/confirmation-modal/confirmation-modal.component';
+import { InformationModalComponent } from 'src/app/modals/information-modal/information-modal.component';
+
+@Component({
+  selector: 'app-widget-details-dialog',
+  templateUrl: './widget-details-dialog.component.html',
+  styleUrls: ['./widget-details-dialog.component.scss']
+})
+export class WidgetDetailsDialogComponent implements OnInit {
+  @Input() public widget: IWidget;
+  @Input() public availableMicroServices: Array<IMircroservies>;
+  @Input() public applicationList: Array<Object>;
+
+  @Output() passEntry: EventEmitter<any> = new EventEmitter();
+
+  widgetsList: any;
+  uploadForm: FormGroup;
+  result: any;
+  selected: any;
+  isEditMode: boolean = false;
+  hasSelectedApp: boolean = false;
+  availableApps = [];
+  availableRoles = [];
+  allRoleSelected: boolean = false;
+  appCounter = 0;
+  duplicatedName:boolean = true;
+  widgetFileTypeError:boolean = false;
+  isFileNotSelected:boolean = true;
+
+  constructor(public widgetOnboardingService: WidgetOnboardingService, 
+    public microservice: MicroserviceService, public applicationsService: ApplicationsService,
+    public formBuilder: FormBuilder, public activeModal: NgbActiveModal, 
+    public ngbModal: NgbModal, public adminsService: AdminsService) { }
+
+  ngOnInit() {
+    this.widget.allUser = true;
+    this.getOnboardingWidgets();
+    this.getAvailableApps();
+    this.duplicatedName = true;
+    this.allRoleSelected = false;
+    this.appCounter = 0;
+    this.uploadForm = this.formBuilder.group({
+      profile: [''],
+      widgetName:[''],
+      description:[''],
+      serviceEndPoint:[''],
+      allowAllUser:[''],
+      applicationName:[''],
+      applicationRole:['']
+    });
+    
+    if(this.widget && this.widget.name){
+      this.isEditMode = true;
+    }
+    if(this.isEditMode && this.widget && this.widget.allowAllUser == "Y"){
+      this.widget.allUser = true;
+    }else if(this.isEditMode && this.widget && this.widget.allowAllUser == "N"){
+      this.widget.allUser = false;
+    }
+    if(this.widget && this.widget.serviceId != null){
+      this.widget.serviceURL = this.widget.serviceId;
+    }
+  }
+
+  //Add Or Update Widget.
+  saveChanges(){
+    if(this.widget.name == '' || this.widget.name == undefined){
+      this.openConfirmationModal("Error",'Widget Name is required.');
+      return;
+    }
+
+    if(!this.isEditMode){
+      this.updateWidgetName();
+    }
+
+    if(this.duplicatedName == false){
+      this.openConfirmationModal("Error",'Name not available - please choose different name.');
+      return;
+    }
+
+    if(this.widgetFileTypeError == true){
+      this.openConfirmationModal("Error",'File must be .zip');
+      return;
+    }
+
+    let widgetName = this.widget.name;
+    let description = this.widget.desc
+    let file_loc = widgetName + ".zip";
+    const formData = new FormData();
+    formData.append('file', this.uploadForm.get('profile').value);
+    //console.log("FormData >>>>::> ",formData.get('file'));
+
+    /*if((formData == undefined && !this.isEditMode) ||
+      (!this.widget.allUser && this.appCounter == 0) ||
+      this.widget.name == null ||
+      (!this.widget.allUser && !this.allRoleSelected)){
+        console.log("return from 2nd check");
+        return;	
+    }*/
+    
+    let selectedRoles = [];
+
+    if(!this.widget.allUser){
+      for(var i = 0; i < this.availableApps.length; i++){
+        if(this.availableApps[i].isSelected){
+          for(var n = 0; n < this.availableApps[i].roles.length; n++) {
+            if(this.availableApps[i].roles[n].isSelected){
+              var role = {
+                    app: {appId: this.availableApps[i].id},
+                    roleId: this.availableApps[i].roles[n].id,
+                    roleName: this.availableApps[i].roles[n].name,
+                };
+              selectedRoles.push(role);
+            }
+          }
+        }
+      }
+    }
+
+    let allowAllUser = 0;
+    if(this.widget.allUser){
+      allowAllUser = 1;
+    }
+
+    let serviceId = null;
+    if(this.widget.serviceURL != null &&   this.widget.serviceURL != undefined){
+        serviceId = parseInt(this.widget.serviceURL);
+    }
+    var newWidget = {
+        name: widgetName,
+        desc: description,
+        widgetRoles: selectedRoles,
+        fileLocation: file_loc,
+        allowAllUser: allowAllUser,
+        serviceId: serviceId
+    };
+
+    if(this.isEditMode){
+      //console.log("widget in updateWidget :::: >>> ",newWidget);
+      if(formData && formData.get('file')){
+        this.widgetOnboardingService.updateWidgetWithFile(formData, this.widget.id, newWidget)
+          .subscribe( _data => {
+            this.result = 'updated';
+            this.passEntry.emit(this.result);
+          }, error => {
+            console.log(error);
+            this.openConfirmationModal("Error",'Could not update. Please retry.');
+        });
+      }else{
+        this.widgetOnboardingService.updateWidget(this.widget.id, newWidget)
+          .subscribe( _data => {
+            this.result = 'updated';
+            this.passEntry.emit(this.result);
+          }, error => {
+            this.openConfirmationModal("Error",'Could not update. Please retry.');
+            console.log(error);
+        });
+      }
+    }else{
+      //console.log("newWidget in createWidget :::: >>> ",newWidget);
+      this.widgetOnboardingService.createWidget(newWidget, formData)
+        .subscribe( _data => {
+          this.result = 'created';
+          this.passEntry.emit(this.result);
+        }, error => {
+          this.openConfirmationModal("Error",'Could not save. Please retry.');
+          console.log(error);
+      });
+    }
+    this.ngbModal.dismissAll();
+  }
+
+  onFileSelect(event) {
+    this.widgetFileTypeError = false;
+    this.isFileNotSelected = false;
+    if (event.target.files.length > 0) {
+      const file = event.target.files[0];
+      //console.log("onFileSelect called.. ",file);
+      var fileName = file.name;
+      var validFormats = ['zip'];
+      var ext = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase();
+      if(validFormats.indexOf(ext) == -1){
+        this.widgetFileTypeError = true;
+      }
+      this.uploadForm.get('profile').setValue(file);
+    }
+  }
+
+  appUpdate(){
+    this.hasSelectedApp = false;
+    this.appCounter = 0;
+    for(var i = 0; i < this.availableApps.length; i++){
+      if(this.availableApps[i].isSelected){
+        this.appCounter++;
+        if(!this.hasSelectedApp)
+          this.hasSelectedApp = true;
+      }
+      if(this.availableApps[i].isSelected
+      && this.availableApps[i].roles.length == 0){
+        var index = i;
+        this.availableRoles = [];    
+        this.adminsService.getRolesByApp(this.availableApps[i].id)
+        .subscribe( roles => {
+          if(roles && roles.length >0){
+            for(var i = 0; i < roles.length; i++){
+              this.availableRoles.push({
+                id: roles[i].id,
+                      name: roles[i].name,
+                      isSelected: false,
+              }); 
+            }
+          }
+          this.availableApps[index].roles = this.availableRoles;
+        }, error => {
+          console.log(error);
+        });
+      }
+    }
+    this.allRoleSelected = true;
+    this.checkRoleSelected();
+  }
+
+  roleUpdate(app){
+    this.allRoleSelected = true;
+      for(var i = 0; i < app.roles.length; i++){
+        if(app.roles[i].isSelected){
+          app.roleSelected = true;
+          this.checkRoleSelected();
+          return;
+        }
+      }
+    app.roleSelected = false;
+    this.checkRoleSelected();
+  }
+    
+  checkRoleSelected(){
+    for(var i = 0; i < this.availableApps.length; i++){
+      if(this.availableApps[i].isSelected
+      && !this.availableApps[i].roleSelected){
+        this.allRoleSelected = false;
+        return;
+      }
+    }
+  }
+
+  getAppName = function(appId){
+    for(var i = 0; i < this.availableApps.length; i++){
+      if(this.availableApps[i].id == appId){
+        return this.availableApps[i].name;
+      }
+    }
+  }
+
+  updateWidgetName(){
+    for(var i = 0; i < this.widgetsList.length; i++){
+      if(this.widget.name && this.widget.name.toUpperCase() == this.widgetsList[i].name.toUpperCase()){
+        this.duplicatedName = false;
+        return;
+      }
+    }
+    this.duplicatedName = true;
+  }
+
+  getOnboardingWidgets(){
+    this.widgetOnboardingService.getManagedWidgets()
+      .subscribe(_data => {
+          this.result = _data
+          if(!(_data instanceof Array)){
+           return;
+          }
+          if (this.result == null || this.result == 'undefined') {
+              console.log('WidgetOnboardingService::getOnboardingWidgets Failed: Result or result.data is null');
+          }else {
+            this.widgetsList = _data;
+        }
+      }, error =>{
+        console.log(error);
+    });
+  }
+
+  getAvailableApps(){
+    if(this.isEditMode == false){	
+      this.availableApps=[];
+      this.applicationsService.getAppsForSuperAdminAndAccountAdmin()
+        .subscribe(apps => {
+          this.availableApps=[];
+          for(var i=0;i<apps.length;i++) {
+            if (!apps[i].restrictedApp) {
+              this.availableApps.push({
+                  id: apps[i].id,
+                  name: apps[i].name,
+                  roles: [],
+                  roleSelected: false,
+                  isSelected: false,
+              });
+          }
+        }
+        }, error =>{
+          console.log(error);
+      });
+    }else if(this.isEditMode == true){
+      if(this.widget.allowAllUser == "Y"){
+        this.widget.allUser = true;
+      }
+      this.applicationsService.getAppsForSuperAdminAndAccountAdmin()
+      .subscribe(apps => {
+        this.availableApps=[];
+        let selectedApps = {};
+        var availableApps = this.availableApps;  
+        this.allRoleSelected = true;
+        for(var i=0; i < this.widget.widgetRoles.length; i++){
+          if(selectedApps[this.widget.widgetRoles[i].app.appId] != undefined)
+            selectedApps[this.widget.widgetRoles[i].app.appId] += this.widget.widgetRoles[i].roleId + ";" + this.widget.widgetRoles[i].roleName + ";"; 
+          else{
+            selectedApps[this.widget.widgetRoles[i].app.appId] = this.widget.widgetRoles[i].roleId + ";" + this.widget.widgetRoles[i].roleName + ";";        		
+            this.appCounter++;
+          }
+        }         		
+        apps.forEach(function(app, index){
+            availableApps.push({
+              id: app.id,
+              name: app.name,
+              roles: [],
+              roleSelected: false,
+              isSelected: false,
+            });
+            if(selectedApps[app.id] != undefined){
+              this.adminsService.getRolesByApp(app.id)
+              .subscribe(roles => {
+                var role = selectedApps[app.id].split(';');
+                var selectedRoles = [];
+                var n = 0;
+                while((n+1) < role.length){
+                    selectedRoles.push({
+                        id: role[n++],
+                        name: role[n++],
+                        isSelected: true,
+                    });
+                }					
+                for(var m = 0; m < roles.length; m++){
+                  var hasSelected = true;
+                  for(var n = 0; n < selectedRoles.length; n++){
+                    if(selectedRoles[n].id == roles[m].id){
+                      hasSelected = false;
+                      break;
+                    }
+                  }
+                  if(hasSelected){
+                    selectedRoles.push({
+                      id: roles[m].id,
+                      name: roles[m].name,
+                      isSelected: false,
+                    }); 
+                  }	   
+                }  
+                availableApps[index].roleSelected = true;
+                availableApps[index].isSelected = true;
+                availableApps[index].roles = selectedRoles;
+              }, error =>{
+                  console.log(error);
+              });
+            }
+        })
+      
+      }, error =>{
+        console.log(error);
+      });
+    }
+    //console.log("this.availableApps :: ",this.availableApps);
+  }
+
+  openConfirmationModal(_title: string, _message: string) {
+    const modalInfoRef = this.ngbModal.open(ConfirmationModalComponent);
+    modalInfoRef.componentInstance.title = _title;
+    modalInfoRef.componentInstance.message = _message;
+  }
+
+  openInformationModal(_title: string, _message: string){
+    const modalInfoRef = this.ngbModal.open(InformationModalComponent);
+    modalInfoRef.componentInstance.title = _title;
+    modalInfoRef.componentInstance.message = _message;
+    return modalInfoRef;
+  }
+  
+}
diff --git a/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.html b/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.html
new file mode 100644
index 0000000..4306554
--- /dev/null
+++ b/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.html
@@ -0,0 +1,100 @@
+<!--
+  ============LICENSE_START==========================================
+  ONAP Portal
+  ===================================================================
+  Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+  ===================================================================
+ 
+  Unless otherwise specified, all software contained herein is licensed
+  under the Apache License, Version 2.0 (the "License");
+  you may not use this software 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.
+ 
+  Unless otherwise specified, all documentation contained herein is licensed
+  under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+  you may not use this documentation except in compliance with the License.
+  You may obtain a copy of the License at
+ 
+              https://creativecommons.org/licenses/by/4.0/
+ 
+  Unless required by applicable law or agreed to in writing, documentation
+  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============================================
+   
+  -->
+
+<div class="container">
+    <div class="ecomp-main-view-title">
+      <h1>Widget Onboarding</h1>
+    </div>
+
+    <div class="c-ecomp-abs-select default">
+      <div class="table-dropdown">
+        <mat-form-field class="widget-application-select">
+            <mat-label> Select Application </mat-label>
+            <mat-select name="widget-application-select" [(ngModel)]="filterByApp" #appId="ngModel" (ngModelChange)="applyAppFilter($event)">
+              <mat-option *ngFor="let rowData of applicationList" [value]="rowData">{{rowData.title}}</mat-option>
+            </mat-select>
+        </mat-form-field>
+           
+        <mat-form-field>
+          <input matInput type="text" (keyup)="applyFilter($event.target.value)" placeholder="Search in entire table">
+        </mat-form-field>
+       
+        <button type="button" class="btn btn-primary" (click)="openAddWigetModal('')">
+          <i class="icon ion-md-person-add"></i>Add Widget
+        </button>
+      </div>
+    </div>
+  
+    <table mat-table [dataSource]="dataSource" matSort>
+      <!-- Wiget Name Column -->
+      <ng-container matColumnDef="widgetName">
+        <th id="col1" mat-header-cell *matHeaderCellDef> Widget Name  </th>
+        <td (click)="openAddWigetModal(element)" id="rowheader_t1_{{i}}-widgetName" 
+          mat-cell *matCellDef="let element; let i = index;"> {{element.name}}
+        </td>
+      </ng-container>
+  
+      <!-- Application Name Column -->
+      <ng-container matColumnDef="application">
+        <th id="col2" mat-header-cell *matHeaderCellDef> Application </th>
+        <td (click)="openAddWigetModal(element)" id="rowheader_t1_{{i}}-application" 
+          mat-cell *matCellDef="let element; let i=index;"> {{element.appContent}} </td>
+      </ng-container>
+  
+      <!-- Download URL -->
+      <ng-container matColumnDef="download">
+        <th id="col3" mat-header-cell *matHeaderCellDef> Download </th>
+        <td id="rowheader_t1_{{$index}}-download" mat-cell *matCellDef="let element; let i=index;">
+            <i class="icon ion-md-download" (click)="downloadWidget(element)"></i>
+        </td>
+      </ng-container>
+  
+      <!-- Delete Column -->
+      <ng-container matColumnDef="delete">
+        <th id="col4" mat-header-cell *matHeaderCellDef> Delete </th>
+        <td id="rowheader_t1_{{i}}" mat-cell *matCellDef="let element; let i=index;">
+          <span class="icon-trash" id="{{i}}-button-portal-admin-remove" (click)="removeWidget(element)">
+            <i class="icon ion-md-trash"></i>
+          </span>
+        </td>
+      </ng-container>
+  
+      <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
+      <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
+    </table>
+    <mat-paginator [pageSizeOptions]="[10, 20]" showFirstLastButtons></mat-paginator>
+</div>
diff --git a/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.scss b/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.scss
new file mode 100644
index 0000000..b87b7ab
--- /dev/null
+++ b/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.scss
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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============================================
+ *
+ * 
+ */
+
+.container th{
+    padding-bottom: 15px;
+    font-weight: bold;
+}
+
+::ng-deep .mat-form-field-infix {
+    width: 200px;
+}
+
+::ng-deep .widget-application-select {
+    display: inline-block;
+    position: relative;
+    text-align: left;
+    margin-left: 10px;
+    margin-right: 110px;
+}
+
+.ion-md-download{
+    cursor: pointer;
+}
+
+.ion-md-trash{
+    cursor: pointer;
+}
\ No newline at end of file
diff --git a/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.spec.ts b/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.spec.ts
new file mode 100644
index 0000000..2fd7ef2
--- /dev/null
+++ b/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.spec.ts
@@ -0,0 +1,62 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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 { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { WidgetOnboardingComponent } from './widget-onboarding.component';
+
+describe('WidgetOnboardingComponent', () => {
+  let component: WidgetOnboardingComponent;
+  let fixture: ComponentFixture<WidgetOnboardingComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ WidgetOnboardingComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(WidgetOnboardingComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.ts b/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.ts
new file mode 100644
index 0000000..d87791f
--- /dev/null
+++ b/portal-FE-common/src/app/pages/widget-onboarding/widget-onboarding.component.ts
@@ -0,0 +1,311 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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, ViewChild, Input, ChangeDetectionStrategy } from '@angular/core';
+import { MatTableDataSource } from '@angular/material';
+import { MatSort, MatPaginator } from '@angular/material';
+import { WidgetOnboardingService, MicroserviceService } from '../../shared/services/index';
+import { IMircroservies } from 'src/app/shared/model/microservice-onboarding/microservices';
+import { IWidget } from 'src/app/shared/model/widget-onboarding/widget';
+import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
+import { HttpClient } from '@angular/common/http';
+import { WidgetDetailsDialogComponent } from './widget-details-dialog/widget-details-dialog.component';
+import { ConfirmationModalComponent } from 'src/app/modals/confirmation-modal/confirmation-modal.component';
+import { InformationModalComponent } from 'src/app/modals/information-modal/information-modal.component';
+
+@Component({
+  changeDetection: ChangeDetectionStrategy.OnPush,
+  selector: 'app-widget-onboarding',
+  templateUrl: './widget-onboarding.component.html',
+  styleUrls: ['./widget-onboarding.component.scss']
+})
+export class WidgetOnboardingComponent implements OnInit {
+
+  widgetsList: Array<IWidget> = [];
+  applicationList: Array<Object> = [];
+  availableMicroServices: Array<IMircroservies> = [];
+  displayedColumns: string[] = ['widgetName', 'application', 'download','delete'];
+  isCommError: boolean = false;
+  dataSource = new MatTableDataSource(this.widgetsList);
+  @ViewChild(MatSort) sort: MatSort;
+  @ViewChild(MatPaginator) paginator: MatPaginator;
+ 
+  isEditMode: boolean = false;
+  result: any;
+  
+
+  constructor( public widgetOnboardingService: WidgetOnboardingService, 
+    public microservice: MicroserviceService,public ngbModal: NgbModal) { }
+
+  ngOnInit() {
+    this.prepareApplicationRoleName();
+    this.getOnboardingWidgets();
+    this.populateAvailableApps();
+    this.getAvailableMicroServices();  
+  }
+
+  getOnboardingWidgets(){
+    this.isCommError = false;
+    this.widgetOnboardingService.getManagedWidgets()
+      .subscribe(_data => {
+          this.result = _data
+          if(!(_data instanceof Array)){
+            this.isCommError = true;
+            return;
+          }
+          //console.log("getOnboardingWidgets Data :: ", _data);
+          if (this.result == null || this.result == 'undefined') {
+              console.log('WidgetOnboardingService::getOnboardingWidgets Failed: Result or result.data is null');
+          }else {
+            let reSortedWidget = _data.sort(this.getSortOrder("name"));
+            this.widgetsList = reSortedWidget;
+            this.prepareApplicationRoleName();
+            this.populateTableData(this.widgetsList);
+        }
+      }, error =>{
+        console.log(error);
+    });
+  }
+
+  //Refactor this into a directive
+  getSortOrder(prop){
+    return function(a, b) {
+        if (a[prop].toLowerCase() > b[prop].toLowerCase()) {
+            return 1;
+        } else if (a[prop].toLowerCase() < b[prop].toLowerCase()) {
+            return -1;
+        }
+        return 0;
+    }
+  }
+
+  removeWidget(widget: IWidget) {
+    let confirmationMsg = 'You are about to delete this Widget : ' + widget.name+ '. Click OK to continue.';
+    this.openInformationModal("Confirmation",confirmationMsg).result.then((result) => {
+      if (result === 'Ok') {
+        if(!widget || widget == null){
+          console.log('WidgetOnboardingCtrl::deleteService: No widget or ID... cannot delete');
+          return;
+        }
+        
+        this.widgetsList.splice(this.widgetsList.indexOf(widget), 1);
+        
+        this.widgetOnboardingService.deleteWidget(widget.id)
+          .subscribe( _data => {
+            this.result = _data;
+            this.openConfirmationModal("Success",'Widget deleted successfully');
+          }, error => {
+            console.log(error);
+        }); 
+    
+        this.populateTableData(this.widgetsList);
+      }
+    }, (resut) => {
+      this.openConfirmationModal('Error', resut);
+      return;
+    })
+  }
+
+  
+  openAddWigetModal(rowData:any){
+      //console.log("openAddWigetModal getting called...");
+      const modalRef = this.ngbModal.open(WidgetDetailsDialogComponent, { size: 'lg' });
+      modalRef.componentInstance.widget = rowData;
+      modalRef.componentInstance.availableMicroServices = this.availableMicroServices;
+      modalRef.componentInstance.applicationList = this.applicationList;
+      modalRef.componentInstance.widgetsList = this.widgetsList;
+      if(rowData != 'undefined' && rowData){
+        this.isEditMode = true;
+      }else{
+        modalRef.componentInstance.widget = {};
+        this.isEditMode = false;
+      }
+      modalRef.componentInstance.passEntry.subscribe((receivedEntry: any) => {
+        //console.log("receivedEntry >>> ",receivedEntry);
+        if(receivedEntry){
+          this.widgetsList = [];
+          this.getOnboardingWidgets();
+        }
+      });
+  }
+
+  applyFilter(filterValue: string) {
+    this.dataSource.filter = filterValue.trim().toLowerCase();
+  }
+
+  applyAppFilter(event) {
+    let filterByApp = event.title;
+    if(filterByApp == 'All Applications'){
+      this.getOnboardingWidgets();
+    }else{
+      this.dataSource.filter = filterByApp.trim().toLowerCase();
+    }
+  }
+
+  downloadWidget(widget){
+    this.widgetOnboardingService.downloadWidgetFile(widget.id)
+      .subscribe(res => {
+        var data = res;
+        //console.log("downloadWidgetFile response :: ",data);
+        var filename = widget.name + ".zip";	
+        if (data == undefined || data == null){
+          this.openConfirmationModal("Could not download. Please retry.", '');
+          return;         	
+        }
+        var a = document.createElement('a');
+        var blob = new Blob([data], {type: 'application/octet-stream'});
+        var url = window.URL.createObjectURL(blob);
+        a.href = url;
+        a.download = filename;
+        document.body.appendChild(a);
+        a.click();
+        
+        setTimeout(function(){
+          document.body.removeChild(a);
+          window.URL.revokeObjectURL(url);  
+        }, 100);  
+    
+    }, error =>{
+      console.log(error);
+      this.openConfirmationModal("Could not download. Please retry.", error.message);
+    });
+  }
+
+  
+  populateTableData(wigetList: Array<IWidget>){
+    this.dataSource = new MatTableDataSource(wigetList);
+    this.dataSource.sort = this.sort;
+    this.dataSource.paginator = this.paginator;
+  };
+
+  prepareApplicationRoleName(){
+    if(this.widgetsList && this.widgetsList.length > 0){
+      for(var i = 0; i < this.widgetsList.length; i++){
+        let set = new Set();
+        var info = "";
+        var appContent = [];
+        var appName = [];	
+        if(this.widgetsList[i].widgetRoles && this.widgetsList[i].widgetRoles.length >0){
+          for(var n = 0; n < this.widgetsList[i].widgetRoles.length; n++){
+            if(this.widgetsList[i].widgetRoles[n].app)
+            set.add(this.widgetsList[i].widgetRoles[n].app.appName);
+          }
+          set.forEach(function (item) {
+            info = item.toString() + " - ";
+            for(var n = 0; n < this.widgetsList[i].widgetRoles.length; n++){
+              if(this.widgetsList[i].widgetRoles[n].app && item.toString() == this.widgetsList[i].widgetRoles[n].app.appName){
+                  info += this.widgetsList[i].widgetRoles[n].roleName + "; ";
+              }
+            }
+            appContent.push(info);
+            appName.push(item.toString());
+          }.bind(this));
+        }
+        if(this.widgetsList[i].allowAllUser == "Y"){
+          info = "All Applications";
+          appContent.push("All Applications");
+          appName.push("All Applications");
+        }
+        this.widgetsList[i].appContent = appContent;
+        this.widgetsList[i].appName = appName;
+      }
+    }
+  }
+
+  populateAvailableApps(){
+    this.widgetOnboardingService.populateAvailableApps()
+      .subscribe( _data => {
+        this.applicationList.push({
+          index: 0,
+          title: 'All Applications',
+          value: ''
+        })
+        var reSortedApp = _data.sort(this.getSortOrder("name"));
+        var realAppIndex = 1;
+        for (let i = 1; i <= reSortedApp.length; i++) {
+            if (!reSortedApp[i-1].restrictedApp) {
+                if(_data[i - 1].name && _data[i - 1].name!=""){
+                  this.applicationList.push({
+                      index: realAppIndex,
+                      title: _data[i - 1].name,
+                      value: _data[i - 1].id
+                  })
+                }
+                realAppIndex = realAppIndex + 1;
+            }
+        }
+      }, error => {
+        console.log(error);
+    }); 
+  }
+
+  getAvailableMicroServices = () =>{
+    this.availableMicroServices = [];
+    this.microservice.getServiceList()
+    .subscribe(_data => {
+        this.result = _data;
+        if (this.result == null || this.result == 'undefined') {
+             console.log('MicroserviceService::getAvailableMicroServices Failed: Result or result.data is null');
+        }else {
+          for(var i = 0; i < _data.length; i++){
+            this.availableMicroServices.push({
+              id: _data[i].id,
+              name: _data[i].name,
+              option: _data[i].name + ": " + _data[i].url
+            });
+          }
+        }
+    }, error =>{
+      console.log(error);
+    });
+  }
+
+  openConfirmationModal(_title: string, _message: string) {
+    const modalInfoRef = this.ngbModal.open(ConfirmationModalComponent);
+    modalInfoRef.componentInstance.title = _title;
+    modalInfoRef.componentInstance.message = _message;
+  }
+
+  openInformationModal(_title: string, _message: string){
+    const modalInfoRef = this.ngbModal.open(InformationModalComponent);
+    modalInfoRef.componentInstance.title = _title;
+    modalInfoRef.componentInstance.message = _message;
+    return modalInfoRef;
+  }
+
+}
diff --git a/portal-FE-common/src/app/shared/services/admins/admins.service.spec.ts b/portal-FE-common/src/app/shared/services/admins/admins.service.spec.ts
new file mode 100644
index 0000000..ea61061
--- /dev/null
+++ b/portal-FE-common/src/app/shared/services/admins/admins.service.spec.ts
@@ -0,0 +1,12 @@
+import { TestBed } from '@angular/core/testing';
+
+import { AdminsService } from './admins.service';
+
+describe('AdminsService', () => {
+  beforeEach(() => TestBed.configureTestingModule({}));
+
+  it('should be created', () => {
+    const service: AdminsService = TestBed.get(AdminsService);
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/shared/services/admins/admins.service.ts b/portal-FE-common/src/app/shared/services/admins/admins.service.ts
new file mode 100644
index 0000000..fe76bd4
--- /dev/null
+++ b/portal-FE-common/src/app/shared/services/admins/admins.service.ts
@@ -0,0 +1,66 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { environment } from 'src/environments/environment';
+import { Observable } from 'rxjs';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class AdminsService {
+
+  constructor(private httpClient: HttpClient) { }
+  apiUrl = environment.api;
+
+  getAccountAdmins(): Observable<any> {
+    return this.httpClient.get(this.apiUrl.accountAdmins);
+  };
+
+  getAdminAppsRoles(orgUserId: string): Observable<any> {
+    let params = new HttpParams().set('user', orgUserId);
+    return this.httpClient.get(this.apiUrl.adminAppsRoles, { params: params });
+  };
+
+
+  getRolesByApp(_appId: any): Observable<any> {
+    return this.httpClient.get(this.apiUrl.adminAppsRoles + '/' + _appId);
+  };
+
+  updateAdminAppsRoles(_newAdminAppRoles: any): Observable<any> {
+    return this.httpClient.put(this.apiUrl.adminAppsRoles, _newAdminAppRoles);
+  };
+
+
+  isComplexPassword(str) {
+    let minLength = 8;
+    let message = 'Password is too simple.  Minimum length is ' + minLength + ', '
+      + 'and it must use letters, digits and special characters.';
+    if (str == null)
+      return message;
+
+    let hasLetter = false;
+    let hasDigit = false;
+    let hasSpecial = false;
+    var code, i, len;
+    for (i = 0, len = str.length; i < len; i++) {
+      code = str.charCodeAt(i);
+      if (code > 47 && code < 58) // numeric (0-9)
+        hasDigit = true;
+      else if ((code > 64 && code < 91) || (code > 96 && code < 123)) // A-Z, a-z
+        hasLetter = true;
+      else
+        hasSpecial = true;
+    } // for
+
+    if (str.length < minLength || !hasLetter || !hasDigit || !hasSpecial)
+      return message;
+
+    // All is well.
+    return null;
+
+  };
+
+  addNewUser(newUser, checkDuplicate): Observable<any> {
+    return this.httpClient.post(this.apiUrl.saveNewUser + '?isCheck=' + checkDuplicate, newUser);
+  };
+
+}
diff --git a/portal-FE-common/src/app/shared/services/applications/applications.service.spec.ts b/portal-FE-common/src/app/shared/services/applications/applications.service.spec.ts
new file mode 100644
index 0000000..6801738
--- /dev/null
+++ b/portal-FE-common/src/app/shared/services/applications/applications.service.spec.ts
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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 { TestBed } from '@angular/core/testing';
+
+import { ApplicationsService } from './applications.service';
+
+describe('ApplicationsService', () => {
+  beforeEach(() => TestBed.configureTestingModule({}));
+
+  it('should be created', () => {
+    const service: ApplicationsService = TestBed.get(ApplicationsService);
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/shared/services/applications/applications.service.ts b/portal-FE-common/src/app/shared/services/applications/applications.service.ts
new file mode 100644
index 0000000..828373e
--- /dev/null
+++ b/portal-FE-common/src/app/shared/services/applications/applications.service.ts
@@ -0,0 +1,172 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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 { Injectable } from '@angular/core';
+import { HttpClient, HttpParams } from '@angular/common/http';
+import { environment } from '../../../../environments/environment';
+import { Observable } from 'rxjs';
+import * as uuid from 'uuid';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class ApplicationsService {
+
+  api = environment.api;
+  resp: string;
+  headerParams = { 'X-Widgets-Type': 'all' };
+
+  constructor(private http: HttpClient) { }
+
+  getOnboardingApps(): Observable<any> {
+    let getOnboardingAppsURL = this.api.onboardingApps;
+    return this.http.get(getOnboardingAppsURL);
+  };
+
+  getSingleAppInfo(appName): Observable<any> {
+    let getSingleAppInfoURL = this.api.singleAppInfo;
+    return this.http.get(getSingleAppInfoURL);
+  };
+
+  getSingleAppInfoById(appId) {
+    let httpParams = new HttpParams()
+      .set('appParam', appId);
+    return this.http.get(this.api.singleAppInfoById, { params: httpParams, responseType: 'json' })
+  }
+
+  getPersUserApps(): Observable<any> {
+    let getPersUserAppsURL = this.api.persUserApps;
+    return this.http.get(getPersUserAppsURL);
+  };
+
+  getAppsOrderBySortPref(userAppSortTypePref): Observable<any> {
+    let getAppsOrderBySortPrefURL = this.api.userAppsOrderBySortPref;
+    return this.http.get(getAppsOrderBySortPrefURL);
+  }
+
+  checkIfUserIsSuperAdmin(): Observable<any> {
+    let checkIfUserIsSuperAdminURL = this.api.checkIfUserIsSuperAdmin;
+    return this.http.get(checkIfUserIsSuperAdminURL);
+  }
+
+  saveAppsSortTypeManual(appsSortManual: any): Observable<any> {
+    let saveAppsSortTypeManualURL = this.api.saveUserAppsSortingManual;
+    return this.http.put(saveAppsSortTypeManualURL, appsSortManual);
+  }
+
+  saveAppsSortTypePreference(appsSortPreference: any): Observable<any> {
+    let saveAppsSortTypePreferenceURL = this.api.saveUserAppsSortingPreference;
+    return this.http.put(saveAppsSortTypePreferenceURL, appsSortPreference);
+  }
+
+  getUserAppsSortTypePreference(): Observable<any> {
+    let getUserAppsSortTypePreferenceURL = this.api.userAppsSortTypePreference;
+    return this.http.get(getUserAppsSortTypePreferenceURL);
+  }
+
+  saveWidgetsSortManual(widgetsSortManual: any): Observable<any> {
+    let saveWidgetsSortManualURL = this.api.saveUserWidgetsSortManual;
+    return this.http.put(saveWidgetsSortManualURL, widgetsSortManual);
+  }
+
+  delWidgetsSortPref(widgetsData: any): Observable<any> {
+    let delWidgetsSortPrefURL = this.api.updateWidgetsSortPref;
+    return this.http.put(delWidgetsSortPrefURL, widgetsData);
+  }
+
+  getAvailableApps(): Observable<any> {
+    let getAvailableAppsURL = this.api.availableApps;
+    return this.http.get(getAvailableAppsURL);
+  }
+
+  getAdminApps(): Observable<any> {
+    let getAdminAppsURL = this.api.adminApps;
+    return this.http.get(getAdminAppsURL);
+  }
+
+  getLeftMenuItems(): Observable<any> {
+    let getLeftMenuItemsURL = this.api.leftmenuItems;
+    return this.http.get(getLeftMenuItemsURL);
+  }
+
+  getAppsForSuperAdminAndAccountAdmin(): Observable<any> {
+    let getAppsForSuperAdminAndAccountAdminURL = this.api.appsForSuperAdminAndAccountAdmin;
+    return this.http.get(getAppsForSuperAdminAndAccountAdminURL);
+  }
+
+  getAdminAppsSimpler(): Observable<any> {
+    let getAdminAppsSimplerURL = this.api.adminApps;
+    return this.http.get(getAdminAppsSimplerURL);
+  }
+
+  addOnboardingApp(newApp: any): Observable<any> {
+    let addOnboardingAppURL = this.api.onboardingApps;
+    return this.http.post(addOnboardingAppURL, newApp);
+  }
+
+  updateOnboardingApp(appData: any): Observable<any> {
+    let updateOnboardingAppURL = this.api.onboardingApps;
+    return this.http.put(updateOnboardingAppURL, appData);
+  }
+
+  saveUserAppsRoles(UserAppRolesRequest: any): Observable<any> {
+    let saveUserAppsRolesURL = this.api.saveUserAppRoles;
+    return this.http.put(saveUserAppsRolesURL, UserAppRolesRequest);
+  }
+
+  deleteOnboardingApp(appId: any): Observable<any> {
+    let deleteOnboardingAppURL = this.api.onboardingApps + '/' + appId;
+    return this.http.delete(deleteOnboardingAppURL);
+  }
+
+  syncRolesEcompFromExtAuthSystem(appId: any): Observable<any> {
+    let syncRolesEcompFromExtAuthSystemURL = this.api.syncRolesFromExternalAuthSystem;
+    return this.http.post(syncRolesEcompFromExtAuthSystemURL, appId);
+  }
+
+  syncFunctionsFromExternalAuthSystem(appId: any): Observable<any> {
+    let syncFunctionsFromExternalAuthSystemURL = this.api.syncFunctionsFromExternalAuthSystem;
+    return this.http.post(syncFunctionsFromExternalAuthSystemURL, appId);
+  }
+
+  ping(appId): Observable<any> {
+    let pingURL = this.api.ping;
+    return this.http.get(pingURL);
+  }
+
+}
diff --git a/portal-FE-common/src/app/shared/services/widget-onboarding/widget-onboarding.service.spec.ts b/portal-FE-common/src/app/shared/services/widget-onboarding/widget-onboarding.service.spec.ts
new file mode 100644
index 0000000..90e5326
--- /dev/null
+++ b/portal-FE-common/src/app/shared/services/widget-onboarding/widget-onboarding.service.spec.ts
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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 { TestBed } from '@angular/core/testing';
+
+import { WidgetOnboardingService } from './widget-onboarding.service';
+
+describe('WidgetOnboardingService', () => {
+  beforeEach(() => TestBed.configureTestingModule({}));
+
+  it('should be created', () => {
+    const service: WidgetOnboardingService = TestBed.get(WidgetOnboardingService);
+    expect(service).toBeTruthy();
+  });
+});
diff --git a/portal-FE-common/src/app/shared/services/widget-onboarding/widget-onboarding.service.ts b/portal-FE-common/src/app/shared/services/widget-onboarding/widget-onboarding.service.ts
new file mode 100644
index 0000000..8d55b27
--- /dev/null
+++ b/portal-FE-common/src/app/shared/services/widget-onboarding/widget-onboarding.service.ts
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START==========================================
+ * ONAP Portal
+ * ===================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ===================================================================
+ *
+ * Unless otherwise specified, all software contained herein is licensed
+ * under the Apache License, Version 2.0 (the "License");
+ * you may not use this software 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.
+ *
+ * Unless otherwise specified, all documentation contained herein is licensed
+ * under the Creative Commons License, Attribution 4.0 Intl. (the "License");
+ * you may not use this documentation except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *             https://creativecommons.org/licenses/by/4.0/
+ *
+ * Unless required by applicable law or agreed to in writing, documentation
+ * 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 { Injectable } from '@angular/core';
+import { HttpClient, HttpParams, HttpHeaders } from '@angular/common/http';
+import { environment } from '../../../../environments/environment';
+import { Observable } from 'rxjs';
+import * as uuid from 'uuid';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class WidgetOnboardingService {
+
+  api = environment.api;
+  resp:string;
+
+  headerParams = {'X-Widgets-Type': 'all', 'X-ECOMP-RequestID': uuid.v4() };
+
+  constructor(private http: HttpClient) { }
+
+  getManagedWidgets(): Observable<any>{ 
+    let getManagedWidgetsURL  = this.api.widgetCommon + '/widgetCatalog';
+    return this.http.get(getManagedWidgetsURL , { withCredentials: true } );
+  }
+
+  deleteWidget(widgetId: any): Observable<any> {
+    let deleteWidgetURL = this.api.widgetCommon + '/widgetCatalog'  + '/' + widgetId;
+    return this.http.delete(deleteWidgetURL , { withCredentials: true } );
+  }
+
+  downloadWidgetFile(widgetId: any): Observable<any> {
+    let downloadWidgetURL = this.api.widgetCommon + '/download/' + widgetId;
+    let httpParam = new  HttpParams()
+    .append('requestType', 'downloadWidgetFile');
+    return this.http.get(downloadWidgetURL,{params: httpParam, responseType: "arraybuffer"});
+  }
+
+  getUploadFlag(): Observable<any> {
+    let getUploadFlagURL = this.api.widgetCommon + '/uploadFlag';
+    return this.http.get(getUploadFlagURL , { withCredentials: true } );
+  }
+
+  updateWidgetWithFile(formData: any, widgetId: any, newWidget: any): Observable<any>{
+    let updateWidgetWithFileURL = this.api.widgetCommon + '/widgetCatalog/' + widgetId;
+    let httpParam = new  HttpParams()
+    .append('newWidget', JSON.stringify(newWidget))
+    .append('requestType', 'fileUpload');
+    return this.http.post(updateWidgetWithFileURL, formData, {params: httpParam, withCredentials: true})
+  }
+
+  updateWidget(widgetId: any, widgetData: any): Observable<any> {
+    let updateWidgetURL = this.api.widgetCommon  + '/widgetCatalog' + '/' + widgetId;
+    return this.http.put(updateWidgetURL, widgetData, { withCredentials: true });
+  }
+
+  createWidget(newWidget: any, formData: any): Observable<any> {
+    let httpParam = new  HttpParams()
+    .append('newWidget', JSON.stringify(newWidget))
+    .append('requestType', 'fileUpload');
+    let  createWidgetURL = this.api.widgetCommon + '/widgetCatalog';
+    return this.http.post(createWidgetURL, formData, {params: httpParam, withCredentials: true})
+  }
+
+  populateAvailableApps(): Observable<any> {
+    let  populateAvailableAppsURL = this.api.appsForSuperAdminAndAccountAdmin;
+    return this.http.get(populateAvailableAppsURL, { withCredentials: true })
+  }
+}