{"action":"created","comment":{"url":"https://api.github.com/repos/GPII/universal/pulls/comments/126012398","pull_request_review_id":48459635,"id":126012398,"diff_hunk":"@@ -0,0 +1,396 @@\n+/*\n+ * Event logging.\n+ *\n+ * Copyright 2017 Raising the Floor - International\n+ *\n+ * Licensed under the New BSD license. You may not use this file except in\n+ * compliance with this License.\n+ *\n+ * The R&D leading to these results received funding from the\n+ * Department of Education - Grant H421A150005 (GPII-APCP). However,\n+ * these results do not necessarily represent the policy of the\n+ * Department of Education, and you should not assume endorsement by the\n+ * Federal Government.\n+ *\n+ * You may obtain a copy of the License at\n+ * https://github.com/GPII/universal/blob/master/LICENSE.txt\n+ */\n+\n+\"use strict\";\n+\n+var fluid = require(\"infusion\");\n+var fs = require(\"fs\"),\n+    path = require(\"path\");\n+\n+var gpii = fluid.registerNamespace(\"gpii\");\n+fluid.registerNamespace(\"gpii.eventLog\");\n+\n+fluid.defaults(\"gpii.eventLog\", {\n+    gradeNames: [\"fluid.component\", \"fluid.contextAware\"],\n+    contextAwareness: {\n+        platform: {\n+            checks: {\n+                windows: {\n+                    contextValue: \"{gpii.contexts.windows}\",\n+                    gradeNames: \"gpii.eventLog.windows\"\n+                }\n+            }\n+        }\n+    },\n+    components: {\n+        installID: {\n+            type: \"gpii.installID\"\n+        }\n+    },\n+    invokers: {\n+        logEvent: \"gpii.eventLog.log\",\n+        logTimedEvent: \"gpii.eventLog.logDuration\"\n+    },\n+    listeners: {\n+        \"onCreate.installId\": {\n+            func: \"gpii.eventLog.setInstallId\",\n+            args: [\"@expand:{that}.installID.getInstallID()\"]\n+        },\n+        \"onCreate.log\": {\n+            func: \"{that}.logEvent\",\n+            args: [\"gpii\", \"Start\", {}, \"gpii-start\"]\n+        },\n+        \"onDestroy.log\": {\n+            func: \"{that}.logTimedEvent\",\n+            args: [\"gpii\", \"Stop\", {}, \"gpii-start\"]\n+        },\n+        \"{lifecycleManager}.events.onSessionStart\": {\n+            namespace: \"eventLog\",\n+            func: \"{that}.logEvent\",\n+            args: [\"lifecycle\", \"SessionStart\", {userToken: \"{arguments}.1\"}]\n+        },\n+        \"{lifecycleManager}.events.onSessionStop\": {\n+            namespace: \"eventLog\",\n+            func: \"{that}.logTimedEvent\",\n+            args: [\n+                \"lifecycle\",\n+                \"SessionStop\",\n+                {userToken: \"{arguments}.1.model.userToken\"},\n+                \"{arguments}.1.createTime\",\n+                \"sessionTime\"\n+            ]\n+        }\n+    }\n+});\n+\n+/**\n+ * The timers for timed events.\n+ */\n+gpii.eventLog.timers = {};\n+\n+/**\n+ * The outstanding log entries waiting for their duration.\n+ */\n+gpii.eventLog.pendingLogs = {};\n+\n+/**\n+ * The log file.\n+ */\n+gpii.eventLog.logFilePath = null;\n+\n+/**\n+ * The installation ID\n+ */\n+gpii.eventLog.installationID = null;\n+\n+/**\n+ * Gets (and stores) the installation ID.\n+ * @param installationId The installation ID.\n+ */\n+gpii.eventLog.setInstallId = function (installationId) {\n+    gpii.eventLog.installationID = installationId;\n+};\n+\n+/**\n+ * Returns the actual Date and time\n+ */\n+gpii.eventLog.getTimestamp = function () {\n+    return new Date();\n+};\n+\n+/**\n+ * Creates an object for the log. Everything in this object is what will be logged, and the \"time\" field will be added\n+ * later when it is actually logged.\n+ *\n+ * @param module {String} The part of GPII causing this event.\n+ * @param event {String} Name of the event.\n+ * @param data [optional] Event specific data.\n+ * @param level {Object} [optional] Level of the log, see fluid.logLevelsSpec [FATAL,FAIL,WARN,IMPORTANT,INFO,TRACE].\n+ * @return {Object} The log object.\n+ */\n+gpii.eventLog.createLogObject = function (module, event, data, level) {\n+    var eventObject = {\n+        module: module || \"GPII\",\n+        event: event,\n+        level: level\n+    };\n+\n+    var dataWrapped = gpii.eventLog.wrapData(data);\n+    if (dataWrapped !== null) {\n+        eventObject.data = fluid.copy(dataWrapped);\n+    }\n+    return eventObject;\n+};\n+\n+/**\n+ * Logs an event.\n+ *\n+ * If this event is the start of something (like \"StartSession\"), then fill in the timerId parameter and log the\n+ * corresponding \"end\" event (eg, \"StopSession\") using logDuration().\n+ *\n+ * @param module {String} The part of GPII causing this event.\n+ * @param event {String} The event name.\n+ * @param data {Object} [optional] Event specific data.\n+ * @param level {Object} [optional] Level of the log, see fluid.logLevelsSpec [FATAL,FAIL,WARN,IMPORTANT,INFO,TRACE].\n+ * @param timerId {String} [optional] Unique identifier for a timer, if this event is the start of a duration.\n+ */\n+gpii.eventLog.log = function (module, event, data, level, timerId) {\n+    var eventObject = gpii.eventLog.createLogObject(module, event, data);\n+    if (timerId) {\n+        gpii.eventLog.startTimer(timerId);\n+    }\n+\n+    gpii.eventLog.writeLog(level, eventObject);\n+};\n+\n+/**\n+ * Logs an event, where the event marks the end of a duration. This will add a \"duration\" field to the data object with\n+ * the number of seconds since the corresponding start event (the same timerId).\n+ *\n+ * There should already have been a call to eventLog.log with a corresponding timerId.\n+ *\n+ * @param module {String|Object} The part of GPII causing this event, or an object containing the module, event, and\n+ * data (this object will be modified).\n+ * @param event {String} The event name.\n+ * @param data {Object} [optional] Event specific data.\n+ * @param level {Object} [optional] Level of the log, see fluid.logLevelsSpec [FATAL,FAIL,WARN,IMPORTANT,INFO,TRACE].\n+ * @param timerId {Number|String} The timerId (or a start time (ms)).\n+ */\n+gpii.eventLog.logDuration = function (module, event, data, level, timerId) {\n+    var eventObject;\n+\n+    if (module && module.module) {\n+        eventObject = module;\n+    } else {\n+        eventObject = gpii.eventLog.createLogObject(module, event, data);\n+    }\n+\n+    var duration = gpii.eventLog.stopTimer(timerId);\n+    if (duration || duration === 0) {\n+        if (!eventObject.data) {\n+            eventObject.data = {};\n+        }\n+        eventObject.data.duration = duration;\n+    }\n+\n+    gpii.eventLog.writeLog(level, eventObject);\n+};\n+\n+/**\n+ * Defers logging of an event until a later time (when logDeferred() is called with the same timerId, or the application\n+ * exits).\n+ *\n+ * The purpose of this function is to prepare a duration log entry for later, but without requiring the caller to\n+ * remember the details when the duration is over. This entry will be logged if GPII closes before a corresponding call\n+ * of logDeferred.\n+ *\n+ * @param module {String} The part of GPII causing this event.\n+ * @param event {String} The event name.\n+ * @param data {Object} Event specific data.\n+ * @param level {Object} [optional] Level of the log, see fluid.logLevelsSpec [FATAL,FAIL,WARN,IMPORTANT,INFO,TRACE].\n+ * @param timerId {String} The timerId.\n+ */\n+gpii.eventLog.defer = function (module, event, data, level, timerId) {\n+    if (!timerId) {\n+        timerId = level;\n+        level = null;\n+    }\n+    gpii.eventLog.startTimer(timerId);\n+    gpii.eventLog.pendingLogs[timerId] = gpii.eventLog.createLogObject(module, event, data, level);\n+};\n+\n+/**\n+ * Logs a deferred entry, setting the duration field to the data object.\n+ *\n+ * @param timerId {String} The same timerId for the call to defer().\n+ */\n+gpii.eventLog.logDeferred = function (timerId) {\n+    var eventObject = gpii.eventLog.pendingLogs[timerId];\n+    if (eventObject) {\n+        delete gpii.eventLog.pendingLogs[timerId];\n+\n+        gpii.eventLog.logDuration(eventObject, null, null, null, timerId);\n+    } else {\n+        fluid.log(\"logDeferred: Unknown timerId\", timerId);\n+\n+    }\n+};\n+\n+/**\n+ * Logs an error.\n+ *\n+ * @param module {String} The part of GPII causing this error.\n+ * @param errType {String} Type of error.\n+ * @param err {Object} The error.\n+ */\n+gpii.eventLog.logError = function (module, errType, err) {\n+    var data = {};\n+    if (err instanceof Error) {\n+        // Error doesn't serialise\n+        data.error = {};\n+        fluid.each(Object.getOwnPropertyNames(err), function (a) {\n+            data.error[a] = err[a];\n+        });\n+    } else {\n+        data.error = err;\n+    }\n+    gpii.eventLog.log(module, \"Error.\" + errType, data, fluid.logLevel.FAIL);\n+};\n+\n+// Log uncaught exceptions.\n+fluid.onUncaughtException.addListener(function (err) {\n+    gpii.eventLog.logError(null, \"UncaughtException\", err);\n+}, \"eventLog\");\n+\n+// Log fluid.fail.\n+fluid.failureEvent.addListener(function (args) {\n+    var err = Array.isArray(args) ? args.join(\" \") : err;\n+    gpii.eventLog.logError(null, \"Failure\", err);\n+}, \"eventLog\", \"before:fail\");\n+\n+/**\n+ * Gets the path of the new log file for this instance of gpii.\n+ *\n+ * @return {String} Path of the log file.\n+ */\n+gpii.eventLog.getLogFilePath = function () {\n+    if (!gpii.eventLog.logFilePath) {\n+        var startupTime = Date.now();\n+        gpii.eventLog.logFilePath = process.env.GPII_EVENT_LOG;\n+\n+        if (!gpii.eventLog.logFilePath) {\n+            var settingsDirComponent = gpii.settingsDir();","path":"gpii/node_modules/eventLog/src/eventLog.js","position":277,"original_position":277,"commit_id":"87b3c7090da19db75ceb8348b05c990a7e0d0dc1","original_commit_id":"87b3c7090da19db75ceb8348b05c990a7e0d0dc1","user":{"login":"amb26","id":561418,"avatar_url":"https://avatars3.githubusercontent.com/u/561418?v=3","gravatar_id":"","url":"https://api.github.com/users/amb26","html_url":"https://github.com/amb26","followers_url":"https://api.github.com/users/amb26/followers","following_url":"https://api.github.com/users/amb26/following{/other_user}","gists_url":"https://api.github.com/users/amb26/gists{/gist_id}","starred_url":"https://api.github.com/users/amb26/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/amb26/subscriptions","organizations_url":"https://api.github.com/users/amb26/orgs","repos_url":"https://api.github.com/users/amb26/repos","events_url":"https://api.github.com/users/amb26/events{/privacy}","received_events_url":"https://api.github.com/users/amb26/received_events","type":"User","site_admin":false},"body":"Make this a proper subcomponent, and this function into an invoker which references it. Standalone Infusion components have awkward implications, and will leak, for example","created_at":"2017-07-06T20:51:47Z","updated_at":"2017-07-06T20:51:47Z","html_url":"https://github.com/GPII/universal/pull/515#discussion_r126012398","pull_request_url":"https://api.github.com/repos/GPII/universal/pulls/515","_links":{"self":{"href":"https://api.github.com/repos/GPII/universal/pulls/comments/126012398"},"html":{"href":"https://github.com/GPII/universal/pull/515#discussion_r126012398"},"pull_request":{"href":"https://api.github.com/repos/GPII/universal/pulls/515"}}},"pull_request":{"url":"https://api.github.com/repos/GPII/universal/pulls/515","id":124221692,"html_url":"https://github.com/GPII/universal/pull/515","diff_url":"https://github.com/GPII/universal/pull/515.diff","patch_url":"https://github.com/GPII/universal/pull/515.patch","issue_url":"https://api.github.com/repos/GPII/universal/issues/515","number":515,"state":"open","locked":false,"title":"GPII-2333/2398:Log improvements (for metrics capture).","user":{"login":"stegru","id":1867587,"avatar_url":"https://avatars1.githubusercontent.com/u/1867587?v=3","gravatar_id":"","url":"https://api.github.com/users/stegru","html_url":"https://github.com/stegru","followers_url":"https://api.github.com/users/stegru/followers","following_url":"https://api.github.com/users/stegru/following{/other_user}","gists_url":"https://api.github.com/users/stegru/gists{/gist_id}","starred_url":"https://api.github.com/users/stegru/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stegru/subscriptions","organizations_url":"https://api.github.com/users/stegru/orgs","repos_url":"https://api.github.com/users/stegru/repos","events_url":"https://api.github.com/users/stegru/events{/privacy}","received_events_url":"https://api.github.com/users/stegru/received_events","type":"User","site_admin":false},"body":"Added an eventLog module that sends interesting things to a JSON formatted log file.\r\n\r\nInstead of fouling our debug output by converting it all to JSON (and perhaps having to sift through every fluid.log), a different log file will be created that contains only the things that are interesting enough to send to the cloud for analysis.\r\n\r\nThis PR causes the following to be logged:\r\n- GPII start/stop\r\n- Key in/out\r\n- Unhandled errors and fluid.fail\r\n- The installation ID\r\n\r\nIn hindsight, I should have named the branch [GPII-2398](https://issues.gpii.net/browse/GPII-2398), but I was sort of doing this at the same time as [GPII-2333](https://issues.gpii.net/browse/GPII-2333) and didn't expect it to be so uncoupled.\r\n","created_at":"2017-06-06T15:35:56Z","updated_at":"2017-07-06T20:51:47Z","closed_at":null,"merged_at":null,"merge_commit_sha":"e9ff1ab650c543f445ad88f8408311ff16965b03","assignee":{"login":"kaspermarkus","id":167427,"avatar_url":"https://avatars1.githubusercontent.com/u/167427?v=3","gravatar_id":"","url":"https://api.github.com/users/kaspermarkus","html_url":"https://github.com/kaspermarkus","followers_url":"https://api.github.com/users/kaspermarkus/followers","following_url":"https://api.github.com/users/kaspermarkus/following{/other_user}","gists_url":"https://api.github.com/users/kaspermarkus/gists{/gist_id}","starred_url":"https://api.github.com/users/kaspermarkus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kaspermarkus/subscriptions","organizations_url":"https://api.github.com/users/kaspermarkus/orgs","repos_url":"https://api.github.com/users/kaspermarkus/repos","events_url":"https://api.github.com/users/kaspermarkus/events{/privacy}","received_events_url":"https://api.github.com/users/kaspermarkus/received_events","type":"User","site_admin":false},"assignees":[{"login":"kaspermarkus","id":167427,"avatar_url":"https://avatars1.githubusercontent.com/u/167427?v=3","gravatar_id":"","url":"https://api.github.com/users/kaspermarkus","html_url":"https://github.com/kaspermarkus","followers_url":"https://api.github.com/users/kaspermarkus/followers","following_url":"https://api.github.com/users/kaspermarkus/following{/other_user}","gists_url":"https://api.github.com/users/kaspermarkus/gists{/gist_id}","starred_url":"https://api.github.com/users/kaspermarkus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kaspermarkus/subscriptions","organizations_url":"https://api.github.com/users/kaspermarkus/orgs","repos_url":"https://api.github.com/users/kaspermarkus/repos","events_url":"https://api.github.com/users/kaspermarkus/events{/privacy}","received_events_url":"https://api.github.com/users/kaspermarkus/received_events","type":"User","site_admin":false}],"requested_reviewers":[],"milestone":null,"commits_url":"https://api.github.com/repos/GPII/universal/pulls/515/commits","review_comments_url":"https://api.github.com/repos/GPII/universal/pulls/515/comments","review_comment_url":"https://api.github.com/repos/GPII/universal/pulls/comments{/number}","comments_url":"https://api.github.com/repos/GPII/universal/issues/515/comments","statuses_url":"https://api.github.com/repos/GPII/universal/statuses/87b3c7090da19db75ceb8348b05c990a7e0d0dc1","head":{"label":"stegru:GPII-2333","ref":"GPII-2333","sha":"87b3c7090da19db75ceb8348b05c990a7e0d0dc1","user":{"login":"stegru","id":1867587,"avatar_url":"https://avatars1.githubusercontent.com/u/1867587?v=3","gravatar_id":"","url":"https://api.github.com/users/stegru","html_url":"https://github.com/stegru","followers_url":"https://api.github.com/users/stegru/followers","following_url":"https://api.github.com/users/stegru/following{/other_user}","gists_url":"https://api.github.com/users/stegru/gists{/gist_id}","starred_url":"https://api.github.com/users/stegru/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stegru/subscriptions","organizations_url":"https://api.github.com/users/stegru/orgs","repos_url":"https://api.github.com/users/stegru/repos","events_url":"https://api.github.com/users/stegru/events{/privacy}","received_events_url":"https://api.github.com/users/stegru/received_events","type":"User","site_admin":false},"repo":{"id":66853682,"name":"universal","full_name":"stegru/universal","owner":{"login":"stegru","id":1867587,"avatar_url":"https://avatars1.githubusercontent.com/u/1867587?v=3","gravatar_id":"","url":"https://api.github.com/users/stegru","html_url":"https://github.com/stegru","followers_url":"https://api.github.com/users/stegru/followers","following_url":"https://api.github.com/users/stegru/following{/other_user}","gists_url":"https://api.github.com/users/stegru/gists{/gist_id}","starred_url":"https://api.github.com/users/stegru/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/stegru/subscriptions","organizations_url":"https://api.github.com/users/stegru/orgs","repos_url":"https://api.github.com/users/stegru/repos","events_url":"https://api.github.com/users/stegru/events{/privacy}","received_events_url":"https://api.github.com/users/stegru/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/stegru/universal","description":"Cross platform, core components of the GPII personalization infrastructure","fork":true,"url":"https://api.github.com/repos/stegru/universal","forks_url":"https://api.github.com/repos/stegru/universal/forks","keys_url":"https://api.github.com/repos/stegru/universal/keys{/key_id}","collaborators_url":"https://api.github.com/repos/stegru/universal/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/stegru/universal/teams","hooks_url":"https://api.github.com/repos/stegru/universal/hooks","issue_events_url":"https://api.github.com/repos/stegru/universal/issues/events{/number}","events_url":"https://api.github.com/repos/stegru/universal/events","assignees_url":"https://api.github.com/repos/stegru/universal/assignees{/user}","branches_url":"https://api.github.com/repos/stegru/universal/branches{/branch}","tags_url":"https://api.github.com/repos/stegru/universal/tags","blobs_url":"https://api.github.com/repos/stegru/universal/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/stegru/universal/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/stegru/universal/git/refs{/sha}","trees_url":"https://api.github.com/repos/stegru/universal/git/trees{/sha}","statuses_url":"https://api.github.com/repos/stegru/universal/statuses/{sha}","languages_url":"https://api.github.com/repos/stegru/universal/languages","stargazers_url":"https://api.github.com/repos/stegru/universal/stargazers","contributors_url":"https://api.github.com/repos/stegru/universal/contributors","subscribers_url":"https://api.github.com/repos/stegru/universal/subscribers","subscription_url":"https://api.github.com/repos/stegru/universal/subscription","commits_url":"https://api.github.com/repos/stegru/universal/commits{/sha}","git_commits_url":"https://api.github.com/repos/stegru/universal/git/commits{/sha}","comments_url":"https://api.github.com/repos/stegru/universal/comments{/number}","issue_comment_url":"https://api.github.com/repos/stegru/universal/issues/comments{/number}","contents_url":"https://api.github.com/repos/stegru/universal/contents/{+path}","compare_url":"https://api.github.com/repos/stegru/universal/compare/{base}...{head}","merges_url":"https://api.github.com/repos/stegru/universal/merges","archive_url":"https://api.github.com/repos/stegru/universal/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/stegru/universal/downloads","issues_url":"https://api.github.com/repos/stegru/universal/issues{/number}","pulls_url":"https://api.github.com/repos/stegru/universal/pulls{/number}","milestones_url":"https://api.github.com/repos/stegru/universal/milestones{/number}","notifications_url":"https://api.github.com/repos/stegru/universal/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/stegru/universal/labels{/name}","releases_url":"https://api.github.com/repos/stegru/universal/releases{/id}","deployments_url":"https://api.github.com/repos/stegru/universal/deployments","created_at":"2016-08-29T15:00:05Z","updated_at":"2016-08-29T15:00:06Z","pushed_at":"2017-07-05T10:16:53Z","git_url":"git://github.com/stegru/universal.git","ssh_url":"git@github.com:stegru/universal.git","clone_url":"https://github.com/stegru/universal.git","svn_url":"https://github.com/stegru/universal","homepage":"gpii.net","size":7422,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"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":"GPII:master","ref":"master","sha":"2936d1656e8beb7d6e555e1da54b27596d0740d1","user":{"login":"GPII","id":1343050,"avatar_url":"https://avatars3.githubusercontent.com/u/1343050?v=3","gravatar_id":"","url":"https://api.github.com/users/GPII","html_url":"https://github.com/GPII","followers_url":"https://api.github.com/users/GPII/followers","following_url":"https://api.github.com/users/GPII/following{/other_user}","gists_url":"https://api.github.com/users/GPII/gists{/gist_id}","starred_url":"https://api.github.com/users/GPII/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GPII/subscriptions","organizations_url":"https://api.github.com/users/GPII/orgs","repos_url":"https://api.github.com/users/GPII/repos","events_url":"https://api.github.com/users/GPII/events{/privacy}","received_events_url":"https://api.github.com/users/GPII/received_events","type":"Organization","site_admin":false},"repo":{"id":3623720,"name":"universal","full_name":"GPII/universal","owner":{"login":"GPII","id":1343050,"avatar_url":"https://avatars3.githubusercontent.com/u/1343050?v=3","gravatar_id":"","url":"https://api.github.com/users/GPII","html_url":"https://github.com/GPII","followers_url":"https://api.github.com/users/GPII/followers","following_url":"https://api.github.com/users/GPII/following{/other_user}","gists_url":"https://api.github.com/users/GPII/gists{/gist_id}","starred_url":"https://api.github.com/users/GPII/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/GPII/subscriptions","organizations_url":"https://api.github.com/users/GPII/orgs","repos_url":"https://api.github.com/users/GPII/repos","events_url":"https://api.github.com/users/GPII/events{/privacy}","received_events_url":"https://api.github.com/users/GPII/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/GPII/universal","description":"Cross platform, core components of the GPII personalization infrastructure","fork":false,"url":"https://api.github.com/repos/GPII/universal","forks_url":"https://api.github.com/repos/GPII/universal/forks","keys_url":"https://api.github.com/repos/GPII/universal/keys{/key_id}","collaborators_url":"https://api.github.com/repos/GPII/universal/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/GPII/universal/teams","hooks_url":"https://api.github.com/repos/GPII/universal/hooks","issue_events_url":"https://api.github.com/repos/GPII/universal/issues/events{/number}","events_url":"https://api.github.com/repos/GPII/universal/events","assignees_url":"https://api.github.com/repos/GPII/universal/assignees{/user}","branches_url":"https://api.github.com/repos/GPII/universal/branches{/branch}","tags_url":"https://api.github.com/repos/GPII/universal/tags","blobs_url":"https://api.github.com/repos/GPII/universal/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/GPII/universal/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/GPII/universal/git/refs{/sha}","trees_url":"https://api.github.com/repos/GPII/universal/git/trees{/sha}","statuses_url":"https://api.github.com/repos/GPII/universal/statuses/{sha}","languages_url":"https://api.github.com/repos/GPII/universal/languages","stargazers_url":"https://api.github.com/repos/GPII/universal/stargazers","contributors_url":"https://api.github.com/repos/GPII/universal/contributors","subscribers_url":"https://api.github.com/repos/GPII/universal/subscribers","subscription_url":"https://api.github.com/repos/GPII/universal/subscription","commits_url":"https://api.github.com/repos/GPII/universal/commits{/sha}","git_commits_url":"https://api.github.com/repos/GPII/universal/git/commits{/sha}","comments_url":"https://api.github.com/repos/GPII/universal/comments{/number}","issue_comment_url":"https://api.github.com/repos/GPII/universal/issues/comments{/number}","contents_url":"https://api.github.com/repos/GPII/universal/contents/{+path}","compare_url":"https://api.github.com/repos/GPII/universal/compare/{base}...{head}","merges_url":"https://api.github.com/repos/GPII/universal/merges","archive_url":"https://api.github.com/repos/GPII/universal/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/GPII/universal/downloads","issues_url":"https://api.github.com/repos/GPII/universal/issues{/number}","pulls_url":"https://api.github.com/repos/GPII/universal/pulls{/number}","milestones_url":"https://api.github.com/repos/GPII/universal/milestones{/number}","notifications_url":"https://api.github.com/repos/GPII/universal/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/GPII/universal/labels{/name}","releases_url":"https://api.github.com/repos/GPII/universal/releases{/id}","deployments_url":"https://api.github.com/repos/GPII/universal/deployments","created_at":"2012-03-05T04:01:44Z","updated_at":"2016-10-27T09:49:05Z","pushed_at":"2017-07-06T20:12:50Z","git_url":"git://github.com/GPII/universal.git","ssh_url":"git@github.com:GPII/universal.git","clone_url":"https://github.com/GPII/universal.git","svn_url":"https://github.com/GPII/universal","homepage":"gpii.net","size":7370,"stargazers_count":21,"watchers_count":21,"language":"JavaScript","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":54,"mirror_url":null,"open_issues_count":37,"forks":54,"open_issues":37,"watchers":21,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/GPII/universal/pulls/515"},"html":{"href":"https://github.com/GPII/universal/pull/515"},"issue":{"href":"https://api.github.com/repos/GPII/universal/issues/515"},"comments":{"href":"https://api.github.com/repos/GPII/universal/issues/515/comments"},"review_comments":{"href":"https://api.github.com/repos/GPII/universal/pulls/515/comments"},"review_comment":{"href":"https://api.github.com/repos/GPII/universal/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/GPII/universal/pulls/515/commits"},"statuses":{"href":"https://api.github.com/repos/GPII/universal/statuses/87b3c7090da19db75ceb8348b05c990a7e0d0dc1"}}}}
{"actor":{"display_login":"amb26"}}