Adding relations links to table

Change-Id: I5dd2d206a6da7fec163342f54f1143a7783f7758
diff --git a/src/app/core/nav/nav.spec.ts b/src/app/core/nav/nav.spec.ts
index 3c18e4d..28cd3e1 100644
--- a/src/app/core/nav/nav.spec.ts
+++ b/src/app/core/nav/nav.spec.ts
@@ -19,7 +19,9 @@
 };
 
 const AuthMock = {
-  logout: jasmine.createSpy('logout')
+  logout: jasmine.createSpy('logout').and.returnValue({then: () => {
+    return;
+  }})
 };
 
 describe('Nav component', () => {
diff --git a/src/app/core/nav/nav.ts b/src/app/core/nav/nav.ts
index 31831e1..6c26635 100644
--- a/src/app/core/nav/nav.ts
+++ b/src/app/core/nav/nav.ts
@@ -56,7 +56,10 @@
   }
 
   logout() {
-    this.authService.logout();
+    this.authService.logout()
+      .then(() => {
+        this.$state.go('login');
+      });
   }
 }
 
diff --git a/src/app/core/services/helpers/config.helpers.spec.ts b/src/app/core/services/helpers/config.helpers.spec.ts
index b39f444..28ba22b 100644
--- a/src/app/core/services/helpers/config.helpers.spec.ts
+++ b/src/app/core/services/helpers/config.helpers.spec.ts
@@ -57,6 +57,9 @@
         getUser: () => {
           return {id: 1};
         }
+      })
+      .value('ModelStore', {
+
       });
     angular.mock.module('test');
   });
@@ -182,10 +185,5 @@
       expect(config.inputs.length).toBe(4);
     });
   });
-
-  it('should convert a core model name in an URL', () => {
-    expect(service.urlFromCoreModel('Slice')).toBe('/core/slices');
-    expect(service.urlFromCoreModel('Xos')).toBe('/core/xosses');
-  });
 });
 
diff --git a/src/app/core/services/helpers/config.helpers.ts b/src/app/core/services/helpers/config.helpers.ts
index 1df2662..b2d966a 100644
--- a/src/app/core/services/helpers/config.helpers.ts
+++ b/src/app/core/services/helpers/config.helpers.ts
@@ -4,11 +4,17 @@
 import {IModeldef} from '../../../datasources/rest/modeldefs.rest';
 import {IXosFormConfig, IXosFormInput} from '../../form/form';
 import {IXosAuthService} from '../../../datasources/rest/auth.rest';
+import {IModelStoreService} from '../../../datasources/stores/model.store';
 
 export interface IXosModelDefsField {
   name: string;
   type: string;
   validators?: any;
+  hint?: string;
+  relation?: {
+    model: string;
+    type: string;
+  };
 }
 
 export interface IXosConfigHelpersService {
@@ -20,10 +26,10 @@
   pluralize(string: string, quantity?: number, count?: boolean): string;
   toLabel(string: string, pluralize?: boolean): string;
   toLabels(string: string[], pluralize?: boolean): string[];
-  urlFromCoreModel(name: string): string;
 }
 
 export class ConfigHelpers {
+  static $inject = ['toastr', 'AuthService', 'ModelStore'];
 
   excluded_fields = [
     'created',
@@ -46,7 +52,8 @@
 
   constructor(
     private toastr: ng.toastr.IToastrService,
-    private AuthService: IXosAuthService
+    private AuthService: IXosAuthService,
+    private ModelStore: IModelStoreService
   ) {
     pluralize.addIrregularRule('xos', 'xosses');
     pluralize.addPluralRule(/slice$/i, 'slices');
@@ -125,6 +132,17 @@
         col.link = item => `#/core${baseUrl.replace(':id?', item.id)}`;
       }
 
+      // if the field identify a relation, create a link
+      if (f.relation && f.relation.type === 'many_to_one') {
+        // TODO read the related model name and replace the value, use the xosTable format method?
+        col.type = 'custom';
+        col.formatter = item => {
+          this.populateRelated(item, item[f.name], f);
+          return item[f.name];
+        };
+        col.link = item => `#${this.urlFromCoreModel(f.relation.model)}/${item[f.name]}`;
+      }
+
       if (f.name === 'backend_status') {
         col.type = 'icon';
         col.formatter = (item) => {
@@ -214,4 +232,20 @@
   private capitalizeFirst(string: string): string {
     return string.slice(0, 1).toUpperCase() + string.slice(1);
   }
+
+  private populateRelated(item: any, fk: string, field: IXosModelDefsField): any {
+    // if the relation is not defined return
+    if (!fk || angular.isUndefined(fk) || fk === null) {
+      return;
+    }
+    this.ModelStore.query(field.relation.model)
+      .subscribe(res => {
+        if (angular.isDefined(res) && angular.isDefined(fk)) {
+          let ri = _.find(res, {id: fk});
+          if (angular.isDefined(ri)) {
+            item[`${field.name}-formatted`] = angular.isDefined(ri.name) ? ri.name : ri.humanReadableName;
+          }
+        }
+      });
+  }
 }
diff --git a/src/app/core/table/table.html b/src/app/core/table/table.html
index 382dbec..788e84c 100644
--- a/src/app/core/table/table.html
+++ b/src/app/core/table/table.html
@@ -71,7 +71,8 @@
                     </dl>
                   </span>
                 <span ng-if="col.type === 'custom'">
-                    {{col.formatter(item)}}
+                    {{item[col.prop + '-formatted']}}
+                    <i class="ng-hide">{{col.formatter(item)}}</i>
                   </span>
                 <span ng-if="col.type === 'icon'">
                     <i class="fa fa-{{col.formatter(item)}}">