{"action":"created","comment":{"url":"https://api.github.com/repos/RackHD/on-http/pulls/comments/48830704","id":48830704,"diff_hunk":"@@ -30,64 +32,136 @@ function nodeApiServiceFactory(\n     }\n \n     /**\n-     * Find Enclosure nodes which enclose compute node\n+     * Find target nodes that relate with the removing node\n      * @param  {Object}     node\n-     * @return {Promise}\n+     * @param  {Object}     type\n+     * @return {Promise}    array of target nodes\n      */\n-    NodeApiService.prototype._findEnclNodes = function(node) {\n-        // Find the enclosure nodes who enclose this compute node\n+    NodeApiService.prototype._findTargetNodes = function(node, type) {\n         if (!_.has(node, 'relations')) {\n             return Promise.resolve();\n         }\n \n-        var relation = _.find(node.relations, { relationType: 'enclosedBy' });\n+        var relation = _.find(node.relations, { relationType: type });\n         if (!relation || !_.has(relation, 'targets') ) {\n             return Promise.resolve();\n         }\n \n-        return Promise.map(relation.targets, function (enclNodeId) {\n-            return waterline.nodes.needByIdentifier(enclNodeId)\n+        return Promise.map(relation.targets, function (targetNodeId) {\n+            return waterline.nodes.needByIdentifier(targetNodeId)\n             .catch(function (err) {\n-                logger.warning(\"Error Getting Enclosure Node\", { error: err });\n+                logger.warning(\"Error Getting Downstream Node with type \" + type,\n+                    { error: err });\n                 return;\n             });\n         });\n     };\n \n     /**\n-     * Remove the relations between node and enclosure node, if enclosure node\n-     * doesn't enclose any nodes, this enclosure node is also removed.\n-     * @param  {Object}     node\n-     * @param  {Array}      enclNodes\n-     * @return {Promise}\n+     * Remove the relations to node in the downstram node. If the relation field is\n+     * empty after removing, delete this field.\n+     * @param  {Object}     node node whose relation needs to be updated\n+     * @param  {String}     type relation type that needs to be updated\n+     * @param  {String}     targetId id in relation type that needs to be deleted\n+     * @return {Promise}    node after removing relation\n      */\n-    NodeApiService.prototype._removeEnclRelations = function(node, enclNodes) {\n-        if (!enclNodes) {\n+    NodeApiService.prototype._removeRelation = function(node, type, targetId) {\n+\n+        if (!node) {\n             return Promise.resolve();\n         }\n \n-        // Remove the relationship between enclosure and compute node\n-        return Promise.map(enclNodes, function(enclNode) {\n-            if (!_.has(enclNode, 'relations')) {\n-                return;\n-            }\n+        // Remove the relationship between node and downstram node\n+        if (!_.has(node, 'relations')) {\n+            return Promise.resolve();\n+        }\n \n-            var index = _.findIndex(enclNode.relations, { relationType: 'encloses' });\n-            if (index === -1 || !_.has(enclNode.relations[index], 'targets')) {\n-                return;\n-            }\n+        var index = _.findIndex(node.relations, { relationType: type });\n+        if (index === -1 || !_.has(node.relations[index], 'targets')) {\n+            return Promise.resolve();\n+        }\n+\n+        // Remove target node id in relation field\n+        if (_.indexOf(node.relations[index].targets, targetId) !== -1) {\n+            _.pull(node.relations[index].targets, targetId);\n+        }\n+\n+        // Remove the type of relation if no targets in it\n+        if (node.relations[index].targets.length === 0) {\n+            _.pull(node.relations, node.relations[index]);\n+        }\n+\n+        return waterline.nodes.updateByIdentifier(node.id,\n+                                                  {relations: node.relations});\n+    };\n+\n+    /**\n+     * Handle target nodes. Update relation type field and remove the target\n+     * node if needed.\n+     * @param  {Object}     targetNodos array of target nodes\n+     * @param  {Object}     type relation type in the removing node\n+     * @param  {Object}     nodeId removing node id\n+     * @return {Promise}\n+     */\n+    NodeApiService.prototype._handleTargetNodes = function(targetNodes, type, nodeId) {\n+        var self = this;\n+        var targetType = '';\n+        var relationClass = null;\n+        var needDel = false;\n+\n+        if (Constants.NodeRelations.hasOwnProperty(type)) {\n+            targetType = Constants.NodeRelations[type].mapping;\n+            relationClass = Constants.NodeRelations[type].relationClass;\n+        }\n+\n+        if ((relationClass === 'component') &&\n+            (type.indexOf('By') === -1)) {\n+            // Its components need to be deleted\n+            needDel = true;\n+        }\n \n-            if (_.indexOf(enclNode.relations[index].targets, node.id) !== -1) {\n-                _.pull(enclNode.relations[index].targets, node.id);\n+        return Promise.resolve()\n+        .then(function () {\n+            if (needDel === true) {\n+                // Throw error when all target nodes need to be deleted\n+                // but at least one of them have active workflow\n+                return Promise.map(targetNodes, function(targetNode) {","path":"lib/services/nodes-api-service.js","position":143,"original_position":143,"commit_id":"788a938823676441e3fd67922558c6987b221014","original_commit_id":"788a938823676441e3fd67922558c6987b221014","user":{"login":"iceiilin","id":10793130,"avatar_url":"https://avatars.githubusercontent.com/u/10793130?v=3","gravatar_id":"","url":"https://api.github.com/users/iceiilin","html_url":"https://github.com/iceiilin","followers_url":"https://api.github.com/users/iceiilin/followers","following_url":"https://api.github.com/users/iceiilin/following{/other_user}","gists_url":"https://api.github.com/users/iceiilin/gists{/gist_id}","starred_url":"https://api.github.com/users/iceiilin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iceiilin/subscriptions","organizations_url":"https://api.github.com/users/iceiilin/orgs","repos_url":"https://api.github.com/users/iceiilin/repos","events_url":"https://api.github.com/users/iceiilin/events{/privacy}","received_events_url":"https://api.github.com/users/iceiilin/received_events","type":"User","site_admin":false},"body":"Yes. The node that user requires to delete will be removed only after all its target nodes has been deleted (if their relation requires) successfully.","created_at":"2016-01-05T10:21:42Z","updated_at":"2016-01-05T10:21:42Z","html_url":"https://github.com/RackHD/on-http/pull/80#discussion_r48830704","pull_request_url":"https://api.github.com/repos/RackHD/on-http/pulls/80","_links":{"self":{"href":"https://api.github.com/repos/RackHD/on-http/pulls/comments/48830704"},"html":{"href":"https://github.com/RackHD/on-http/pull/80#discussion_r48830704"},"pull_request":{"href":"https://api.github.com/repos/RackHD/on-http/pulls/80"}}},"pull_request":{"url":"https://api.github.com/repos/RackHD/on-http/pulls/80","id":54713954,"html_url":"https://github.com/RackHD/on-http/pull/80","diff_url":"https://github.com/RackHD/on-http/pull/80.diff","patch_url":"https://github.com/RackHD/on-http/pull/80.patch","issue_url":"https://api.github.com/repos/RackHD/on-http/issues/80","number":80,"state":"open","locked":false,"title":"add deleting all types of nodes","user":{"login":"iceiilin","id":10793130,"avatar_url":"https://avatars.githubusercontent.com/u/10793130?v=3","gravatar_id":"","url":"https://api.github.com/users/iceiilin","html_url":"https://github.com/iceiilin","followers_url":"https://api.github.com/users/iceiilin/followers","following_url":"https://api.github.com/users/iceiilin/following{/other_user}","gists_url":"https://api.github.com/users/iceiilin/gists{/gist_id}","starred_url":"https://api.github.com/users/iceiilin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iceiilin/subscriptions","organizations_url":"https://api.github.com/users/iceiilin/orgs","repos_url":"https://api.github.com/users/iceiilin/repos","events_url":"https://api.github.com/users/iceiilin/events{/privacy}","received_events_url":"https://api.github.com/users/iceiilin/received_events","type":"User","site_admin":false},"body":"Implement full function about deleting nodes and update its related ones based on \"relations\" field in nodes/ document. It is invoked by ODR-314 (https://hwjiraprd01.corp.emc.com/browse/ODR-314) requiring removing compute nodes in an enclosure when user asks to delete that enclosure node.\r\n\r\nCurrently there is only (enclosedBy, encloses) relation in node, but it is easily extensible to cover other relations such as (powers, powerBy) or (compute node use dae, dae used by compute node). When deleting one node, RackHD should also delete or update its corresponding nodes in different conditions.\r\n\r\nThere are 3 types of operations to deal with corresponding (I call it \"target node\" in code and here) nodes:\r\n* delete all target nodes. \r\n   e.g. delete enclosure node -> delete compute node as well\r\n* delete target nodes when it doesn't have other targets in this relation. \r\n   e.g. delete compute node -> delete enclosure node if this enclosure doesn't have other compute node\r\n* update relation in target node. \r\n   e.g. delete compute node -> update relations in pdu node\r\n\r\nThis PR covers all cases above. constants.js in on-core defines mapping table and supported target operations. node-api-service in this PR reads this setting and manipulates database. The required nodes will be deleted only after all its target nodes have been processed successfully.\r\n\r\nrelated PR:\r\nhttps://github.com/RackHD/on-core/pull/55\r\n\r\n","created_at":"2015-12-29T09:47:22Z","updated_at":"2016-01-05T10:21:42Z","closed_at":null,"merged_at":null,"merge_commit_sha":"b69ae48a8b72441f1a6d64344f83a55d2a3ce6ca","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/RackHD/on-http/pulls/80/commits","review_comments_url":"https://api.github.com/repos/RackHD/on-http/pulls/80/comments","review_comment_url":"https://api.github.com/repos/RackHD/on-http/pulls/comments{/number}","comments_url":"https://api.github.com/repos/RackHD/on-http/issues/80/comments","statuses_url":"https://api.github.com/repos/RackHD/on-http/statuses/788a938823676441e3fd67922558c6987b221014","head":{"label":"iceiilin:bugfix/odr-314-del-encl","ref":"bugfix/odr-314-del-encl","sha":"788a938823676441e3fd67922558c6987b221014","user":{"login":"iceiilin","id":10793130,"avatar_url":"https://avatars.githubusercontent.com/u/10793130?v=3","gravatar_id":"","url":"https://api.github.com/users/iceiilin","html_url":"https://github.com/iceiilin","followers_url":"https://api.github.com/users/iceiilin/followers","following_url":"https://api.github.com/users/iceiilin/following{/other_user}","gists_url":"https://api.github.com/users/iceiilin/gists{/gist_id}","starred_url":"https://api.github.com/users/iceiilin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iceiilin/subscriptions","organizations_url":"https://api.github.com/users/iceiilin/orgs","repos_url":"https://api.github.com/users/iceiilin/repos","events_url":"https://api.github.com/users/iceiilin/events{/privacy}","received_events_url":"https://api.github.com/users/iceiilin/received_events","type":"User","site_admin":false},"repo":{"id":45585896,"name":"on-http","full_name":"iceiilin/on-http","owner":{"login":"iceiilin","id":10793130,"avatar_url":"https://avatars.githubusercontent.com/u/10793130?v=3","gravatar_id":"","url":"https://api.github.com/users/iceiilin","html_url":"https://github.com/iceiilin","followers_url":"https://api.github.com/users/iceiilin/followers","following_url":"https://api.github.com/users/iceiilin/following{/other_user}","gists_url":"https://api.github.com/users/iceiilin/gists{/gist_id}","starred_url":"https://api.github.com/users/iceiilin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/iceiilin/subscriptions","organizations_url":"https://api.github.com/users/iceiilin/orgs","repos_url":"https://api.github.com/users/iceiilin/repos","events_url":"https://api.github.com/users/iceiilin/events{/privacy}","received_events_url":"https://api.github.com/users/iceiilin/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/iceiilin/on-http","description":"","fork":true,"url":"https://api.github.com/repos/iceiilin/on-http","forks_url":"https://api.github.com/repos/iceiilin/on-http/forks","keys_url":"https://api.github.com/repos/iceiilin/on-http/keys{/key_id}","collaborators_url":"https://api.github.com/repos/iceiilin/on-http/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/iceiilin/on-http/teams","hooks_url":"https://api.github.com/repos/iceiilin/on-http/hooks","issue_events_url":"https://api.github.com/repos/iceiilin/on-http/issues/events{/number}","events_url":"https://api.github.com/repos/iceiilin/on-http/events","assignees_url":"https://api.github.com/repos/iceiilin/on-http/assignees{/user}","branches_url":"https://api.github.com/repos/iceiilin/on-http/branches{/branch}","tags_url":"https://api.github.com/repos/iceiilin/on-http/tags","blobs_url":"https://api.github.com/repos/iceiilin/on-http/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/iceiilin/on-http/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/iceiilin/on-http/git/refs{/sha}","trees_url":"https://api.github.com/repos/iceiilin/on-http/git/trees{/sha}","statuses_url":"https://api.github.com/repos/iceiilin/on-http/statuses/{sha}","languages_url":"https://api.github.com/repos/iceiilin/on-http/languages","stargazers_url":"https://api.github.com/repos/iceiilin/on-http/stargazers","contributors_url":"https://api.github.com/repos/iceiilin/on-http/contributors","subscribers_url":"https://api.github.com/repos/iceiilin/on-http/subscribers","subscription_url":"https://api.github.com/repos/iceiilin/on-http/subscription","commits_url":"https://api.github.com/repos/iceiilin/on-http/commits{/sha}","git_commits_url":"https://api.github.com/repos/iceiilin/on-http/git/commits{/sha}","comments_url":"https://api.github.com/repos/iceiilin/on-http/comments{/number}","issue_comment_url":"https://api.github.com/repos/iceiilin/on-http/issues/comments{/number}","contents_url":"https://api.github.com/repos/iceiilin/on-http/contents/{+path}","compare_url":"https://api.github.com/repos/iceiilin/on-http/compare/{base}...{head}","merges_url":"https://api.github.com/repos/iceiilin/on-http/merges","archive_url":"https://api.github.com/repos/iceiilin/on-http/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/iceiilin/on-http/downloads","issues_url":"https://api.github.com/repos/iceiilin/on-http/issues{/number}","pulls_url":"https://api.github.com/repos/iceiilin/on-http/pulls{/number}","milestones_url":"https://api.github.com/repos/iceiilin/on-http/milestones{/number}","notifications_url":"https://api.github.com/repos/iceiilin/on-http/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/iceiilin/on-http/labels{/name}","releases_url":"https://api.github.com/repos/iceiilin/on-http/releases{/id}","created_at":"2015-11-05T03:47:26Z","updated_at":"2015-11-05T03:47:27Z","pushed_at":"2015-12-31T09:09:29Z","git_url":"git://github.com/iceiilin/on-http.git","ssh_url":"git@github.com:iceiilin/on-http.git","clone_url":"https://github.com/iceiilin/on-http.git","svn_url":"https://github.com/iceiilin/on-http","homepage":null,"size":1438,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"RackHD:master","ref":"master","sha":"c4fda0f4abafb9a326f5b13843146730a2f41ced","user":{"login":"RackHD","id":13982662,"avatar_url":"https://avatars.githubusercontent.com/u/13982662?v=3","gravatar_id":"","url":"https://api.github.com/users/RackHD","html_url":"https://github.com/RackHD","followers_url":"https://api.github.com/users/RackHD/followers","following_url":"https://api.github.com/users/RackHD/following{/other_user}","gists_url":"https://api.github.com/users/RackHD/gists{/gist_id}","starred_url":"https://api.github.com/users/RackHD/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RackHD/subscriptions","organizations_url":"https://api.github.com/users/RackHD/orgs","repos_url":"https://api.github.com/users/RackHD/repos","events_url":"https://api.github.com/users/RackHD/events{/privacy}","received_events_url":"https://api.github.com/users/RackHD/received_events","type":"Organization","site_admin":false},"repo":{"id":41690081,"name":"on-http","full_name":"RackHD/on-http","owner":{"login":"RackHD","id":13982662,"avatar_url":"https://avatars.githubusercontent.com/u/13982662?v=3","gravatar_id":"","url":"https://api.github.com/users/RackHD","html_url":"https://github.com/RackHD","followers_url":"https://api.github.com/users/RackHD/followers","following_url":"https://api.github.com/users/RackHD/following{/other_user}","gists_url":"https://api.github.com/users/RackHD/gists{/gist_id}","starred_url":"https://api.github.com/users/RackHD/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/RackHD/subscriptions","organizations_url":"https://api.github.com/users/RackHD/orgs","repos_url":"https://api.github.com/users/RackHD/repos","events_url":"https://api.github.com/users/RackHD/events{/privacy}","received_events_url":"https://api.github.com/users/RackHD/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/RackHD/on-http","description":"","fork":false,"url":"https://api.github.com/repos/RackHD/on-http","forks_url":"https://api.github.com/repos/RackHD/on-http/forks","keys_url":"https://api.github.com/repos/RackHD/on-http/keys{/key_id}","collaborators_url":"https://api.github.com/repos/RackHD/on-http/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/RackHD/on-http/teams","hooks_url":"https://api.github.com/repos/RackHD/on-http/hooks","issue_events_url":"https://api.github.com/repos/RackHD/on-http/issues/events{/number}","events_url":"https://api.github.com/repos/RackHD/on-http/events","assignees_url":"https://api.github.com/repos/RackHD/on-http/assignees{/user}","branches_url":"https://api.github.com/repos/RackHD/on-http/branches{/branch}","tags_url":"https://api.github.com/repos/RackHD/on-http/tags","blobs_url":"https://api.github.com/repos/RackHD/on-http/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/RackHD/on-http/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/RackHD/on-http/git/refs{/sha}","trees_url":"https://api.github.com/repos/RackHD/on-http/git/trees{/sha}","statuses_url":"https://api.github.com/repos/RackHD/on-http/statuses/{sha}","languages_url":"https://api.github.com/repos/RackHD/on-http/languages","stargazers_url":"https://api.github.com/repos/RackHD/on-http/stargazers","contributors_url":"https://api.github.com/repos/RackHD/on-http/contributors","subscribers_url":"https://api.github.com/repos/RackHD/on-http/subscribers","subscription_url":"https://api.github.com/repos/RackHD/on-http/subscription","commits_url":"https://api.github.com/repos/RackHD/on-http/commits{/sha}","git_commits_url":"https://api.github.com/repos/RackHD/on-http/git/commits{/sha}","comments_url":"https://api.github.com/repos/RackHD/on-http/comments{/number}","issue_comment_url":"https://api.github.com/repos/RackHD/on-http/issues/comments{/number}","contents_url":"https://api.github.com/repos/RackHD/on-http/contents/{+path}","compare_url":"https://api.github.com/repos/RackHD/on-http/compare/{base}...{head}","merges_url":"https://api.github.com/repos/RackHD/on-http/merges","archive_url":"https://api.github.com/repos/RackHD/on-http/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/RackHD/on-http/downloads","issues_url":"https://api.github.com/repos/RackHD/on-http/issues{/number}","pulls_url":"https://api.github.com/repos/RackHD/on-http/pulls{/number}","milestones_url":"https://api.github.com/repos/RackHD/on-http/milestones{/number}","notifications_url":"https://api.github.com/repos/RackHD/on-http/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/RackHD/on-http/labels{/name}","releases_url":"https://api.github.com/repos/RackHD/on-http/releases{/id}","created_at":"2015-08-31T17:29:08Z","updated_at":"2015-12-13T17:21:54Z","pushed_at":"2016-01-05T02:35:45Z","git_url":"git://github.com/RackHD/on-http.git","ssh_url":"git@github.com:RackHD/on-http.git","clone_url":"https://github.com/RackHD/on-http.git","svn_url":"https://github.com/RackHD/on-http","homepage":null,"size":1448,"stargazers_count":5,"watchers_count":5,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":22,"mirror_url":null,"open_issues_count":1,"forks":22,"open_issues":1,"watchers":5,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/RackHD/on-http/pulls/80"},"html":{"href":"https://github.com/RackHD/on-http/pull/80"},"issue":{"href":"https://api.github.com/repos/RackHD/on-http/issues/80"},"comments":{"href":"https://api.github.com/repos/RackHD/on-http/issues/80/comments"},"review_comments":{"href":"https://api.github.com/repos/RackHD/on-http/pulls/80/comments"},"review_comment":{"href":"https://api.github.com/repos/RackHD/on-http/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/RackHD/on-http/pulls/80/commits"},"statuses":{"href":"https://api.github.com/repos/RackHD/on-http/statuses/788a938823676441e3fd67922558c6987b221014"}}}}