[CORD-2827] Fixed unauthorized error handling

Change-Id: I6ddef7f869c17db4d8479f23f6e8734f6002d8fc
diff --git a/src/app/datasources/helpers/model-discoverer.service.ts b/src/app/datasources/helpers/model-discoverer.service.ts
index fca593c..3ee5a15 100644
--- a/src/app/datasources/helpers/model-discoverer.service.ts
+++ b/src/app/datasources/helpers/model-discoverer.service.ts
@@ -44,7 +44,7 @@
 
 // Service
 export interface IXosModelDiscovererService {
-  discover(): ng.IPromise<boolean>;
+  discover(): ng.IPromise<string>;
   getApiUrlFromModel(model: IXosModel): string;
   areModelsLoaded(): boolean;
 }
@@ -127,28 +127,42 @@
               return this.$q.resolve('true');
             })
             .catch(err => {
-              this.$log.error(`[XosModelDiscovererService] Model ${model.name} NOT stored`, err);
+              this.$log.warn(`[XosModelDiscovererService] Model ${model.name} NOT stored`, err);
+              const isAuthError = this.AuthService.isAuthError(err);
+              if (isAuthError) {
+                this.$log.warn(`[XosModelDiscovererService] User is not authentincated`);
+                return this.$q.reject(err);
+              }
               return this.$q.resolve('false');
             });
             pArray.push(p);
         });
+
+
         this.$q.all(pArray)
           .then((res) => {
-            // the Model Loader promise won't ever be reject, in case it will be resolve with value false,
+            // the ModelLoader promise won't ever be reject, in case it will be resolve with value false,
             // that's because we want to wait anyway for all the models to be loaded
             if (res.indexOf('false') > -1) {
               return d.resolve(false);
             }
             d.resolve(true);
+            this.modelsLoaded = true;
           })
           .catch((e) => {
-            this.$log.error(`[XosModelDiscovererService]`, e);
-            d.resolve(false);
+            this.XosModelStore.clean(); // reset all the observable otherwise they'll store login errors
+            this.$log.warn(`[XosModelDiscovererService]`, e);
+            // the ModelLoader promise will be rejected in case of authentication error
+            d.reject(e);
           })
           .finally(() => {
             this.progressBar.complete();
-            this.modelsLoaded = true;
           });
+      })
+      .catch(err => {
+        this.progressBar.complete();
+        this.$log.error(`[XosModelDiscovererService] Cannot load model defs`, err);
+        return d.resolve('chameleon');
       });
     return d.promise;
   }
@@ -249,16 +263,17 @@
   }
 
   private cacheModelEntries(model: IXosModel): ng.IPromise<IXosModel> {
+
     const d = this.$q.defer();
 
     const apiUrl = this.getApiUrlFromModel(model);
     this.XosModelStore.query(model.name, apiUrl)
+      .skip(1) // NOTE observables returns as first an empty array, so skip it
       .subscribe(
         () => {
           return d.resolve(model);
         },
         err => {
-          this.AuthService.handleUnauthenticatedRequest(err);
           return d.reject(err);
         }
       );
diff --git a/src/app/datasources/helpers/model.discoverer.service.spec.ts b/src/app/datasources/helpers/model.discoverer.service.spec.ts
index a6e6340..5deb444 100644
--- a/src/app/datasources/helpers/model.discoverer.service.spec.ts
+++ b/src/app/datasources/helpers/model.discoverer.service.spec.ts
@@ -73,9 +73,13 @@
 };
 const MockXosModelStore = {
   query: jasmine.createSpy('modelStore.query')
-    .and.callFake(() => {
+    .and.callFake((model) => {
       const list = new BehaviorSubject([]);
       list.next([]);
+      window.setTimeout(() => {
+        // NOTE force the Observable to call next two times, as cacheModelEntries is not caching the first result (since it's generated)
+        list.next([]);
+      }, 100);
       return list.asObservable();
     })
 };
@@ -165,6 +169,22 @@
     expect(service['getParentStateFromModel']({name: 'Tenant', app: 'services.vsg'})).toBe('xos.vsg');
   });
 
+  it('should add an item to navigation', () => {
+    service['addNavItem']({name: 'Tenant', app: 'services.vsg'});
+    expect(MockXosNavigationService.add).toHaveBeenCalledWith({
+      label: 'Tenants',
+      state: 'xos.vsg.tenant',
+      parent: 'xos.vsg'
+    });
+
+    service['addNavItem']({name: 'Tenant', verbose_name: 'Verbose', app: 'services.vsg'});
+    expect(MockXosNavigationService.add).toHaveBeenCalledWith({
+      label: 'Verboses',
+      state: 'xos.vsg.tenant',
+      parent: 'xos.vsg'
+    });
+  });
+
   it('should add a new service entry in the system', () => {
     service['addService']({name: 'Tenant', app: 'services.vsg'});
     expect(MockXosRuntimeStates.addState).toHaveBeenCalledWith('xos.vsg', {
@@ -226,22 +246,6 @@
     scope.$apply();
   });
 
-  it('should add an item to navigation', () => {
-    service['addNavItem']({name: 'Tenant', app: 'services.vsg'});
-    expect(MockXosNavigationService.add).toHaveBeenCalledWith({
-      label: 'Tenants',
-      state: 'xos.vsg.tenant',
-      parent: 'xos.vsg'
-    });
-
-    service['addNavItem']({name: 'Tenant', verbose_name: 'Verbose', app: 'services.vsg'});
-    expect(MockXosNavigationService.add).toHaveBeenCalledWith({
-      label: 'Verboses',
-      state: 'xos.vsg.tenant',
-      parent: 'xos.vsg'
-    });
-  });
-
   it('should cache a model', () => {
     service['cacheModelEntries']({name: 'Tenant', app: 'services.vsg'});
     expect(MockXosModelStore.query).toHaveBeenCalledWith('Tenant', '/vsg/tenants');
@@ -276,7 +280,7 @@
       service.discover()
         .then((res) => {
           expect(MockProgressBar.start).toHaveBeenCalled();
-          expect(MockXosModelDefs.get).toHaveBeenCalled(); // FIXME replace correct spy
+          expect(MockXosModelDefs.get).toHaveBeenCalled();
           expect(service['cacheModelEntries'].calls.count()).toBe(2);
           expect(service['addState'].calls.count()).toBe(2);
           expect(service['addNavItem'].calls.count()).toBe(2);
@@ -287,6 +291,10 @@
           done();
         });
       scope.$apply();
+      window.setTimeout(() => {
+        // resolve promises after the observable.next as been called twice (cacheModelEntries requires it)
+        scope.$apply();
+      }, 101);
     });
   });
 });