{"action":"created","comment":{"url":"https://api.github.com/repos/RackHD/on-core/pulls/comments/67355168","id":67355168,"diff_hunk":"@@ -11,111 +11,190 @@ NodeModelFactory.$inject = [\n     '_',\n     'Promise',\n     'Constants',\n+    'Services.Configuration',\n     'Protocol.Waterline'\n ];\n \n-function NodeModelFactory (Model, waterline, _, Promise, Constants, waterlineProtocol) {\n+var bson = require('bson');\n+function NodeModelFactory (Model, waterline, _, Promise, Constants, configuration, waterlineProtocol) {\n+    var dbType = configuration.get('databaseType', 'mongo');\n+\n+    var attributes = {\n+        identifiers: {\n+            type: 'array',\n+            required: false,\n+            index: true\n+        },\n+        name: {\n+            type: 'string',\n+            required: true\n+        },\n+        obmSettings: {\n+            type: 'json',\n+            required: false,\n+            json: true\n+        },\n+        type: {\n+            type: 'string',\n+            enum: _.values(Constants.NodeTypes),\n+            defaultsTo: 'compute',\n+            index: true\n+        },\n+        workflows: {\n+            collection: 'graphobjects',\n+            via: 'node'\n+        },\n+        catalogs: {\n+            collection: 'catalogs',\n+            via: 'node'\n+        },\n+        sku: {\n+            model: 'skus'\n+        },\n+        snmpSettings: {\n+            type: 'json',\n+            json: true,\n+            required: false\n+        },\n+        bootSettings: {\n+            type: 'json',\n+            json: true,\n+            required: false\n+        },\n+        sshSettings: {\n+            type: 'json',\n+            json: true,\n+            required: false\n+        },\n+        autoDiscover: {\n+            type: 'boolean',\n+            defaultsTo: false\n+        },\n+        relations: {\n+            type: 'array',\n+            defaultsTo: []\n+        },\n+        tags: {\n+            type: 'array',\n+            defaultsTo: []\n+        },\n+        // We only count a node as having been discovered if\n+        // a node document exists AND it has any catalogs\n+        // associated with it\n+        discovered: function() {\n+            var self = this;\n+            return waterline.catalogs.findOne({\"node\": self.id})\n+            .then(function(catalog) {\n+                return !_.isEmpty(catalog);\n+            });\n+        }\n+    };\n+\n+    if( dbType !== 'mongo' )  {\n+        // For mongo compatibility we have to add the \n+        // BSON id column to non-mongo DBs\n+        attributes.id = {\n+            type: 'string',\n+            primaryKey: true\n+        };\n+    }\n+\n     return Model.extend({\n-        connection: 'mongo',\n+        connection: dbType,\n         identity: 'nodes',\n-        attributes: {\n-            name: {\n-                type: 'string',\n-                required: true\n-            },\n-            obmSettings: {\n-                type: 'json',\n-                required: false,\n-                json: true\n-            },\n-            type: {\n-                type: 'string',\n-                enum: _.values(Constants.NodeTypes),\n-                defaultsTo: 'compute'\n-            },\n-            workflows: {\n-                collection: 'graphobjects',\n-                via: 'node'\n-            },\n-            catalogs: {\n-                collection: 'catalogs',\n-                via: 'node'\n-            },\n-            sku: {\n-                model: 'skus'\n-            },\n-            snmpSettings: {\n-                type: 'json',\n-                json: true,\n-                required: false\n-            },\n-            bootSettings: {\n-                type: 'json',\n-                json: true,\n-                required: false\n-            },\n-            sshSettings: {\n-                type: 'json',\n-                json: true,\n-                required: false\n-            },\n-            autoDiscover: {\n-                type: 'boolean',\n-                defaultsTo: false\n-            },\n-            relations: {\n-                type: 'array',\n-                defaultsTo: []\n-            },\n-            tags: {\n-                type: 'array',\n-                defaultsTo: []\n-            },\n-            // We only count a node as having been discovered if\n-            // a node document exists AND it has any catalogs\n-            // associated with it\n-            discovered: function() {\n-                var self = this;\n-                return waterline.catalogs.findOne({\"node\": self.id})\n-                .then(function(catalog) {\n-                    return !_.isEmpty(catalog);\n-                });\n-            }\n-        },\n+        attributes: attributes,\n         addTags: function(id, tags) {\n             var self = this;\n-            var query = { _id: waterline.nodes.mongo.objectId(id) };\n-            var update = {\n-                            $addToSet: { tags: { $each: tags } },\n-                            $set: { updatedAt: new Date() }\n-                        };\n-            var options = { new: true };\n-            return self.findAndModifyMongo(query, {}, update, options)\n-            .then(function(record) {\n-                record.id = record._id;\n-                waterlineProtocol.publishRecord(self, 'updated', record);\n-                waterlineProtocol.publishRecord(self, 'tag-added', record);\n-                return record;\n-            });\n+            var dbType = _.first(self.connection);\n+            switch(dbType) {\n+            case 'mongo':\n+                var query = { _id: waterline.nodes.mongo.objectId(id) };\n+                var update = {\n+                                $addToSet: { tags: { $each: tags } },\n+                                $set: { updatedAt: new Date() }\n+                            };\n+                var options = { new: true };\n+                return self.findAndModifyMongo(query, {}, update, options)\n+                .then(function(record) {\n+                    record.id = record._id;\n+                    waterlineProtocol.publishRecord(self, 'updated', record);\n+                    waterlineProtocol.publishRecord(self, 'tag-added', record);\n+                    return record;\n+                });\n+            case 'postgresql':\n+                // sails-postgres stores 'array' types as 'text' containing JSON.  We must\n+                // unfurl the JSON, merge with the request tag array, ensure uniqueness of entries,\n+                // then JSONify the result set and update the tags entries with it.\n+                return self.postgresqlRunLockedQuery('' +\n+                    'WITH t as (select array_agg(elem::text) from nodes, json_array_elements(nodes.tags::json) elem where id = $2), ' +\n+                    's as (select distinct unnest(array_cat(t.array_agg, ARRAY[$1])) from t) ' +\n+                    'UPDATE nodes SET tags = concat( \\'[\\', (select string_agg(unnest,\\',\\') from s), \\']\\'),\"updatedAt\"=$3 WHERE id = $2',\n+                    [ '\"' + tags.join('\",\"') + '\"', id, new Date()])\n+                .then(function() {\n+                    return self.find({id: id})\n+                    .then(function(record) {\n+                        record.id = record._id;\n+                        waterlineProtocol.publishRecord(self, 'updated', record);\n+                        waterlineProtocol.publishRecord(self, 'tag-added', record);\n+                        return record;\n+                    });\n+                });\n+            }\n         },\n+\n         remTags: function(id, tag) {\n             var self = this;\n-            var query = { _id: waterline.nodes.mongo.objectId(id) };\n-            var update = {\n-                            $pull: { tags: tag },\n-                            $set: { updatedAt: new Date() }\n-                        };\n-            var options = { new: true };\n-            return self.findAndModifyMongo(query, {}, update, options)\n-            .then(function(record) {\n-                record.id = record._id;\n-                waterlineProtocol.publishRecord(self, 'updated', record);\n-                waterlineProtocol.publishRecord(self, 'tag-removed', record);\n-                return record;\n-            });\n+            var dbType = _.first(self.connection);\n+            switch(dbType) {\n+            case 'mongo':\n+                var query = { _id: waterline.nodes.mongo.objectId(id) };\n+                var update = {\n+                                $pull: { tags: tag },\n+                                $set: { updatedAt: new Date() }\n+                            };\n+                var options = { new: true };\n+                return self.findAndModifyMongo(query, {}, update, options)\n+                .then(function(record) {\n+                    record.id = record._id;\n+                    waterlineProtocol.publishRecord(self, 'updated', record);\n+                    waterlineProtocol.publishRecord(self, 'tag-removed', record);\n+                    return record;\n+                });\n+            case 'postgresql':\n+                // sails-postgres stores 'array' types as 'text' containing JSON.  We must\n+                // unfurl the JSON, remove the entry from the array, ensure uniqueness of entries,\n+                // then JSONify the result set and update the tags entries with it.\n+                return self.postgresqlRunLockedQuery('' +\n+                    'WITH t as (select array_agg(elem::text) from nodes, json_array_elements(nodes.tags::json) elem where id = $2), ' +","path":"lib/models/node.js","position":250,"original_position":250,"commit_id":"918b3ecb4b5c8a89b9e6b5e610b29476922abb51","original_commit_id":"918b3ecb4b5c8a89b9e6b5e610b29476922abb51","user":{"login":"houndci-bot","id":6697940,"avatar_url":"https://avatars.githubusercontent.com/u/6697940?v=3","gravatar_id":"","url":"https://api.github.com/users/houndci-bot","html_url":"https://github.com/houndci-bot","followers_url":"https://api.github.com/users/houndci-bot/followers","following_url":"https://api.github.com/users/houndci-bot/following{/other_user}","gists_url":"https://api.github.com/users/houndci-bot/gists{/gist_id}","starred_url":"https://api.github.com/users/houndci-bot/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/houndci-bot/subscriptions","organizations_url":"https://api.github.com/users/houndci-bot/orgs","repos_url":"https://api.github.com/users/houndci-bot/repos","events_url":"https://api.github.com/users/houndci-bot/events{/privacy}","received_events_url":"https://api.github.com/users/houndci-bot/received_events","type":"User","site_admin":false},"body":"Line is too long.","created_at":"2016-06-16T14:31:37Z","updated_at":"2016-06-16T14:31:37Z","html_url":"https://github.com/RackHD/on-core/pull/162#discussion_r67355168","pull_request_url":"https://api.github.com/repos/RackHD/on-core/pulls/162","_links":{"self":{"href":"https://api.github.com/repos/RackHD/on-core/pulls/comments/67355168"},"html":{"href":"https://github.com/RackHD/on-core/pull/162#discussion_r67355168"},"pull_request":{"href":"https://api.github.com/repos/RackHD/on-core/pulls/162"}}},"pull_request":{"url":"https://api.github.com/repos/RackHD/on-core/pulls/162","id":74091069,"html_url":"https://github.com/RackHD/on-core/pull/162","diff_url":"https://github.com/RackHD/on-core/pull/162.diff","patch_url":"https://github.com/RackHD/on-core/pull/162.patch","issue_url":"https://api.github.com/repos/RackHD/on-core/issues/162","number":162,"state":"open","locked":false,"title":"Add support for using PostgreSQL as an alternative backend","user":{"login":"zyoung51","id":13998687,"avatar_url":"https://avatars.githubusercontent.com/u/13998687?v=3","gravatar_id":"","url":"https://api.github.com/users/zyoung51","html_url":"https://github.com/zyoung51","followers_url":"https://api.github.com/users/zyoung51/followers","following_url":"https://api.github.com/users/zyoung51/following{/other_user}","gists_url":"https://api.github.com/users/zyoung51/gists{/gist_id}","starred_url":"https://api.github.com/users/zyoung51/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zyoung51/subscriptions","organizations_url":"https://api.github.com/users/zyoung51/orgs","repos_url":"https://api.github.com/users/zyoung51/repos","events_url":"https://api.github.com/users/zyoung51/events{/privacy}","received_events_url":"https://api.github.com/users/zyoung51/received_events","type":"User","site_admin":false},"body":"- this commit adds support for non-workflow related models only","created_at":"2016-06-16T14:31:22Z","updated_at":"2016-06-16T14:31:37Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8c6ccc04fa629b6c4ffaaff0c8763375d6dec746","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/RackHD/on-core/pulls/162/commits","review_comments_url":"https://api.github.com/repos/RackHD/on-core/pulls/162/comments","review_comment_url":"https://api.github.com/repos/RackHD/on-core/pulls/comments{/number}","comments_url":"https://api.github.com/repos/RackHD/on-core/issues/162/comments","statuses_url":"https://api.github.com/repos/RackHD/on-core/statuses/918b3ecb4b5c8a89b9e6b5e610b29476922abb51","head":{"label":"zyoung51:feature/non-workflow-postgresql","ref":"feature/non-workflow-postgresql","sha":"918b3ecb4b5c8a89b9e6b5e610b29476922abb51","user":{"login":"zyoung51","id":13998687,"avatar_url":"https://avatars.githubusercontent.com/u/13998687?v=3","gravatar_id":"","url":"https://api.github.com/users/zyoung51","html_url":"https://github.com/zyoung51","followers_url":"https://api.github.com/users/zyoung51/followers","following_url":"https://api.github.com/users/zyoung51/following{/other_user}","gists_url":"https://api.github.com/users/zyoung51/gists{/gist_id}","starred_url":"https://api.github.com/users/zyoung51/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zyoung51/subscriptions","organizations_url":"https://api.github.com/users/zyoung51/orgs","repos_url":"https://api.github.com/users/zyoung51/repos","events_url":"https://api.github.com/users/zyoung51/events{/privacy}","received_events_url":"https://api.github.com/users/zyoung51/received_events","type":"User","site_admin":false},"repo":{"id":47285533,"name":"on-core","full_name":"zyoung51/on-core","owner":{"login":"zyoung51","id":13998687,"avatar_url":"https://avatars.githubusercontent.com/u/13998687?v=3","gravatar_id":"","url":"https://api.github.com/users/zyoung51","html_url":"https://github.com/zyoung51","followers_url":"https://api.github.com/users/zyoung51/followers","following_url":"https://api.github.com/users/zyoung51/following{/other_user}","gists_url":"https://api.github.com/users/zyoung51/gists{/gist_id}","starred_url":"https://api.github.com/users/zyoung51/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/zyoung51/subscriptions","organizations_url":"https://api.github.com/users/zyoung51/orgs","repos_url":"https://api.github.com/users/zyoung51/repos","events_url":"https://api.github.com/users/zyoung51/events{/privacy}","received_events_url":"https://api.github.com/users/zyoung51/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/zyoung51/on-core","description":"","fork":true,"url":"https://api.github.com/repos/zyoung51/on-core","forks_url":"https://api.github.com/repos/zyoung51/on-core/forks","keys_url":"https://api.github.com/repos/zyoung51/on-core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/zyoung51/on-core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/zyoung51/on-core/teams","hooks_url":"https://api.github.com/repos/zyoung51/on-core/hooks","issue_events_url":"https://api.github.com/repos/zyoung51/on-core/issues/events{/number}","events_url":"https://api.github.com/repos/zyoung51/on-core/events","assignees_url":"https://api.github.com/repos/zyoung51/on-core/assignees{/user}","branches_url":"https://api.github.com/repos/zyoung51/on-core/branches{/branch}","tags_url":"https://api.github.com/repos/zyoung51/on-core/tags","blobs_url":"https://api.github.com/repos/zyoung51/on-core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/zyoung51/on-core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/zyoung51/on-core/git/refs{/sha}","trees_url":"https://api.github.com/repos/zyoung51/on-core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/zyoung51/on-core/statuses/{sha}","languages_url":"https://api.github.com/repos/zyoung51/on-core/languages","stargazers_url":"https://api.github.com/repos/zyoung51/on-core/stargazers","contributors_url":"https://api.github.com/repos/zyoung51/on-core/contributors","subscribers_url":"https://api.github.com/repos/zyoung51/on-core/subscribers","subscription_url":"https://api.github.com/repos/zyoung51/on-core/subscription","commits_url":"https://api.github.com/repos/zyoung51/on-core/commits{/sha}","git_commits_url":"https://api.github.com/repos/zyoung51/on-core/git/commits{/sha}","comments_url":"https://api.github.com/repos/zyoung51/on-core/comments{/number}","issue_comment_url":"https://api.github.com/repos/zyoung51/on-core/issues/comments{/number}","contents_url":"https://api.github.com/repos/zyoung51/on-core/contents/{+path}","compare_url":"https://api.github.com/repos/zyoung51/on-core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/zyoung51/on-core/merges","archive_url":"https://api.github.com/repos/zyoung51/on-core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/zyoung51/on-core/downloads","issues_url":"https://api.github.com/repos/zyoung51/on-core/issues{/number}","pulls_url":"https://api.github.com/repos/zyoung51/on-core/pulls{/number}","milestones_url":"https://api.github.com/repos/zyoung51/on-core/milestones{/number}","notifications_url":"https://api.github.com/repos/zyoung51/on-core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/zyoung51/on-core/labels{/name}","releases_url":"https://api.github.com/repos/zyoung51/on-core/releases{/id}","deployments_url":"https://api.github.com/repos/zyoung51/on-core/deployments","created_at":"2015-12-02T20:07:46Z","updated_at":"2016-02-07T19:40:02Z","pushed_at":"2016-06-16T14:26:47Z","git_url":"git://github.com/zyoung51/on-core.git","ssh_url":"git@github.com:zyoung51/on-core.git","clone_url":"https://github.com/zyoung51/on-core.git","svn_url":"https://github.com/zyoung51/on-core","homepage":null,"size":1052,"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":"941d2521ce4e2ff436364f63424d7c6df96e4487","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":41690044,"name":"on-core","full_name":"RackHD/on-core","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-core","description":"","fork":false,"url":"https://api.github.com/repos/RackHD/on-core","forks_url":"https://api.github.com/repos/RackHD/on-core/forks","keys_url":"https://api.github.com/repos/RackHD/on-core/keys{/key_id}","collaborators_url":"https://api.github.com/repos/RackHD/on-core/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/RackHD/on-core/teams","hooks_url":"https://api.github.com/repos/RackHD/on-core/hooks","issue_events_url":"https://api.github.com/repos/RackHD/on-core/issues/events{/number}","events_url":"https://api.github.com/repos/RackHD/on-core/events","assignees_url":"https://api.github.com/repos/RackHD/on-core/assignees{/user}","branches_url":"https://api.github.com/repos/RackHD/on-core/branches{/branch}","tags_url":"https://api.github.com/repos/RackHD/on-core/tags","blobs_url":"https://api.github.com/repos/RackHD/on-core/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/RackHD/on-core/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/RackHD/on-core/git/refs{/sha}","trees_url":"https://api.github.com/repos/RackHD/on-core/git/trees{/sha}","statuses_url":"https://api.github.com/repos/RackHD/on-core/statuses/{sha}","languages_url":"https://api.github.com/repos/RackHD/on-core/languages","stargazers_url":"https://api.github.com/repos/RackHD/on-core/stargazers","contributors_url":"https://api.github.com/repos/RackHD/on-core/contributors","subscribers_url":"https://api.github.com/repos/RackHD/on-core/subscribers","subscription_url":"https://api.github.com/repos/RackHD/on-core/subscription","commits_url":"https://api.github.com/repos/RackHD/on-core/commits{/sha}","git_commits_url":"https://api.github.com/repos/RackHD/on-core/git/commits{/sha}","comments_url":"https://api.github.com/repos/RackHD/on-core/comments{/number}","issue_comment_url":"https://api.github.com/repos/RackHD/on-core/issues/comments{/number}","contents_url":"https://api.github.com/repos/RackHD/on-core/contents/{+path}","compare_url":"https://api.github.com/repos/RackHD/on-core/compare/{base}...{head}","merges_url":"https://api.github.com/repos/RackHD/on-core/merges","archive_url":"https://api.github.com/repos/RackHD/on-core/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/RackHD/on-core/downloads","issues_url":"https://api.github.com/repos/RackHD/on-core/issues{/number}","pulls_url":"https://api.github.com/repos/RackHD/on-core/pulls{/number}","milestones_url":"https://api.github.com/repos/RackHD/on-core/milestones{/number}","notifications_url":"https://api.github.com/repos/RackHD/on-core/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/RackHD/on-core/labels{/name}","releases_url":"https://api.github.com/repos/RackHD/on-core/releases{/id}","deployments_url":"https://api.github.com/repos/RackHD/on-core/deployments","created_at":"2015-08-31T17:28:32Z","updated_at":"2016-01-31T09:01:09Z","pushed_at":"2016-06-16T14:31:22Z","git_url":"git://github.com/RackHD/on-core.git","ssh_url":"git@github.com:RackHD/on-core.git","clone_url":"https://github.com/RackHD/on-core.git","svn_url":"https://github.com/RackHD/on-core","homepage":null,"size":1078,"stargazers_count":9,"watchers_count":9,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":37,"mirror_url":null,"open_issues_count":6,"forks":37,"open_issues":6,"watchers":9,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/RackHD/on-core/pulls/162"},"html":{"href":"https://github.com/RackHD/on-core/pull/162"},"issue":{"href":"https://api.github.com/repos/RackHD/on-core/issues/162"},"comments":{"href":"https://api.github.com/repos/RackHD/on-core/issues/162/comments"},"review_comments":{"href":"https://api.github.com/repos/RackHD/on-core/pulls/162/comments"},"review_comment":{"href":"https://api.github.com/repos/RackHD/on-core/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/RackHD/on-core/pulls/162/commits"},"statuses":{"href":"https://api.github.com/repos/RackHD/on-core/statuses/918b3ecb4b5c8a89b9e6b5e610b29476922abb51"}}}}
{"actor":{"display_login":"houndci-bot"}}