GitHub PullRequestReviewCommentEvent at 2014-10-31 09:50:37
Repository: grasshopper
Actor: simong
Payload: {"action":"created","comment":{"url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/comments/19658612","id":19658612,"diff_hunk":"@@ -0,0 +1,356 @@\n+/**\n+ * Copyright (c) 2014 \"Fronteer LTD\"\n+ * Grasshopper Event Engine\n+ *\n+ * This program is free software: you can redistribute it and/or modify\n+ * it under the terms of the GNU Affero General Public License as\n+ * published by the Free Software Foundation, either version 3 of the\n+ * License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU Affero General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU Affero General Public License\n+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n+ */\n+\n+var _ = require('lodash');\n+var assert = require('assert');\n+var util = require('util');\n+\n+var DB = require('gh-core/lib/db');\n+var Grasshopper = require('gh-core');\n+var RestAPI = require('gh-rest');\n+var RestUtil = require('gh-rest/lib/util');\n+\n+/**\n+ * Create the initial test configuration\n+ *\n+ * @return {Object}    config    JSON object containing configuration values for Cassandra, Redis, logging and telemetry\n+ */\n+var createInitialTestConfig = module.exports.createInitialTestConfig = function() {\n+    // Require the configuration file, from here on the configuration should be\n+    // passed around instead of required\n+    var config = require('../../../config');\n+    // Streams can't be deep copied so we stash them in a variable, delete them from the config\n+    // and add them to the final config\n+    var logConfig = config.log;\n+    delete config.log;\n+    config = _.cloneDeep(config);\n+    config.log = logConfig;\n+    config.log.streams = [{\n+        'level': 'trace',\n+        'path': './tests.log'\n+    }];\n+\n+    // Ensure that test data does not find its way into the main database\n+    config.db.database = 'grasshoppertest';\n+    config.db.dropOnStartup = true;\n+\n+    // Ensure that the tests always run on the correct hostname/ports\n+    config.servers.adminHostname = 'admin.grasshopper.com';\n+    config.servers.adminPort = 2000;\n+    config.servers.appsPort = 2001;\n+\n+    return config;\n+};\n+\n+/**\n+ * Set up Grasshopper so tests can be safely executed\n+ *\n+ * @param  {Object}      config         The application configuration\n+ * @param  {Function}    callback       Standard callback function\n+ */\n+var setUpBeforeTests = module.exports.setUpBeforeTests = function(config, callback) {\n+    Grasshopper.init(config, function(err) {\n+        if (err) {\n+            return callback(new Error(err.msg));\n+        }\n+\n+        // Pipe REST events to our test logger\n+        _bindRequestLogger();\n+\n+        // Get a global admin rest client\n+        getGlobalAdminRestClient(function(globalAdminRestClient) {\n+\n+            // Create 2 tenants with 2 apps each\n+            // TODO: Use REST api once the endpoints have been implemented\n+            DB.Tenant.create({'displayName': 'Cambridge University'}).done(function(err, cambridgeTenant) {\n+                if (err) {\n+                    return callback(new Error(err.msg));\n+                }\n+                // TODO: Use REST api once the endpoints have been implemented\n+                DB.Tenant.create({'displayName': 'Oxford University'}).done(function(err, oxfordTenant) {\n+                    if (err) {\n+                        return callback(new Error(err.msg));\n+                    }\n+\n+                    createDefaultTestApps(globalAdminRestClient, cambridgeTenant, 'timetable.cam.ac.uk', function(err, cambridge2013App, cambridge2014App, cambridge2013Admin, cambridge2014Admin) {\n+                        if (err) {\n+                            return callback(new Error(err.msg));\n+                        }\n+                        createDefaultTestApps(globalAdminRestClient, oxfordTenant, 'timetable.oxford.ac.uk', function(err, oxford2013App, oxford2014App, oxford2013Admin, oxford2014Admin) {\n+                            if (err) {\n+                                return callback(new Error(err.msg));\n+                            }\n+\n+                            // Expose the tenant and apps on the global object\n+                            global.tests = {\n+                                'tenants': {\n+                                    'cam': cambridgeTenant,\n+                                    'oxford': oxfordTenant\n+                                },\n+                                'apps': {\n+                                    'cam2013': cambridge2013App,\n+                                    'cam2014': cambridge2014App,\n+                                    'oxford2013': oxford2013App,\n+                                    'oxford2014': oxford2014App\n+                                },\n+                                'admins': {\n+                                    'cam2013': cambridge2013Admin,\n+                                    'cam2014': cambridge2014Admin,\n+                                    'oxford2013': oxford2013Admin,\n+                                    'oxford2014': oxford2014Admin\n+                                }\n+                            };\n+\n+                            return callback();\n+                        });\n+                    });\n+                });\n+            });\n+        });\n+    });\n+};\n+\n+/**\n+ * Bind request logging for gh-rest unit test debugging\n+ *\n+ * @api private\n+ */\n+var _bindRequestLogger = function() {\n+    var requestLog = require('gh-core/lib/logger').logger('request-log');\n+\n+    RestUtil.on('request', function(restCtx, url, method, data) {\n+        requestLog().trace({\n+            'restCtx': restCtx,\n+            'url': url,\n+            'method': method,\n+            'data': data\n+        }, 'Performing REST request');\n+    });\n+\n+    RestUtil.on('response', function(body, res) {\n+        requestLog().trace({ 'res': res, 'body': body }, 'REST Request complete');\n+    });\n+\n+    RestUtil.on('error', function(err, body, res) {\n+        requestLog().error({\n+            'err': err,\n+            'res': res,\n+            'body': body\n+        }, 'An error occurred sending a REST request');\n+    });\n+};\n+\n+/**\n+ * Create 2 timetable applications for a tenant. Each app represents a different year\n+ *\n+ * @param  {RestClient}     globalAdminRestClient   A rest client that is authenticated as the global admin\n+ * @param  {Tenant}         tenant                  The tenant on which to create the application\n+ * @param  {String}         hostSuffix              The suffix for the applications their hostnames\n+ * @param  {Function}       callback                Standard callback function\n+ * @param  {Object}         callback.err            An error that occurred, if any\n+ * @param  {App}            callback.app2013        The 2013 application\n+ * @param  {App}            callback.app2014        The 2014 application\n+ * @param  {User}           callback.admin2013      A user that can administer the 2013 application\n+ * @param  {User}           callback.admin2014      A user that can administer the 2014 application\n+ * @api private\n+ */\n+var createDefaultTestApps = function(globalAdminRestClient, tenant, hostSuffix, callback) {\n+    createDefaultTestApp(globalAdminRestClient, tenant, hostSuffix, 2013, function(err, app2013, admin2013) {\n+        if (err) {\n+            return callback(err);\n+        }\n+\n+        createDefaultTestApp(globalAdminRestClient, tenant, hostSuffix, 2014, function(err, app2014, admin2014) {\n+            if (err) {\n+                return callback(err);\n+            }\n+\n+            return callback(null, app2013, app2014, admin2013, admin2014);\n+        });\n+    });\n+};\n+\n+/**\n+ * Create a timetable application for a given tenant and year\n+ *\n+ * @param  {RestClient}     globalAdminRestClient   A rest client that is authenticated as the global admin\n+ * @param  {Tenant}         tenant                  The tenant on which to create the application\n+ * @param  {String}         hostSuffix              The suffix for the applications their hostnames\n+ * @param  {Number}         year                    The year for which to create the application\n+ * @param  {Function}       callback                Standard callback function\n+ * @param  {Object}         callback.err            An error that occurred, if any\n+ * @param  {App}            callback.app            The application\n+ * @param  {App}            callback.app            A user that can administer the application\n+ * @api private\n+ */\n+var createDefaultTestApp = function(globalAdminRestClient, tenant, hostSuffix, year, callback) {\n+    var app = {\n+        'displayName': year,\n+        'hostname': util.format('%s.%s', year, hostSuffix),\n+        'type': 'timetable',\n+        'enabled': true,\n+        'TenantId': tenant.id\n+    };\n+    // TODO: Use REST api once the endpoints have been implemented\n+    DB.App.create(app).done(function(err, app) {\n+        if (err) {\n+            return callback(err);\n+        }\n+\n+        // Create a user that can act as an admin for this app\n+        generateTestUser(globalAdminRestClient, tenant.id, 'localhost:2001', app.hostname, function(err, user) {\n+            if (err) {\n+                return callback(err);\n+            }\n+\n+            // TODO: Make that user an admin of the app\n+            return callback(null, app, user);\n+        });\n+    });\n+};\n+\n+/**\n+ * Get a REST client that is authenticated as the global admin\n+ *\n+ * @param  {Function}       callback                Standard callback function\n+ * @param  {RestClient}     callback.client         An authenticated rest client\n+ */\n+var getGlobalAdminRestClient = module.exports.getGlobalAdminRestClient = function(callback) {\n+    var options = {\n+        'host': 'localhost:2000',\n+        'hostHeader': 'admin.grasshopper.com',\n+        'authenticationType': 'local',\n+        'username': 'administrator',\n+        'password': 'password'\n+    };\n+    RestAPI.createClient(options, function(err, client) {\n+        assert.ok(!err);\n+        return callback(client);\n+    });\n+};\n+\n+/**\n+ * Generate a number of random users that can be used inside of tests\n+ *\n+ * @param  {RestContext}    restCtx             Standard REST Context object that contains the current tenant URL and the current user credentials","path":"node_modules/gh-tests/lib/util.js","position":250,"original_position":250,"commit_id":"37c573e978d3b22839e71fd5ab89a8c3147f5c14","original_commit_id":"37c573e978d3b22839e71fd5ab89a8c3147f5c14","user":{"login":"simong","id":92180,"avatar_url":"https://avatars.githubusercontent.com/u/92180?v=2","gravatar_id":"","url":"https://api.github.com/users/simong","html_url":"https://github.com/simong","followers_url":"https://api.github.com/users/simong/followers","following_url":"https://api.github.com/users/simong/following{/other_user}","gists_url":"https://api.github.com/users/simong/gists{/gist_id}","starred_url":"https://api.github.com/users/simong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simong/subscriptions","organizations_url":"https://api.github.com/users/simong/orgs","repos_url":"https://api.github.com/users/simong/repos","events_url":"https://api.github.com/users/simong/events{/privacy}","received_events_url":"https://api.github.com/users/simong/received_events","type":"User","site_admin":false},"body":"Yea, we need a rest client so we know on which tenant to create the user.","created_at":"2014-10-31T09:50:37Z","updated_at":"2014-10-31T09:50:37Z","html_url":"https://github.com/fronteerio/grasshopper/pull/1#discussion_r19658612","pull_request_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/1","_links":{"self":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/comments/19658612"},"html":{"href":"https://github.com/fronteerio/grasshopper/pull/1#discussion_r19658612"},"pull_request":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/1"}}},"pull_request":{"url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/1","id":23555766,"html_url":"https://github.com/fronteerio/grasshopper/pull/1","diff_url":"https://github.com/fronteerio/grasshopper/pull/1.diff","patch_url":"https://github.com/fronteerio/grasshopper/pull/1.patch","issue_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/1","number":1,"state":"open","locked":false,"title":"Initial test and REST client framework","user":{"login":"simong","id":92180,"avatar_url":"https://avatars.githubusercontent.com/u/92180?v=2","gravatar_id":"","url":"https://api.github.com/users/simong","html_url":"https://github.com/simong","followers_url":"https://api.github.com/users/simong/followers","following_url":"https://api.github.com/users/simong/following{/other_user}","gists_url":"https://api.github.com/users/simong/gists{/gist_id}","starred_url":"https://api.github.com/users/simong/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/simong/subscriptions","organizations_url":"https://api.github.com/users/simong/orgs","repos_url":"https://api.github.com/users/simong/repos","events_url":"https://api.github.com/users/simong/events{/privacy}","received_events_url":"https://api.github.com/users/simong/received_events","type":"User","site_admin":false},"body":"This PR contains a rough outline of:\r\n * the test framework\r\n * the REST client framework\r\n * linting","created_at":"2014-10-29T19:00:33Z","updated_at":"2014-10-31T09:50:37Z","closed_at":null,"merged_at":null,"merge_commit_sha":"8d503f6347ad405ea94460eda141d877e18dd381","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/1/commits","review_comments_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/1/comments","review_comment_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls/comments/{number}","comments_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/1/comments","statuses_url":"https://api.github.com/repos/fronteerio/grasshopper/statuses/37c573e978d3b22839e71fd5ab89a8c3147f5c14","head":{"label":"fronteerio:tests-rest","ref":"tests-rest","sha":"37c573e978d3b22839e71fd5ab89a8c3147f5c14","user":{"login":"fronteerio","id":9116505,"avatar_url":"https://avatars.githubusercontent.com/u/9116505?v=2","gravatar_id":"","url":"https://api.github.com/users/fronteerio","html_url":"https://github.com/fronteerio","followers_url":"https://api.github.com/users/fronteerio/followers","following_url":"https://api.github.com/users/fronteerio/following{/other_user}","gists_url":"https://api.github.com/users/fronteerio/gists{/gist_id}","starred_url":"https://api.github.com/users/fronteerio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fronteerio/subscriptions","organizations_url":"https://api.github.com/users/fronteerio/orgs","repos_url":"https://api.github.com/users/fronteerio/repos","events_url":"https://api.github.com/users/fronteerio/events{/privacy}","received_events_url":"https://api.github.com/users/fronteerio/received_events","type":"Organization","site_admin":false},"repo":{"id":24999124,"name":"grasshopper","full_name":"fronteerio/grasshopper","owner":{"login":"fronteerio","id":9116505,"avatar_url":"https://avatars.githubusercontent.com/u/9116505?v=2","gravatar_id":"","url":"https://api.github.com/users/fronteerio","html_url":"https://github.com/fronteerio","followers_url":"https://api.github.com/users/fronteerio/followers","following_url":"https://api.github.com/users/fronteerio/following{/other_user}","gists_url":"https://api.github.com/users/fronteerio/gists{/gist_id}","starred_url":"https://api.github.com/users/fronteerio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fronteerio/subscriptions","organizations_url":"https://api.github.com/users/fronteerio/orgs","repos_url":"https://api.github.com/users/fronteerio/repos","events_url":"https://api.github.com/users/fronteerio/events{/privacy}","received_events_url":"https://api.github.com/users/fronteerio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/fronteerio/grasshopper","description":"Grasshopper Event Engine","fork":false,"url":"https://api.github.com/repos/fronteerio/grasshopper","forks_url":"https://api.github.com/repos/fronteerio/grasshopper/forks","keys_url":"https://api.github.com/repos/fronteerio/grasshopper/keys{/key_id}","collaborators_url":"https://api.github.com/repos/fronteerio/grasshopper/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/fronteerio/grasshopper/teams","hooks_url":"https://api.github.com/repos/fronteerio/grasshopper/hooks","issue_events_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/events{/number}","events_url":"https://api.github.com/repos/fronteerio/grasshopper/events","assignees_url":"https://api.github.com/repos/fronteerio/grasshopper/assignees{/user}","branches_url":"https://api.github.com/repos/fronteerio/grasshopper/branches{/branch}","tags_url":"https://api.github.com/repos/fronteerio/grasshopper/tags","blobs_url":"https://api.github.com/repos/fronteerio/grasshopper/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/fronteerio/grasshopper/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/fronteerio/grasshopper/git/refs{/sha}","trees_url":"https://api.github.com/repos/fronteerio/grasshopper/git/trees{/sha}","statuses_url":"https://api.github.com/repos/fronteerio/grasshopper/statuses/{sha}","languages_url":"https://api.github.com/repos/fronteerio/grasshopper/languages","stargazers_url":"https://api.github.com/repos/fronteerio/grasshopper/stargazers","contributors_url":"https://api.github.com/repos/fronteerio/grasshopper/contributors","subscribers_url":"https://api.github.com/repos/fronteerio/grasshopper/subscribers","subscription_url":"https://api.github.com/repos/fronteerio/grasshopper/subscription","commits_url":"https://api.github.com/repos/fronteerio/grasshopper/commits{/sha}","git_commits_url":"https://api.github.com/repos/fronteerio/grasshopper/git/commits{/sha}","comments_url":"https://api.github.com/repos/fronteerio/grasshopper/comments{/number}","issue_comment_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/comments/{number}","contents_url":"https://api.github.com/repos/fronteerio/grasshopper/contents/{+path}","compare_url":"https://api.github.com/repos/fronteerio/grasshopper/compare/{base}...{head}","merges_url":"https://api.github.com/repos/fronteerio/grasshopper/merges","archive_url":"https://api.github.com/repos/fronteerio/grasshopper/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/fronteerio/grasshopper/downloads","issues_url":"https://api.github.com/repos/fronteerio/grasshopper/issues{/number}","pulls_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls{/number}","milestones_url":"https://api.github.com/repos/fronteerio/grasshopper/milestones{/number}","notifications_url":"https://api.github.com/repos/fronteerio/grasshopper/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/fronteerio/grasshopper/labels{/name}","releases_url":"https://api.github.com/repos/fronteerio/grasshopper/releases{/id}","created_at":"2014-10-09T16:51:39Z","updated_at":"2014-10-11T21:56:25Z","pushed_at":"2014-10-30T10:48:03Z","git_url":"git://github.com/fronteerio/grasshopper.git","ssh_url":"git@github.com:fronteerio/grasshopper.git","clone_url":"https://github.com/fronteerio/grasshopper.git","svn_url":"https://github.com/fronteerio/grasshopper","homepage":null,"size":412,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":2,"forks":1,"open_issues":2,"watchers":0,"default_branch":"master"}},"base":{"label":"fronteerio:master","ref":"master","sha":"d7b20955eeab53764999e49683dd73705dda9172","user":{"login":"fronteerio","id":9116505,"avatar_url":"https://avatars.githubusercontent.com/u/9116505?v=2","gravatar_id":"","url":"https://api.github.com/users/fronteerio","html_url":"https://github.com/fronteerio","followers_url":"https://api.github.com/users/fronteerio/followers","following_url":"https://api.github.com/users/fronteerio/following{/other_user}","gists_url":"https://api.github.com/users/fronteerio/gists{/gist_id}","starred_url":"https://api.github.com/users/fronteerio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fronteerio/subscriptions","organizations_url":"https://api.github.com/users/fronteerio/orgs","repos_url":"https://api.github.com/users/fronteerio/repos","events_url":"https://api.github.com/users/fronteerio/events{/privacy}","received_events_url":"https://api.github.com/users/fronteerio/received_events","type":"Organization","site_admin":false},"repo":{"id":24999124,"name":"grasshopper","full_name":"fronteerio/grasshopper","owner":{"login":"fronteerio","id":9116505,"avatar_url":"https://avatars.githubusercontent.com/u/9116505?v=2","gravatar_id":"","url":"https://api.github.com/users/fronteerio","html_url":"https://github.com/fronteerio","followers_url":"https://api.github.com/users/fronteerio/followers","following_url":"https://api.github.com/users/fronteerio/following{/other_user}","gists_url":"https://api.github.com/users/fronteerio/gists{/gist_id}","starred_url":"https://api.github.com/users/fronteerio/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/fronteerio/subscriptions","organizations_url":"https://api.github.com/users/fronteerio/orgs","repos_url":"https://api.github.com/users/fronteerio/repos","events_url":"https://api.github.com/users/fronteerio/events{/privacy}","received_events_url":"https://api.github.com/users/fronteerio/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/fronteerio/grasshopper","description":"Grasshopper Event Engine","fork":false,"url":"https://api.github.com/repos/fronteerio/grasshopper","forks_url":"https://api.github.com/repos/fronteerio/grasshopper/forks","keys_url":"https://api.github.com/repos/fronteerio/grasshopper/keys{/key_id}","collaborators_url":"https://api.github.com/repos/fronteerio/grasshopper/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/fronteerio/grasshopper/teams","hooks_url":"https://api.github.com/repos/fronteerio/grasshopper/hooks","issue_events_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/events{/number}","events_url":"https://api.github.com/repos/fronteerio/grasshopper/events","assignees_url":"https://api.github.com/repos/fronteerio/grasshopper/assignees{/user}","branches_url":"https://api.github.com/repos/fronteerio/grasshopper/branches{/branch}","tags_url":"https://api.github.com/repos/fronteerio/grasshopper/tags","blobs_url":"https://api.github.com/repos/fronteerio/grasshopper/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/fronteerio/grasshopper/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/fronteerio/grasshopper/git/refs{/sha}","trees_url":"https://api.github.com/repos/fronteerio/grasshopper/git/trees{/sha}","statuses_url":"https://api.github.com/repos/fronteerio/grasshopper/statuses/{sha}","languages_url":"https://api.github.com/repos/fronteerio/grasshopper/languages","stargazers_url":"https://api.github.com/repos/fronteerio/grasshopper/stargazers","contributors_url":"https://api.github.com/repos/fronteerio/grasshopper/contributors","subscribers_url":"https://api.github.com/repos/fronteerio/grasshopper/subscribers","subscription_url":"https://api.github.com/repos/fronteerio/grasshopper/subscription","commits_url":"https://api.github.com/repos/fronteerio/grasshopper/commits{/sha}","git_commits_url":"https://api.github.com/repos/fronteerio/grasshopper/git/commits{/sha}","comments_url":"https://api.github.com/repos/fronteerio/grasshopper/comments{/number}","issue_comment_url":"https://api.github.com/repos/fronteerio/grasshopper/issues/comments/{number}","contents_url":"https://api.github.com/repos/fronteerio/grasshopper/contents/{+path}","compare_url":"https://api.github.com/repos/fronteerio/grasshopper/compare/{base}...{head}","merges_url":"https://api.github.com/repos/fronteerio/grasshopper/merges","archive_url":"https://api.github.com/repos/fronteerio/grasshopper/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/fronteerio/grasshopper/downloads","issues_url":"https://api.github.com/repos/fronteerio/grasshopper/issues{/number}","pulls_url":"https://api.github.com/repos/fronteerio/grasshopper/pulls{/number}","milestones_url":"https://api.github.com/repos/fronteerio/grasshopper/milestones{/number}","notifications_url":"https://api.github.com/repos/fronteerio/grasshopper/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/fronteerio/grasshopper/labels{/name}","releases_url":"https://api.github.com/repos/fronteerio/grasshopper/releases{/id}","created_at":"2014-10-09T16:51:39Z","updated_at":"2014-10-11T21:56:25Z","pushed_at":"2014-10-30T10:48:03Z","git_url":"git://github.com/fronteerio/grasshopper.git","ssh_url":"git@github.com:fronteerio/grasshopper.git","clone_url":"https://github.com/fronteerio/grasshopper.git","svn_url":"https://github.com/fronteerio/grasshopper","homepage":null,"size":412,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":2,"forks":1,"open_issues":2,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/1"},"html":{"href":"https://github.com/fronteerio/grasshopper/pull/1"},"issue":{"href":"https://api.github.com/repos/fronteerio/grasshopper/issues/1"},"comments":{"href":"https://api.github.com/repos/fronteerio/grasshopper/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/comments/{number}"},"commits":{"href":"https://api.github.com/repos/fronteerio/grasshopper/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/fronteerio/grasshopper/statuses/37c573e978d3b22839e71fd5ab89a8c3147f5c14"}}}}