[CORD-2810] Prevent the user from inserting values in read_only fields

Change-Id: I00460544dd7d36f8482b04ea89912005a108f6b8
diff --git a/src/app/core/field/field.html b/src/app/core/field/field.html
index a91665d..5189ea5 100644
--- a/src/app/core/field/field.html
+++ b/src/app/core/field/field.html
@@ -18,99 +18,107 @@
 <label ng-if="vm.field.type !== 'object' && vm.field.type !== 'array'">
     {{vm.field.label}}
     <span class="required" ng-if="vm.field.validators.required">*</span>
+    <span class="read-only" ng-if="vm.field.read_only">read only</span>
 </label>
-<input
-        xos-custom-validator custom-validator="vm.field.validators.custom || null"
-        ng-if="vm.field.type !== 'boolean' && vm.field.type !== 'object' && vm.field.type !== 'select' && vm.field.type !== 'array'"
-        type="{{vm.field.type}}"
-        name="{{vm.name}}"
-        class="form-control"
-        ng-model="vm.ngModel"
-        ng-minlength="vm.field.validators.minlength || 0"
-        ng-maxlength="vm.field.validators.maxlength || 2000"
-        ng-required="vm.field.validators.required || false" />
-<select class="form-control" ng-if="vm.field.type === 'select'"
-        name = "{{vm.name}}"
-        ng-options="item.id as item.label for item in vm.field.options"
-        ng-model="vm.ngModel"
-        ng-required="vm.field.validators.required || false">
-</select>
-<span class="boolean-field" ng-if="vm.field.type === 'boolean'">
-        <a
-           class="btn btn-success"
-           ng-show="vm.ngModel"
-           ng-click="vm.ngModel = false">
-          <i class="fa fa-check"></i>
-        </a>
-        <a
-           class="btn btn-danger"
-           ng-show="!vm.ngModel"
-           ng-click="vm.ngModel = true">
-          <i class="fa fa-remove"></i>
-        </a>
-      </span>
-<div
-        class="panel panel-default object-field"
-        ng-if="vm.field.type == 'object' && (!vm.isEmptyObject(vm.ngModel) || !vm.isEmptyObject(vm.field.properties))"
->
-    <div class="panel-heading">{{vm.field.label}}</div>
-    <div class="panel-body">
-        <div ng-if="!vm.field.properties" ng-repeat="(k, v) in vm.ngModel">
-            <xos-field
-                    name="k"
-                    field="{label: vm.formatLabel(k), type: vm.getType(v)}"
-                    ng-model="v">
-            </xos-field>
-        </div>
-        <div ng-if="vm.field.properties" ng-repeat="(k, v) in vm.field.properties">
-            <xos-field
-                    name="k"
-                    field="{
-                label: v.label || vm.formatLabel(k),
-                type: v.type,
-                validators: v.validators
-              }"
-                    ng-model="vm.ngModel[k]">
-            </xos-field>
-        </div>
-    </div>
+
+<div ng-if="vm.field.read_only">
+    <pre class="form-control" readonly>{{vm.ngModel || ""}}</pre>
 </div>
-<div
-        class="panel panel-default array-field"
-        ng-if="vm.field.type == 'array'">
-    <div class="panel-heading">{{vm.field.label}}</div>
-    <div class="panel-body selected">
-        <ul class="draggable" dnd-list="vm.ngModel">
-            <li
-                    class="array-element"
-                    ng-repeat="item in vm.ngModel"
-                    dnd-draggable="item"
-                    dnd-moved="vm.ngModel.splice($index, 1)"
-                    dnd-effect-allowed="move"
-                    dnd-selected="models.selected = item"
-            >
-                <div class="well well-sm text-center">
-                    {{item}}
-                </div>
-            </li>
-            <div class="clearfix"></div>
-        </ul>
+
+<div ng-if="!vm.field.read_only">
+    <input
+            xos-custom-validator custom-validator="vm.field.validators.custom || null"
+            ng-if="vm.field.type !== 'boolean' && vm.field.type !== 'object' && vm.field.type !== 'select' && vm.field.type !== 'array'"
+            type="{{vm.field.type}}"
+            name="{{vm.name}}"
+            class="form-control"
+            ng-model="vm.ngModel"
+            ng-minlength="vm.field.validators.minlength || 0"
+            ng-maxlength="vm.field.validators.maxlength || 2000"
+            ng-required="vm.field.validators.required || false" />
+    <select class="form-control" ng-if="vm.field.type === 'select'"
+            name = "{{vm.name}}"
+            ng-options="item.id as item.label for item in vm.field.options"
+            ng-model="vm.ngModel"
+            ng-required="vm.field.validators.required || false">
+    </select>
+    <span class="boolean-field" ng-if="vm.field.type === 'boolean'">
+            <a
+               class="btn btn-success"
+               ng-show="vm.ngModel"
+               ng-click="vm.ngModel = false">
+              <i class="fa fa-check"></i>
+            </a>
+            <a
+               class="btn btn-danger"
+               ng-show="!vm.ngModel"
+               ng-click="vm.ngModel = true">
+              <i class="fa fa-remove"></i>
+            </a>
+          </span>
+    <div
+            class="panel panel-default object-field"
+            ng-if="vm.field.type == 'object' && (!vm.isEmptyObject(vm.ngModel) || !vm.isEmptyObject(vm.field.properties))"
+    >
+        <div class="panel-heading">{{vm.field.label}}</div>
+        <div class="panel-body">
+            <div ng-if="!vm.field.properties" ng-repeat="(k, v) in vm.ngModel">
+                <xos-field
+                        name="k"
+                        field="{label: vm.formatLabel(k), type: vm.getType(v)}"
+                        ng-model="v">
+                </xos-field>
+            </div>
+            <div ng-if="vm.field.properties" ng-repeat="(k, v) in vm.field.properties">
+                <xos-field
+                        name="k"
+                        field="{
+                    label: v.label || vm.formatLabel(k),
+                    type: v.type,
+                    validators: v.validators
+                  }"
+                        ng-model="vm.ngModel[k]">
+                </xos-field>
+            </div>
+        </div>
     </div>
-    <div class="panel-body unselected">
-        <ul class="draggable" dnd-list="vm.field.availableOptions">
-            <li
-                    class="array-element"
-                    ng-repeat="item in vm.field.availableOptions"
-                    dnd-draggable="item"
-                    dnd-moved="vm.field.availableOptions.splice($index, 1)"
-                    dnd-effect-allowed="move"
-                    dnd-selected="models.selected = item"
-            >
-                <div class="well well-sm text-center">
-                    {{item}}
-                </div>
-            </li>
-            <div class="clearfix"></div>
-        </ul>
+    <div
+            class="panel panel-default array-field"
+            ng-if="vm.field.type == 'array'">
+        <div class="panel-heading">{{vm.field.label}}</div>
+        <div class="panel-body selected">
+            <ul class="draggable" dnd-list="vm.ngModel">
+                <li
+                        class="array-element"
+                        ng-repeat="item in vm.ngModel"
+                        dnd-draggable="item"
+                        dnd-moved="vm.ngModel.splice($index, 1)"
+                        dnd-effect-allowed="move"
+                        dnd-selected="models.selected = item"
+                >
+                    <div class="well well-sm text-center">
+                        {{item}}
+                    </div>
+                </li>
+                <div class="clearfix"></div>
+            </ul>
+        </div>
+        <div class="panel-body unselected">
+            <ul class="draggable" dnd-list="vm.field.availableOptions">
+                <li
+                        class="array-element"
+                        ng-repeat="item in vm.field.availableOptions"
+                        dnd-draggable="item"
+                        dnd-moved="vm.field.availableOptions.splice($index, 1)"
+                        dnd-effect-allowed="move"
+                        dnd-selected="models.selected = item"
+                >
+                    <div class="well well-sm text-center">
+                        {{item}}
+                    </div>
+                </li>
+                <div class="clearfix"></div>
+            </ul>
+        </div>
     </div>
 </div>
\ No newline at end of file
diff --git a/src/app/core/field/field.scss b/src/app/core/field/field.scss
index 7bdb659..b86c95f 100644
--- a/src/app/core/field/field.scss
+++ b/src/app/core/field/field.scss
@@ -18,6 +18,12 @@
 
 @import "../../style/vars";
 
-span.required {
+span.required,
+span.read-only {
   color: $color-accent;
+}
+
+span.read-only {
+  vertical-align: super;
+  font-size: 70%;
 }
\ No newline at end of file
diff --git a/src/app/core/form/form.spec.ts b/src/app/core/form/form.spec.ts
index b010907..1be9a43 100644
--- a/src/app/core/form/form.spec.ts
+++ b/src/app/core/form/form.spec.ts
@@ -102,13 +102,15 @@
           name: 'id',
           label: 'Id:',
           type: 'number',
-          validators: {}
+          validators: {},
+          read_only: false
         },
         {
           name: 'first_name',
           label: 'Name:',
           type: 'text',
-          validators: {}
+          validators: {},
+          read_only: false
         },
         {
           name: 'email',
@@ -116,19 +118,22 @@
           type: 'email',
           validators: {
             required: true
-          }
+          },
+          read_only: false
         },
         {
           name: 'birthDate',
           label: 'DOB:',
           type: 'date',
-          validators: {}
+          validators: {},
+          read_only: false
         },
         {
           name: 'enabled',
           label: 'Status:',
           type: 'boolean',
-          validators: {}
+          validators: {},
+          read_only: false
         },
         {
           name: 'role',
@@ -138,7 +143,8 @@
             {id: 1, label: 'user'},
             {id: 2, label: 'admin'}
           ],
-          validators: {}
+          validators: {},
+          read_only: false
         }
       ];
 
diff --git a/src/app/core/form/form.ts b/src/app/core/form/form.ts
index 8faa2bd..1c10c33 100644
--- a/src/app/core/form/form.ts
+++ b/src/app/core/form/form.ts
@@ -56,6 +56,7 @@
   name: string;
   label: string;
   type: string; // options are: [date, boolean, number, email, string, select],
+  read_only: boolean;
   hint?: string;
   validators: IXosFormInputValidator;
   options?: IXosFormInputOptions[];
diff --git a/src/app/core/services/helpers/config.helpers.spec.ts b/src/app/core/services/helpers/config.helpers.spec.ts
index db1c840..9e7318e 100644
--- a/src/app/core/services/helpers/config.helpers.spec.ts
+++ b/src/app/core/services/helpers/config.helpers.spec.ts
@@ -36,7 +36,8 @@
     {
       type: 'number',
       name: 'id',
-      validators: []
+      validators: [],
+      read_only: false
     },
     {
       type: 'string',
@@ -46,7 +47,8 @@
           bool_value: true,
           name: 'required'
         }
-      ]
+      ],
+      read_only: false
     },
     {
       type: 'string',
@@ -56,7 +58,8 @@
           int_value: 30,
           name: 'maxlength'
         }
-      ]
+      ],
+      read_only: false
     },
     {
       type: 'number',
@@ -70,12 +73,14 @@
           int_value: 40,
           name: 'max'
         }
-      ]
+      ],
+      read_only: false
     },
     {
       type: 'date',
       name: 'updated',
-      validators: []
+      validators: [],
+      read_only: false
     },
   ],
   description: '',
@@ -255,13 +260,15 @@
           type: 'boolean',
           name: 'active',
           default: '"True"',
-          validators: []
+          validators: [],
+          read_only: false
         },
         {
           type: 'boolean',
           name: 'disabled',
           default: '"False"',
-          validators: []
+          validators: [],
+          read_only: false
         },
       ];
       const form_fields = service.modelFieldToInputCfg(fields);
@@ -312,7 +319,8 @@
       relation: {
         model: 'Test',
         type: 'many_to_one'
-      }
+      },
+      read_only: false
     };
 
     describe('the populateRelated method', () => {
@@ -332,7 +340,8 @@
         name: 'test',
         label: 'Test',
         type: 'select',
-        validators: {}
+        validators: {},
+        read_only: false
       };
 
       it('should add the available choice to the select', () => {
@@ -358,7 +367,8 @@
             {
               type: 'number',
               name: 'foo',
-              validators: []
+              validators: [],
+              read_only: false
             },
             {
               type: 'string',
@@ -368,7 +378,8 @@
                   bool_value: true,
                   name: 'required'
                 }
-              ]
+              ],
+              read_only: false
             }
           ],
           description: '',
diff --git a/src/app/core/services/helpers/config.helpers.ts b/src/app/core/services/helpers/config.helpers.ts
index 4baf9c5..443b6c2 100644
--- a/src/app/core/services/helpers/config.helpers.ts
+++ b/src/app/core/services/helpers/config.helpers.ts
@@ -34,6 +34,7 @@
 export interface IXosModelDefsField {
   name: string;
   type: string;
+  read_only: boolean;
   validators?: IXosModelDefsFieldValidators[];
   hint?: string;
   relation?: {
@@ -271,7 +272,8 @@
         type: f.type,
         validators: this.formatValidators(f.validators),
         hint: f.hint,
-        default: this.formatDefaultValues(f.default)
+        default: this.formatDefaultValues(f.default),
+        read_only: f.read_only
       };
 
       // NOTE populate drop-downs based on relation
diff --git a/src/app/datasources/helpers/model-discoverer.service.ts b/src/app/datasources/helpers/model-discoverer.service.ts
index 0addc05..fca593c 100644
--- a/src/app/datasources/helpers/model-discoverer.service.ts
+++ b/src/app/datasources/helpers/model-discoverer.service.ts
@@ -19,11 +19,11 @@
 // TODO test me hard!!!
 
 import * as _ from 'lodash';
-import {IXosModeldefsService, IXosModeldef, IXosModelDefsField, IXosModelDefsRelation} from '../rest/modeldefs.rest';
+import {IXosModeldefsService, IXosModeldef, IXosModelDefsRelation} from '../rest/modeldefs.rest';
 import {IXosTableCfg} from '../../core/table/table';
 import {IXosFormCfg} from '../../core/form/form';
 import {IXosNavigationService} from '../../core/services/navigation';
-import {IXosConfigHelpersService} from '../../core/services/helpers/config.helpers';
+import {IXosConfigHelpersService, IXosModelDefsField} from '../../core/services/helpers/config.helpers';
 import {IXosRuntimeStatesService, IXosState} from '../../core/services/runtime-states';
 import {IXosModelStoreService} from '../stores/model.store';
 import {IXosAuthService} from '../rest/auth.rest';
diff --git a/src/app/datasources/helpers/model.discoverer.service.spec.ts b/src/app/datasources/helpers/model.discoverer.service.spec.ts
index 8011442..a6e6340 100644
--- a/src/app/datasources/helpers/model.discoverer.service.spec.ts
+++ b/src/app/datasources/helpers/model.discoverer.service.spec.ts
@@ -26,8 +26,8 @@
 const stubModels: IXosModeldef[] = [
   {
     fields: [
-      {name: 'id', type: 'number'},
-      {name: 'foo', type: 'string'}
+      {name: 'id', type: 'number', read_only: false},
+      {name: 'foo', type: 'string', read_only: false}
     ],
     relations: [],
     name: 'Node',
@@ -37,8 +37,8 @@
   },
   {
     fields: [
-      {name: 'id', type: 'number'},
-      {name: 'bar', type: 'string'}
+      {name: 'id', type: 'number', read_only: false},
+      {name: 'bar', type: 'string', read_only: false}
     ],
     relations: [],
     name: 'VSGTenant',
diff --git a/src/app/datasources/helpers/modeldefs.service.spec.ts b/src/app/datasources/helpers/modeldefs.service.spec.ts
index 9d5660d..e0f37a6 100644
--- a/src/app/datasources/helpers/modeldefs.service.spec.ts
+++ b/src/app/datasources/helpers/modeldefs.service.spec.ts
@@ -43,8 +43,8 @@
 
     const modelDef: IXosModel = {
       fields: [
-        {name: 'id', type: 'number'},
-        {name: 'foo', type: 'string'}
+        {name: 'id', type: 'number', read_only: false},
+        {name: 'foo', type: 'string', read_only: false}
       ],
       relations: [],
       name: 'Node',
diff --git a/src/app/datasources/rest/modeldefs.rest.ts b/src/app/datasources/rest/modeldefs.rest.ts
index 300b71b..60fc917 100644
--- a/src/app/datasources/rest/modeldefs.rest.ts
+++ b/src/app/datasources/rest/modeldefs.rest.ts
@@ -19,18 +19,6 @@
 import {IXosModelDefsField} from '../../core/services/helpers/config.helpers';
 import {IXosAppConfig} from '../../../index';
 
-// Models interfaces
-export interface IXosModelDefsField {
-  name: string;
-  type: string;
-  validators?: any;
-  hint?: string;
-  relation?: {
-    model: string;
-    type: string;
-  };
-}
-
 export interface IXosModelDefsRelation {
   model: string; // model name
   type: string; // relation type
diff --git a/src/app/views/crud/crud.html b/src/app/views/crud/crud.html
index 596add8..c586bca 100644
--- a/src/app/views/crud/crud.html
+++ b/src/app/views/crud/crud.html
@@ -49,7 +49,7 @@
                 <xos-form ng-model="vm.model" config="vm.formCfg"></xos-form>
             </div>
         </uib-tab>
-        <uib-tab ng-if="vm.debugTab" heading="Debug">
+        <uib-tab ng-if="vm.debugTab && vm.model.id" heading="Debug">
             <div class="panel-body">
                 <xos-debug-model ng-model="vm.model"></xos-debug-model>
             </div>