Added instructions and tests

Change-Id: I18e491c4a0c188866dcad1f9db52c4f781054e62
diff --git a/views/ngXosViews/UITutorial/src/css/main.css b/views/ngXosViews/UITutorial/src/css/main.css
index 3684934..d008cb5 100644
--- a/views/ngXosViews/UITutorial/src/css/main.css
+++ b/views/ngXosViews/UITutorial/src/css/main.css
@@ -7,14 +7,19 @@
       height: 400px;
       background: #002b36;
       padding: 10px;
-      overflow: scroll; }
+      overflow: scroll;
+      color: #93a1a1; }
       #xosUITutorial js-shell #shell-panel .prompt {
         color: #d33682; }
+      #xosUITutorial js-shell #shell-panel .input {
+        color: #268bd2; }
       #xosUITutorial js-shell #shell-panel .cursor {
         color: #2aa198; }
       #xosUITutorial js-shell #shell-panel .error {
         color: #dc322f; }
-      #xosUITutorial js-shell #shell-panel #shell-view {
-        color: #93a1a1; }
-        #xosUITutorial js-shell #shell-panel #shell-view > div > div {
-          color: #268bd2; }
+      #xosUITutorial js-shell #shell-panel #shell-view > div > div {
+        color: #268bd2; }
+      #xosUITutorial js-shell #shell-panel #shell-view .jsonObject {
+        white-space: nowrap; }
+      #xosUITutorial js-shell #shell-panel #shell-view .jsonCollection > .jsonObject {
+        margin-left: 10px; }
diff --git a/views/ngXosViews/UITutorial/src/index.html b/views/ngXosViews/UITutorial/src/index.html
index 6819b46..d325f9c 100644
--- a/views/ngXosViews/UITutorial/src/index.html
+++ b/views/ngXosViews/UITutorial/src/index.html
@@ -39,6 +39,7 @@
 <!-- inject:js -->
 <script src="/vendor/ng-xos-lib/dist/ngXosHelpers.min.js"></script>
 <script src="/.tmp/main.js"></script>
+<script src="/.tmp/templateHandler.js"></script>
 <script src="/.tmp/responseHandler.js"></script>
 <script src="/.tmp/exploreCmd.js"></script>
 <script src="/.tmp/errorHandler.js"></script>
diff --git a/views/ngXosViews/UITutorial/src/js/errorHandler.js b/views/ngXosViews/UITutorial/src/js/errorHandler.js
index dbe41f4..aa75d38 100644
--- a/views/ngXosViews/UITutorial/src/js/errorHandler.js
+++ b/views/ngXosViews/UITutorial/src/js/errorHandler.js
@@ -1,10 +1,9 @@
 (function () {
   'use strict';
   angular.module('xos.UITutorial')
-  .service('ErrorHandler', function(){
+  .service('ErrorHandler', function(TemplateHandler){
     this.print = (msg, done) => {
-      const errorTpl = _.template(`<span class="error">[ERROR] <%= msg %></span>`);
-      done(errorTpl({msg: msg}));
+      done(TemplateHandler.error({msg: msg}));
     };
   });
 })();
\ No newline at end of file
diff --git a/views/ngXosViews/UITutorial/src/js/exploreCmd.js b/views/ngXosViews/UITutorial/src/js/exploreCmd.js
index d4bba90..0aba8fa 100644
--- a/views/ngXosViews/UITutorial/src/js/exploreCmd.js
+++ b/views/ngXosViews/UITutorial/src/js/exploreCmd.js
@@ -3,36 +3,41 @@
   angular.module('xos.UITutorial')
   .service('ExploreCmd', function($injector, ResponseHandler, ErrorHandler){
 
-    this.setup = (shell) => {
-      shell.setCommandHandler('resource', {
-        exec: (cmd, args, done) => {
-          switch(args[0]){
-            case 'list':
-              return listAvailableResources(done);
-              break;
-            default:
-              // use the resource
-              const resourceName = args.shift();
-              const method = args.shift();
-              return consumeResource(resourceName, method, args, done);
-          }
-        },
-        completion: function(cmd, arg, line, callback) {
-          const args = ['list'].concat(getAvailableResources());
-          if(line.text.match(/resource\s[A-Z][a-z]+\s/)){
-            // if arg is a resource, then return available methods
-            if(args.indexOf(arg) !== -1){
-              arg = '';
-            }
-            const methods = ['query', 'get', 'save', '$save', 'delete'];
-            return callback(shell.bestMatch(arg, methods));
-          }
-          return callback(shell.bestMatch(arg, args));
+    this.resourceExec = (cmd, args, done) => {
+      switch(args[0]){
+        case 'list':
+          return this.listAvailableResources(done);
+          break;
+        default:
+          // use the resource
+          const resourceName = args.shift();
+          const method = args.shift();
+          return this.consumeResource(resourceName, method, args, done);
+      }
+    };
+
+    this.resourceCompletion = (cmd, arg, line, done) => {
+      const args = ['list'].concat(this.getAvailableResources());
+      if(line.text.match(/resource\s[A-Z][a-z]+\s/)){
+        // if arg is a resource, then return available methods
+        if(args.indexOf(arg) !== -1){
+          arg = '';
         }
+        const methods = ['query', 'get', 'save', '$save', 'delete'];
+        return done(this.shell.bestMatch(arg, methods));
+      }
+      return done(this.shell.bestMatch(arg, args));
+    };
+
+    this.setup = (shell) => {
+      this.shell = shell;
+      shell.setCommandHandler('resource', {
+        exec: this.resourceExec,
+        completion: this.resourceCompletion
       });
     };
 
-    const getAvailableResources = () => {
+    this.getAvailableResources = () => {
       return angular.module('xos.helpers')._invokeQueue
           .filter((d) => {
             if(d[1] !== 'service'){
@@ -44,17 +49,18 @@
           .reduce((list, i) => list.concat([i[2][0]]), []);
     }
 
-    const listAvailableResources = (done) => {
-      const resources = getAvailableResources()
+    this.listAvailableResources = (done) => {
+      // TODO use a template
+      const resources = this.getAvailableResources()
           .reduce((html, i) => `${html}${i}<br/>`, '');
       done(resources);
     }
 
-    const consumeResource = (resourceName, method, args, done) => {
+    this.consumeResource = (resourceName, method, args, done) => {
 
       // TODO if not resourceName/method print cmd instructions
 
-      if(getAvailableResources().indexOf(resourceName) === -1){
+      if(this.getAvailableResources().indexOf(resourceName) === -1){
         return ErrorHandler.print(`Resource "${resourceName}" does not exists`, done);
       }
 
@@ -62,36 +68,43 @@
         return ErrorHandler.print(`Method "${method}" not allowed`, done);
       }
 
+      // TODO @Teo if get/delete check for arguments
+      let params = {};
+
+      // if the method require arguments checks for them
+      if(['get', '$save', 'delete'].indexOf(method) !== -1){
+        if(args.length === 0){
+          return ErrorHandler.print(`Method "${method}" require parameters`, done);
+        }
+      }
+
+      // if there are arguments parse them
+      // TODO wrap in a try catch, we have no guarantee that a user insert the correct params
+      if(args.length > 0){
+        params = eval(`(${args[0]})`);
+      }
+
+      // if it is a query is not possible to use id as parameter
+      if(method === 'query' && angular.isDefined(params.id)){
+        return ErrorHandler.print(`Is not possible to use "id" as filter in method "${method}", use "get" instead!`, done);
+      }
+
       let Resource;
       try{
         Resource = $injector.get(resourceName);
-
-        // TODO @Teo if get/delete check for arguments
-        let params = {};
-
-        // if the method require arguments checks for them
-        if(['get', '$save', 'delete'].indexOf(method) !== -1){
-          if(args.length === 0){
-            return ErrorHandler.print(`Method "${method}" require parameters`, done);
-          }
-        }
-
-        // if there are arguments parse them
-        if(args.length > 0){
-          params = eval(`(${args[0]})`);
-        }
-
-        // if it is a query is not possible to use id as parameter
-        if(method === 'query' && angular.isDefined(params.id)){
-          return ErrorHandler.print(`Is not possible to use "id" as filter in method "${method}", use "get" instead!`, done);
-        }
-
         Resource[method](params).$promise
         .then(res => {
-          return ResponseHandler.parse(res, done);
+          const jsCode = `${resourceName}.${method}(${Object.keys(params).length > 0 ? JSON.stringify(params): ''})`;
+          return ResponseHandler.parse(res, jsCode, done);
+        })
+        .catch(e => {
+          if(e.status === 404){
+            return ErrorHandler.print(`${resourceName} with method "${method}" and parameters ${JSON.stringify(params)} ${e.data.detail}`, done);
+          }
         });
       }
       catch(e){
+        console.error(e);
         return ErrorHandler.print(`Failed to inject resource "${resourceName}"`, done);
       }
     };
diff --git a/views/ngXosViews/UITutorial/src/js/main.js b/views/ngXosViews/UITutorial/src/js/main.js
index bc5c27e..c89f1da 100644
--- a/views/ngXosViews/UITutorial/src/js/main.js
+++ b/views/ngXosViews/UITutorial/src/js/main.js
@@ -16,7 +16,7 @@
 .config(function($httpProvider){
   $httpProvider.interceptors.push('NoHyperlinks');
 })
-.directive('jsShell', function(){
+.directive('jsShell', function(TemplateHandler){
   return {
     restrict: 'E',
     scope: {},
@@ -24,23 +24,28 @@
     controllerAs: 'vm',
     templateUrl: 'templates/js-shell.tpl.html',
     controller: function(ExploreCmd){
-      var history = new Josh.History({ key: 'helloworld.history'});
-      var shell = Josh.Shell({history: history});
+      var history = new Josh.History({ key: 'jsshell.history'});
+      this.shell = Josh.Shell({history: history});
 
-      shell.onNewPrompt(function(done) {
+      this.shell.onNewPrompt(done => {
         done('[ngXosLib] $ ');
       });
 
-      shell.setCommandHandler('explore', {
+      this.shell.setCommandHandler('explore', {
         exec: (cmd, args, done) => {
-          ExploreCmd.setup(shell);
-          done();
+          ExploreCmd.setup(this.shell);
+          done(TemplateHandler.instructions({
+            title: `You can now explore the API use angular $resouces!`,
+            messages: [
+              `Use <code>resource list</code> to list all the available resources and <code>resource {resoureName} {method} {?paramters}</code> to call the API.`,
+              `An example command is <code>resource Slices query</code>`,
+              `You can also provide paramters with <code>resource Slices query {max_instances: 10}</code>`
+            ]
+          }));
         }
       });
 
-
-
-      shell.activate();
+      this.shell.activate();
     }
   };
 });
\ No newline at end of file
diff --git a/views/ngXosViews/UITutorial/src/js/responseHandler.js b/views/ngXosViews/UITutorial/src/js/responseHandler.js
index 801129e..e343dda 100644
--- a/views/ngXosViews/UITutorial/src/js/responseHandler.js
+++ b/views/ngXosViews/UITutorial/src/js/responseHandler.js
@@ -1,27 +1,47 @@
 (function () {
   'use strict';
   angular.module('xos.UITutorial')
-  .service('ResponseHandler', function(){
-    this.parse = (res, done) => {
-      var compiled = _.template('<div><pre><%- JSON.stringify(val,null,1) %></div></pre>');
-      var compiledArray = _.template('<% _.forEach(valueArr, function(item) { %><div><pre><%- JSON.stringify(item) %></pre></div><%}); %>');
-      var resFunc = function (res) {
-        let retVar;
-        let exclude = ['deleted','enabled','enacted','exposed_ports','lazy_blocked','created','validators','controllers','backend_status','backend_register','policed','no_policy','write_protect','no_sync','updated'];
-        if(_.isArray(res)) {
-          retVar = [];
-          retVar = _.map(res, (o)=> {
-            return _.omit(o, exclude);
-          });
-          retVar = compiledArray({'valueArr':retVar});
-        }
-        else{
-          retVar = _.omit(res,exclude);
-          retVar = compiled({'val':retVar} );
-        }
-        return retVar;
+  .service('ResponseHandler', function(TemplateHandler){
+
+    const exclude = [
+      'deleted',
+      'enabled',
+      'enacted',
+      'exposed_ports',
+      'lazy_blocked',
+      'created',
+      'validators',
+      'controllers',
+      'backend_status',
+      'backend_register',
+      'policed',
+      'no_policy',
+      'write_protect',
+      'no_sync',
+      'updated'
+    ];
+
+    this.parseObject = (obj, comma = '') => {
+      obj = _.omit(obj, exclude);
+      return TemplateHandler.jsonObject({'obj': obj, comma: comma});
+    };
+
+    this.parseCollection = (array) => {
+      array = array.map((o, i) => `${this.parseObject(o, i === (array.length - 1) ? '':',')}`);
+      return TemplateHandler.jsonCollection({'collection': array});
+    };
+
+    this.parse = (res, jsCode, done) => {
+      if(_.isArray(res)) {
+        res = this.parseCollection(res);
       }
-      done( resFunc(res));
+      else{
+        res = this.parseObject(res);
+      }
+      done(TemplateHandler.resourcesResponse({
+        jsCode: jsCode,
+        res: res
+      }));
     };
   });
 })();
\ No newline at end of file
diff --git a/views/ngXosViews/UITutorial/src/js/templateHandler.js b/views/ngXosViews/UITutorial/src/js/templateHandler.js
new file mode 100644
index 0000000..752b214
--- /dev/null
+++ b/views/ngXosViews/UITutorial/src/js/templateHandler.js
@@ -0,0 +1,26 @@
+(function () {
+  'use strict';
+  angular.module('xos.UITutorial')
+  .service('TemplateHandler', function(_){
+    
+    this.error = _.template(`<span class="error">[ERROR] <%= msg %></span>`);
+
+    this.instructions = _.template(`
+      <div>
+        <strong><%= title %></strong>
+        <% _.forEach(messages, function(m) { %><p><%= m %></p><% }); %>
+      </div>
+    `);
+
+    this.resourcesResponse = _.template(`
+      <div>
+        <p>Corresponding js code: <code><%= jsCode %></code></p>
+        <div class="json"><%= res %></div>
+      </div>
+    `);
+
+    this.jsonObject = _.template(`<div class="jsonObject"><%= JSON.stringify(obj) %><%=comma%></code></div>`);
+
+    this.jsonCollection = _.template(`<div class="jsonCollection">[<% _.forEach(collection, function(item) { %><%= item %><%}); %>]</div>`);
+  });
+})();
\ No newline at end of file
diff --git a/views/ngXosViews/UITutorial/src/sass/main.scss b/views/ngXosViews/UITutorial/src/sass/main.scss
index 83ea377..f3b0496 100644
--- a/views/ngXosViews/UITutorial/src/sass/main.scss
+++ b/views/ngXosViews/UITutorial/src/sass/main.scss
@@ -28,11 +28,16 @@
       background: $background;
       padding: 10px;
       overflow: scroll;
+      color: $emph;
 
       .prompt {
         color: $magenta;
       }
 
+      .input {
+        color: $blue;
+      }
+
       .cursor {
         color: $cyan;
       }
@@ -42,11 +47,20 @@
       }
 
       #shell-view {
-        color: $emph;
 
         >div>div{
           color: $blue;
         }
+
+        .jsonObject {
+          white-space: nowrap;
+        }
+
+        .jsonCollection {
+          >.jsonObject{
+            margin-left: 10px;
+          }
+        }
       }
     }
   }
diff --git a/views/ngXosViews/UITutorial/src/templates/js-shell.tpl.html b/views/ngXosViews/UITutorial/src/templates/js-shell.tpl.html
index 543006d..8799fd4 100644
--- a/views/ngXosViews/UITutorial/src/templates/js-shell.tpl.html
+++ b/views/ngXosViews/UITutorial/src/templates/js-shell.tpl.html
@@ -1,3 +1,6 @@
 <div id="shell-panel">
+  <div>
+    Type <code>help</code> or hit <code>TAB</code> for a list of commands.
+  </div>
   <div id="shell-view"></div>
 </div>
\ No newline at end of file