You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by sr...@apache.org on 2016/05/17 11:29:05 UTC

tez git commit: TEZ-3254. Tez UI: Consider downloading Hive/Pig explain plans (sree)

Repository: tez
Updated Branches:
  refs/heads/master f70aa172e -> c5ad13ed5


TEZ-3254. Tez UI: Consider downloading Hive/Pig explain plans (sree)


Project: http://git-wip-us.apache.org/repos/asf/tez/repo
Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/c5ad13ed
Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/c5ad13ed
Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/c5ad13ed

Branch: refs/heads/master
Commit: c5ad13ed51878b65927e306eb3196f802cf83e4b
Parents: f70aa17
Author: Sreenath Somarajapuram <sr...@apache.org>
Authored: Tue May 17 16:57:53 2016 +0530
Committer: Sreenath Somarajapuram <sr...@apache.org>
Committed: Tue May 17 16:57:53 2016 +0530

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 tez-ui/src/main/webapp/app/controllers/dags.js  |   6 +-
 tez-ui/src/main/webapp/app/entities/dag.js      |   6 +-
 tez-ui/src/main/webapp/app/models/dag.js        |   3 +-
 tez-ui/src/main/webapp/app/routes/dag/index.js  |  22 +++-
 tez-ui/src/main/webapp/app/routes/dags.js       |   3 +
 tez-ui/src/main/webapp/app/serializers/dag.js   |  30 ++++--
 .../src/main/webapp/app/templates/dag/index.hbs |   4 +-
 .../main/webapp/app/utils/download-dag-zip.js   |  40 +++++--
 .../main/webapp/tests/unit/entities/dag-test.js | 107 +++++++++++++++++++
 .../main/webapp/tests/unit/models/dag-test.js   |  19 ++++
 .../webapp/tests/unit/routes/dag/index-test.js  |  32 ++++++
 .../main/webapp/tests/unit/routes/dags-test.js  |  22 ++++
 .../webapp/tests/unit/serializers/dag-test.js   |  80 ++++++++++++++
 14 files changed, 343 insertions(+), 32 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index d319eb8..04cada7 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -29,6 +29,7 @@ ALL CHANGES:
   TEZ-3227. Tez UI: Replace UI1 with UI2
   TEZ-3233. Tez UI: Have LLAP information reflect in Tez UI
   TEZ-3086. Tez UI: Backward compatibility changes
+  TEZ-3254. Tez UI: Consider downloading Hive/Pig explain plans
 
 Release 0.8.4: Unreleased
 

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/app/controllers/dags.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/controllers/dags.js b/tez-ui/src/main/webapp/app/controllers/dags.js
index b35c693..a25ffa3 100644
--- a/tez-ui/src/main/webapp/app/controllers/dags.js
+++ b/tez-ui/src/main/webapp/app/controllers/dags.js
@@ -137,9 +137,9 @@ export default TableController.extend({
     headerTitle: 'Caller ID',
     contentPath: 'callerID'
   },{
-    id: 'callerType',
-    headerTitle: 'Caller Type',
-    contentPath: 'callerType'
+    id: 'callerContext',
+    headerTitle: 'Caller Context',
+    contentPath: 'callerContext'
   },{
     id: 'logs',
     headerTitle: 'Logs',

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/app/entities/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/entities/dag.js b/tez-ui/src/main/webapp/app/entities/dag.js
index 80862b7..0befc8d 100644
--- a/tez-ui/src/main/webapp/app/entities/dag.js
+++ b/tez-ui/src/main/webapp/app/entities/dag.js
@@ -21,14 +21,14 @@ import Entity from './entity';
 export default Entity.extend({
   queryRecord: function (loader, id, options, query, urlParams) {
     return this._super(loader, id, options, query, urlParams).then(function (dag) {
-      if(!dag.get("callerInfo")) {
+      if(dag.get("callerDescription") === undefined) {
         var dagName = dag.get("name") || "",
             hiveQueryID = dagName.substr(0, dagName.indexOf(":"));
         if(hiveQueryID && dagName !== hiveQueryID) {
           loader.queryRecord("hive-query", hiveQueryID, options, query, urlParams).then(function (hive) {
             dag.setProperties({
-              callerType: "Hive",
-              callerInfo: hive.get("queryText")
+              callerContext: "Hive",
+              callerDescription: hive.get("queryText")
             });
           });
         }

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/app/models/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/models/dag.js b/tez-ui/src/main/webapp/app/models/dag.js
index fc7cacd..84509e1 100644
--- a/tez-ui/src/main/webapp/app/models/dag.js
+++ b/tez-ui/src/main/webapp/app/models/dag.js
@@ -65,8 +65,9 @@ export default AMTimelineModel.extend({
   vertexIdNameMap: DS.attr("object"),
 
   callerID: DS.attr("string"),
+  callerContext: DS.attr("string"),
+  callerDescription: DS.attr("string"),
   callerType: DS.attr("string"),
-  callerInfo: DS.attr("string"),
 
   amWsVersion: DS.attr("string"),
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/app/routes/dag/index.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/routes/dag/index.js b/tez-ui/src/main/webapp/app/routes/dag/index.js
index acabc58..0bf01b2 100644
--- a/tez-ui/src/main/webapp/app/routes/dag/index.js
+++ b/tez-ui/src/main/webapp/app/routes/dag/index.js
@@ -35,13 +35,33 @@ export default SingleAmPollsterRoute.extend({
     return this.get("loader").queryRecord('dag', this.modelFor("dag").get("id"), options);
   },
 
+  getCallerInfo: function (dag) {
+    var dagName = dag.get("name") || "",
+        callerType = dag.get("callerType"),
+        callerID = dag.get("callerID");
+
+    if(!callerID || !callerType) {
+      let hiveQueryID = dagName.substr(0, dagName.indexOf(":"));
+      if(hiveQueryID && dagName !== hiveQueryID) {
+        callerType = "HIVE_QUERY_ID";
+        callerID = hiveQueryID;
+      }
+    }
+
+    return {
+      type: callerType,
+      id: callerID
+    };
+  },
+
   actions: {
     downloadDagJson: function () {
       var dag = this.get("loadedValue"),
           downloader = downloadDAGZip(dag, {
             batchSize: 500,
             timelineHost: this.get("hosts.timeline"),
-            timelineNamespace: this.get("env.app.namespaces.webService.timeline")
+            timelineNamespace: this.get("env.app.namespaces.webService.timeline"),
+            callerInfo: this.getCallerInfo(dag)
           }),
           modalContent = Ember.Object.create({
             dag: dag,

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/app/routes/dags.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/routes/dags.js b/tez-ui/src/main/webapp/app/routes/dags.js
index 33366c8..2247637 100644
--- a/tez-ui/src/main/webapp/app/routes/dags.js
+++ b/tez-ui/src/main/webapp/app/routes/dags.js
@@ -161,6 +161,9 @@ export default AbstractRoute.extend({
       var loader = this.get("loader");
       loader.unloadAll("dag");
       loader.unloadAll("ahs-app");
+
+      this.set("controller.pageNum", 1);
+
       this._super();
     },
   }

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/app/serializers/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/serializers/dag.js b/tez-ui/src/main/webapp/app/serializers/dag.js
index 39832b2..a64bfe1 100644
--- a/tez-ui/src/main/webapp/app/serializers/dag.js
+++ b/tez-ui/src/main/webapp/app/serializers/dag.js
@@ -127,32 +127,40 @@ export default TimelineSerializer.extend({
     vertexIdNameMap: getIdNameMap,
 
     callerID: 'primaryfilters.callerId.0',
+    callerContext: 'callerContext',
+    callerDescription: 'callerDescription',
     callerType: 'callerType',
-    callerInfo: 'callerInfo',
 
     amWsVersion: 'otherinfo.amWebServiceVersion',
   },
 
-  extractAttributes: function (modelClass, resourceHash) {
+  normalizeResourceHash: function (resourceHash) {
     var data = resourceHash.data,
         dagInfo = Ember.get(resourceHash, "data.otherinfo.dagPlan.dagInfo"), // New style, from TEZ-2851
         dagContext = Ember.get(resourceHash, "data.otherinfo.dagPlan.dagContext"); // Old style
 
-    if(dagInfo) {
+    if(dagContext) {
+      data.callerContext = Ember.String.classify((Ember.get(dagContext, "context")||"").toLowerCase());
+      data.callerDescription = Ember.get(dagContext, "description");
+      data.callerType = Ember.get(dagContext, "callerType");
+    }
+    else if(dagInfo) {
       let infoObj = {};
       try{
         infoObj = JSON.parse(dagInfo);
-      }catch(e){}
+      }catch(e){
+        infoObj = dagInfo;
+      }
 
-      data.callerType = Ember.get(infoObj, "context");
-      data.callerInfo = Ember.get(infoObj, "description") || Ember.get(dagInfo, "blob") || dagInfo;
-    }
-    else if(dagContext) {
-      data.callerType = Ember.String.classify((Ember.get(dagContext, "context")||"").toLowerCase());
-      data.callerInfo = Ember.get(dagContext, "description");
+      data.callerContext = Ember.get(infoObj, "context");
+      data.callerDescription = Ember.get(infoObj, "description") || Ember.get(dagInfo, "blob") || dagInfo;
     }
 
-    return this._super(modelClass, resourceHash);
+    return resourceHash;
+  },
+
+  extractAttributes: function (modelClass, resourceHash) {
+    return this._super(modelClass, this.normalizeResourceHash(resourceHash));
   },
 
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/app/templates/dag/index.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/dag/index.hbs b/tez-ui/src/main/webapp/app/templates/dag/index.hbs
index baba162..6a47dbe 100644
--- a/tez-ui/src/main/webapp/app/templates/dag/index.hbs
+++ b/tez-ui/src/main/webapp/app/templates/dag/index.hbs
@@ -80,8 +80,8 @@
 
   {{outlet}}
 
-  {{#if model.callerInfo}}
-    {{caller-info type=model.callerType info=model.callerInfo}}
+  {{#if model.callerDescription}}
+    {{caller-info type=model.callerContext info=model.callerDescription}}
   {{/if}}
 
   {{#if model.diagnostics}}

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/app/utils/download-dag-zip.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/utils/download-dag-zip.js b/tez-ui/src/main/webapp/app/utils/download-dag-zip.js
index eeb20ac..9c91a92 100644
--- a/tez-ui/src/main/webapp/app/utils/download-dag-zip.js
+++ b/tez-ui/src/main/webapp/app/utils/download-dag-zip.js
@@ -128,9 +128,15 @@ var IO = {
         processNext();
       }).fail(function(xhr, statusText/*, errorObject*/) {
         delete pendingRequests[reqID];
-        markFailed(statusText);
         inProgress--;
-        checkForCompletion();
+        if(item.onItemFail) {
+          item.onItemFail();
+          processNext();
+        }
+        else {
+          markFailed(statusText);
+          checkForCompletion();
+        }
       });
     }
 
@@ -288,22 +294,34 @@ export default function downloadDagZip(dag, options) {
           onItemFetched: processSingleItem
         },
         {
-          url: getUrl('TEZ_VERTEX_ID', dagID),
+          url: getUrl('TEZ_VERTEX_ID', null, dagID),
           context: { name: 'vertices', type: 'TEZ_VERTEX_ID', part: 0 },
           onItemFetched: processMultipleItems
         },
         {
-          url: getUrl('TEZ_TASK_ID', dagID),
+          url: getUrl('TEZ_TASK_ID', null, dagID),
           context: { name: 'tasks', type: 'TEZ_TASK_ID', part: 0 },
           onItemFetched: processMultipleItems
         },
         {
-          url: getUrl('TEZ_TASK_ATTEMPT_ID', dagID),
+          url: getUrl('TEZ_TASK_ATTEMPT_ID', null, dagID),
           context: { name: 'task_attempts', type: 'TEZ_TASK_ATTEMPT_ID', part: 0 },
           onItemFetched: processMultipleItems
         }
-      ],
-      totalItemsToDownload = itemsToDownload.length,
+      ];
+
+      let callerID = options.callerInfo.id,
+          entityType = options.callerInfo.type;
+      if(callerID && entityType) {
+        itemsToDownload.push({
+          url: getUrl(entityType, callerID),
+          context: { name: entityType.toLocaleLowerCase(), type: entityType },
+          onItemFetched: processSingleItem,
+          onItemFail: checkIfAllDownloaded
+        });
+      }
+
+  var totalItemsToDownload = itemsToDownload.length,
       numItemTypesToDownload = totalItemsToDownload,
       downloader = IO.fileDownloader(),
       zipHelper = IO.zipHelper({
@@ -323,12 +341,12 @@ export default function downloadDagZip(dag, options) {
         }
       });
 
-  function getUrl(type, dagID, fromID) {
+  function getUrl(type, id, dagID, fromID) {
     var url,
         queryBatchSize = batchSize + 1;
 
-    if (type === 'TEZ_DAG_ID' || type === 'TEZ_APPLICATION') {
-      url = `${baseurl}/${type}/${dagID}`;
+    if (id) {
+      url = `${baseurl}/${type}/${id}`;
     } else {
       url = `${baseurl}/${type}?primaryFilter=TEZ_DAG_ID:${dagID}&limit=${queryBatchSize}`;
       if (!!fromID) {
@@ -376,7 +394,7 @@ export default function downloadDagZip(dag, options) {
     if (!!nextBatchStart) {
       context.part++;
       downloader.queueItem({
-        url: getUrl(context.type, dagID, nextBatchStart),
+        url: getUrl(context.type, null, dagID, nextBatchStart),
         context: context,
         onItemFetched: processMultipleItems
       });

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/tests/unit/entities/dag-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/entities/dag-test.js b/tez-ui/src/main/webapp/tests/unit/entities/dag-test.js
index 5410ae2..f48ad3a 100644
--- a/tez-ui/src/main/webapp/tests/unit/entities/dag-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/entities/dag-test.js
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+import Ember from 'ember';
 import { moduleFor, test } from 'ember-qunit';
 
 moduleFor('entitie:dag', 'Unit | Entity | dag', {
@@ -26,4 +27,110 @@ moduleFor('entitie:dag', 'Unit | Entity | dag', {
 test('Basic creation test', function(assert) {
   let entity = this.subject();
   assert.ok(entity);
+  assert.ok(entity.queryRecord);
+});
+
+test('queryRecord-Hive:No description test', function(assert) {
+  let testQuery = "testQuery",
+      entityName = "entity-name",
+      testDag = Ember.Object.create({
+        name: "testName:1"
+      }),
+      hiveQuery = Ember.Object.create({
+        queryText: testQuery
+      }),
+      store = {
+        queryRecord: function (name) {
+          assert.equal(name, entityName);
+          return Ember.RSVP.resolve(testDag);
+        }
+      },
+      loader = Ember.Object.create({
+        nameSpace: "ns",
+        queryRecord: function (type, id/*, options, query, urlParams*/) {
+          assert.equal(type, "hive-query");
+          assert.equal(id, "testName");
+          return Ember.RSVP.resolve(hiveQuery);
+        }
+      }),
+      entity = this.subject({
+        name: entityName,
+        store: store
+      });
+
+  assert.expect(1 + 2 + 3);
+
+  entity.queryRecord(loader).then(function (dag) {
+    assert.equal(testDag, dag);
+    assert.equal(testDag.get("callerContext"), "Hive");
+    assert.equal(testDag.get("callerDescription"), testQuery);
+  });
+});
+
+test('queryRecord-Not Hive:No description test', function(assert) {
+  let testQuery = "testQuery",
+      entityName = "entity-name",
+      testDag = Ember.Object.create({
+        name: "testName"
+      }),
+      hiveQuery = Ember.Object.create({
+        queryText: testQuery
+      }),
+      store = {
+        queryRecord: function (name) {
+          assert.equal(name, entityName);
+          return Ember.RSVP.resolve(testDag);
+        }
+      },
+      loader = Ember.Object.create({
+        nameSpace: "ns",
+        queryRecord: function (/*type, id, options, query, urlParams*/) {
+          assert.ok(false);
+          return Ember.RSVP.resolve(hiveQuery);
+        }
+      }),
+      entity = this.subject({
+        name: entityName,
+        store: store
+      });
+
+  assert.expect(1 + 3);
+
+  entity.queryRecord(loader).then(function (dag) {
+    assert.equal(testDag, dag);
+    assert.ok(!testDag.get("callerContext"));
+    assert.ok(!testDag.get("callerDescription"));
+  });
+});
+
+test('queryRecord-With description test', function(assert) {
+  let testQuery = "testQuery",
+      entityName = "entity-name",
+      testDag = Ember.Object.create({
+        name: "testName",
+        callerDescription: testQuery
+      }),
+      loader = Ember.Object.create({
+        nameSpace: "ns",
+        queryRecord: function (/*type, id, options, query, urlParams*/) {
+          assert.ok(false);
+        }
+      }),
+      store = {
+        queryRecord: function (name) {
+          assert.equal(name, entityName);
+          return Ember.RSVP.resolve(testDag);
+        }
+      },
+      entity = this.subject({
+        name: entityName,
+        store: store
+      });
+
+  assert.expect(1 + 2);
+
+  entity.queryRecord(loader).then(function (dag) {
+    assert.equal(testDag, dag);
+    assert.equal(testDag.get("callerDescription"), testQuery);
+  });
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/tests/unit/models/dag-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/models/dag-test.js b/tez-ui/src/main/webapp/tests/unit/models/dag-test.js
index 513af7d..78affcf 100644
--- a/tez-ui/src/main/webapp/tests/unit/models/dag-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/models/dag-test.js
@@ -37,4 +37,23 @@ test('Basic creation test', function(assert) {
     assert.ok(!!model.needs.am);
     assert.equal(model.get("queue"), testQueue);
   });
+
+  assert.ok(model.name);
+  assert.ok(model.submitter);
+
+  assert.ok(model.vertices);
+  assert.ok(model.edges);
+  assert.ok(model.vertexGroups);
+
+  assert.ok(model.domain);
+  assert.ok(model.containerLogs);
+
+  assert.ok(model.vertexIdNameMap);
+
+  assert.ok(model.callerID);
+  assert.ok(model.callerContext);
+  assert.ok(model.callerDescription);
+  assert.ok(model.callerType);
+
+  assert.ok(model.amWsVersion);
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/tests/unit/routes/dag/index-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/routes/dag/index-test.js b/tez-ui/src/main/webapp/tests/unit/routes/dag/index-test.js
index 8c0cb5d..b7ee27f 100644
--- a/tez-ui/src/main/webapp/tests/unit/routes/dag/index-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/routes/dag/index-test.js
@@ -16,6 +16,7 @@
  * limitations under the License.
  */
 
+import Ember from 'ember';
 import { moduleFor, test } from 'ember-qunit';
 
 moduleFor('route:dag/index', 'Unit | Route | dag/index', {
@@ -31,6 +32,7 @@ test('Basic creation test', function(assert) {
   assert.ok(route.loaderNamespace);
   assert.ok(route.setupController);
   assert.ok(route.load);
+  assert.ok(route.getCallerInfo);
 });
 
 test('setupController test', function(assert) {
@@ -45,3 +47,33 @@ test('setupController test', function(assert) {
   route.setupController({}, {});
 });
 
+test('getCallerInfo test', function(assert) {
+  let route = this.subject({
+      startCrumbBubble: Ember.K
+      }),
+
+      testID = "id",
+      testType = "entity",
+
+      dag = Ember.Object.create({
+        name: "hive_query_id:1",
+      }),
+      callerInfo;
+
+  // callerID computed - No callerType
+  callerInfo = route.getCallerInfo(dag);
+  assert.equal(callerInfo.id, "hive_query_id");
+  assert.equal(callerInfo.type, "HIVE_QUERY_ID");
+
+  // callerID computed - No callerID
+  dag.set("callerType", testType);
+  callerInfo = route.getCallerInfo(dag);
+  assert.equal(callerInfo.id, "hive_query_id");
+  assert.equal(callerInfo.type, "HIVE_QUERY_ID");
+
+  // callerID & callerType available
+  dag.set("callerID", testID);
+  callerInfo = route.getCallerInfo(dag);
+  assert.equal(callerInfo.id, testID);
+  assert.equal(callerInfo.type, testType);
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/tests/unit/routes/dags-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/routes/dags-test.js b/tez-ui/src/main/webapp/tests/unit/routes/dags-test.js
index dfa8223..39a3300 100644
--- a/tez-ui/src/main/webapp/tests/unit/routes/dags-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/routes/dags-test.js
@@ -130,3 +130,25 @@ test('loadNewPage test', function(assert) {
 
   route.loadNewPage();
 });
+
+test('actions.willTransition test', function(assert) {
+  let testPageNum = 5,
+      controller = Ember.Object.create({
+        pageNum: testPageNum
+      }),
+      route = this.subject({
+        controller: controller,
+      });
+
+  route.set("loader", {
+    unloadAll: function () {
+      assert.ok(true);
+    }
+  });
+
+  assert.expect(2 + 1 + 1);
+
+  assert.equal(controller.get("pageNum"), testPageNum);
+  route.send("willTransition");
+  assert.equal(controller.get("pageNum"), 1); // PageNum must be reset
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/c5ad13ed/tez-ui/src/main/webapp/tests/unit/serializers/dag-test.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/tests/unit/serializers/dag-test.js b/tez-ui/src/main/webapp/tests/unit/serializers/dag-test.js
index eb39508..7df4084 100644
--- a/tez-ui/src/main/webapp/tests/unit/serializers/dag-test.js
+++ b/tez-ui/src/main/webapp/tests/unit/serializers/dag-test.js
@@ -27,6 +27,9 @@ test('Basic creation test', function(assert) {
   let serializer = this.subject();
 
   assert.ok(serializer);
+
+  assert.ok(serializer.normalizeResourceHash);
+
   assert.ok(serializer.maps.atsStatus);
   assert.ok(serializer.maps.startTime);
   assert.ok(serializer.maps.endTime);
@@ -129,3 +132,80 @@ test('vertexIdNameMap test', function(assert) {
     ID3: "name3",
   });
 });
+
+test('normalizeResourceHash test', function(assert) {
+  let serializer = this.subject(),
+
+      callerInfo = {
+        callerId: "id_1",
+        callerType: "HIVE_QUERY_ID",
+        context: "Hive",
+        description: "hive query"
+      },
+
+      data;
+
+  // dagContext test
+  data = serializer.normalizeResourceHash({
+    data: {
+      otherinfo: {
+        dagPlan: {
+          dagContext: callerInfo
+        }
+      }
+    }
+  }).data;
+
+  assert.equal(data.callerContext, callerInfo.context);
+  assert.equal(data.callerDescription, callerInfo.description);
+  assert.equal(data.callerType, callerInfo.callerType);
+
+  // dagInfo test
+  data = serializer.normalizeResourceHash({
+    data: {
+      otherinfo: {
+        dagPlan: {
+          dagInfo: `{"context": "${callerInfo.context}", "description": "${callerInfo.description}"}`
+        }
+      }
+    }
+  }).data;
+
+  assert.equal(data.callerContext, callerInfo.context);
+  assert.equal(data.callerDescription, callerInfo.description);
+  assert.notOk(data.callerType);
+
+  // dagInfo.blob test
+  data = serializer.normalizeResourceHash({
+    data: {
+      otherinfo: {
+        dagPlan: {
+          dagInfo: {
+            context: callerInfo.context,
+            blob: callerInfo.description
+          }
+        }
+      }
+    }
+  }).data;
+
+  assert.equal(data.callerContext, callerInfo.context);
+  assert.equal(data.callerDescription, callerInfo.description);
+  assert.notOk(data.callerType);
+
+  // dagContext have presidence over dagInfo
+  data = serializer.normalizeResourceHash({
+    data: {
+      otherinfo: {
+        dagPlan: {
+          dagContext: callerInfo,
+          dagInfo: `{"context": "RandomContext", "description": "RandomDesc"}`
+        }
+      }
+    }
+  }).data;
+
+  assert.equal(data.callerContext, callerInfo.context);
+  assert.equal(data.callerDescription, callerInfo.description);
+  assert.equal(data.callerType, callerInfo.callerType);
+});