{"action":"created","comment":{"url":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/comments/54002777","id":54002777,"diff_hunk":"@@ -0,0 +1,429 @@\n+'use strict';\n+\n+const compact = require('lodash/compact');\n+const concatStream = require('concat-stream');\n+const DecentralizedComputation =\n+    require('../../models/decentralized-computation.js');\n+const fs = require('fs');\n+const GitHubApi = require('github');\n+const https = require('https');\n+const mkdirp = require('mkdirp');\n+const path = require('path');\n+const rimraf = require('rimraf');\n+const spawn = require('child_process').spawn;\n+const tar = require('tar-fs');\n+const url = require('url');\n+const values = require('lodash/values');\n+\n+/**\n+ * Get a computation ID from its name and version.\n+ *\n+ * @param {string} name\n+ * @param {string} version\n+ * @returns {string}\n+ */\n+function getId(name, version) {\n+    return `${name}@${version}`;\n+}\n+\n+/**\n+ * Get a registry filter function.\n+ *\n+ * @param {string} name\n+ * @param {string} version\n+ * @returns {function}\n+ */\n+function registryFilter(name, version) {\n+    return function(registryItem) {\n+        return (\n+            registryItem.name === name &&\n+            registryItem.tags.indexOf(version) !== -1\n+        );\n+    };\n+}\n+\n+/**\n+ * Find tarball URL from GitHub API response.\n+ *\n+ * The GitHub tag API is poorly documented. Response objects aren’t accurate:\n+ * {@link https://developer.github.com/v3/git/tags/}\n+ *\n+ * {@link http://mikedeboer.github.io/node-github/#repos.prototype.getTags}\n+ *\n+ * @todo  The GitHub API paginates tags. This function will need to recursively\n+ * search pages for the tag. There's no tag count/info, so it's impossible to do\n+ * a binary search. Figure out something!\n+ *\n+ * @param {object} options\n+ * @param {GitHubApi} options.github GitHub API client instance\n+ * @param {string} options.repo Repository name\n+ * @param {string} options.user Repository owner\n+ * @param {string} options.version Tag to find\n+ * @param {string} [options.page=1] API page to search (1-based index)\n+ * @param {function} callback\n+ */\n+function findTarballUrl(options, callback) {\n+    const github = options.github;\n+    const repo = options.repo;\n+    const user = options.user;\n+    const version = options.version;\n+    const page = typeof options.page !== 'undefined' ? options.page : 1;\n+\n+    github.repos.getTags({\n+        page: page,\n+\n+        // jscs:disable\n+        per_page: 100, // jshint ignore:line\n+        // jscs:enable\n+        repo: repo,\n+        user: user,\n+    }, (err, res) => {\n+        if (err) {\n+            return callback(err);\n+        }\n+\n+        const tag = res.find(t => t.name === 'v' + version);\n+\n+        // Potentially recurse here\n+        if (!tag) {\n+            return callback(new Error(\n+                `Couldn’t find tag for ${user}/${repo}@${version}`\n+            ));\n+        }\n+\n+        // jscs:disable\n+        callback(null, tag.tarball_url); // jshint ignore:line\n+        // jscs:enable\n+    });\n+}\n+\n+/**\n+ * Get a computation's tarball URL.\n+ *\n+ * @param {object} options\n+ * @param {GitHubApi} options.github GitHub API client instance\n+ * @param {string} options.name\n+ * @param {object[]} options.registry\n+ * @param {string} options.version\n+ * @returns {Promise}\n+ */\n+function getTarballUrl(options) {\n+    const github = options.github;\n+    const name = options.name;\n+    const registry = options.registry;\n+    const version = options.version;\n+\n+    const registryItem = registry.find(registryFilter(name, version));\n+\n+    if (!registryItem) {\n+        return Promise.reject(new Error(\n+            `Couldn’t find URL for ${getId(name, version)}`\n+        ));\n+    }\n+\n+    /**\n+     * @todo  This expects `registryItem.url` to be a GitHub URL. Expand to be\n+     * more flexible.\n+     */\n+    const pathPieces = compact(url.parse(registryItem.url).path.split('/'));\n+\n+    return new Promise((resolve, reject) => {\n+        findTarballUrl({\n+            github: github,\n+            repo: pathPieces[1],\n+            user: pathPieces[0],\n+            version: version,\n+        }, (err, tarballUrl) => {\n+            if (err) {\n+                return reject(err);\n+            }\n+\n+            resolve(tarballUrl);\n+        });\n+    });\n+}\n+\n+/**\n+ * Run `npm install` on a path.\n+ *\n+ * @param {string} path\n+ * @returns {Promise}\n+ */\n+function runNPMInstall(path) {\n+    return new Promise((resolve, reject) => {\n+        const child = spawn('npm', ['install', '--production'], { cwd: path });\n+\n+        let error;\n+        let out;\n+\n+        child.stderr.pipe(concatStream(data => {\n+            error = data.toString();\n+        }));\n+        child.stdout.pipe(concatStream(data => {\n+            out = data.toString();\n+        }));\n+\n+        child.on('close', exitCode => {\n+            if (exitCode) {\n+                return reject(error);\n+            }\n+\n+            resolve(out);\n+        });\n+    });\n+}\n+\n+/**\n+ * Computation registry.\n+ * @class\n+ *\n+ * @example\n+ * const instance = new ComputationRegistry({\n+ *   github: new GitHubApi({...}),\n+ *   path: path.join(__dirname, 'computations'),\n+ *   registry: [{\n+ *     name: 'my-computation',\n+ *     tags: ['1.0.0'],\n+ *     url: 'https://github.com/MRN-Code/coinstac-my-computation',\n+ *   }, {\n+ *     name: 'my-other-computation',\n+ *     tags: ['0.5.1', '0.6.0'],\n+ *     url: 'https://github.com/MRN-Code/coinstac-my-other-computation',\n+ *   }],\n+ * });\n+ *\n+ * @param {object} options\n+ * @param {GitHubApi} options.github GitHub API client instance\n+ * @param {string} options.path Path on disk to cache computations\n+ * @param {object[]} options.registry Collection of computation registry objects\n+ */\n+function ComputationRegistry(options) {\n+    if (!options.github || !(options.github instanceof GitHubApi)) {\n+        throw new TypeError('Expected GitHubApi instance');\n+    }\n+\n+    if (!options.path) {\n+        throw new TypeError('Expected path');\n+    }\n+\n+    if (!options.registry || !Array.isArray(options.registry)) {\n+        throw new TypeError('Expected registry of computations');\n+    }\n+\n+    this.github = options.github;\n+    this.path = options.path;\n+    this.registry = options.registry;\n+    this.store = {};\n+}\n+\n+/**\n+ * Actually add a computation to the store.\n+ *\n+ * @param {string} name\n+ * @param {string} version\n+ * @param {object} definition Raw computation definition\n+ * @returns {Promise} Resolves to `DecentralizedComputation` if match is found\n+ */\n+ComputationRegistry.prototype._doAdd = function(name, version, definition) {\n+    this.store[getId(name, version)] = new DecentralizedComputation(definition);\n+    return this.get(name, version);\n+};\n+\n+/**\n+ * Get a computation's path on disk.\n+ *\n+ * @param {string} name\n+ * @param {string} version\n+ * @returns {string}\n+ */\n+ComputationRegistry.prototype._getComputationPath = function(name, version) {\n+    return path.join(this.path, '/' + getId(name, version));\n+};\n+\n+/**\n+ * Get a computation definition from disk.\n+ *\n+ * @param {string} name\n+ * @param {string} version\n+ * @returns {Promise}\n+ */\n+ComputationRegistry.prototype._getFromDisk = function(name, version) {\n+    const computationPath = this._getComputationPath(name, version);\n+\n+    return new Promise((resolve, reject) => {\n+        fs.stat(computationPath, err => {\n+            if (err) {\n+                return reject(err);\n+            }\n+\n+            try {\n+                resolve(require(computationPath));\n+            } catch (error) {\n+                reject(error);\n+            }\n+        });\n+    });\n+};\n+\n+/**\n+ * Get a computation definition from source (the web).\n+ *\n+ * This has a side effect of saving the computation definition to disk.\n+ *\n+ * @param {string} name\n+ * @param {string} version\n+ * @returns {Promise}\n+ */\n+ComputationRegistry.prototype._getFromSource = function(name, version) {\n+    const path = this._getComputationPath(name, version);\n+\n+    return getTarballUrl({\n+        github: this.github,\n+        name: name,\n+        registry: this.registry,\n+        version: version,\n+    })\n+        .then(tarballUrl => {\n+            return new Promise((resolve, reject) => {\n+                mkdirp(path, err => {\n+                    if (err) {\n+                        return reject(err);\n+                    }\n+\n+                    const tarExtract = tar.extract(path);\n+                    const request = https.request(tarballUrl).pipe(tarExtract);\n+\n+                    request.on('error', error => {\n+                        reject(error);\n+                    });\n+                    tarExtract.on('finish', function() {\n+                        resolve();\n+                    });\n+                });\n+            });\n+        })\n+        .then(() => runNPMInstall(path))\n+        .then(() => this._getFromDisk(name, version));\n+};\n+\n+/**\n+ * Add a decentralized computation to the store.\n+ *\n+ * @param {string} name\n+ * @param {string} version\n+ * @returns {Promise}\n+ */\n+ComputationRegistry.prototype.add = function(name, version) {\n+    const isValid = this.registry.some(registryFilter(name, version));\n+\n+    function doAdd(definition) {\n+        return this._doAdd(name, version, definition); //jshint ignore:line\n+    }\n+\n+    // Check registry to make sure name/version is valid\n+    if (!isValid) {\n+        return Promise.reject(new Error(\n+            `computation ${getId(name, version)} not in registry`\n+        ));\n+    }\n+\n+    // Check the store to see if computation exists\n+    return this.get(name, version)\n+\n+        // Check disk to see if computation is saved\n+        .catch(() => {\n+            return this._getFromDisk(name, version).then(doAdd.bind(this));\n+        })\n+\n+        // Download computation from source, save to Disk\n+        .catch(() => {\n+            return this._getFromSource(name, version).then(doAdd.bind(this));\n+        });\n+};\n+\n+/**\n+ * Get all decentralized computations from store.\n+ *\n+ * @returns {Promise}\n+ */\n+ComputationRegistry.prototype.all = function() {\n+    return Promise.resolve(values(this.store));\n+};\n+\n+/**\n+ * Get a single decentralized computation from store.\n+ *\n+ * @param {string} name\n+ * @param {string} version\n+ * @returns {Promise} Resolves to `DecentralizedComputation` if match is found\n+ */\n+ComputationRegistry.prototype.get = function(name, version) {","path":"src/services/classes/computation-registry.js","position":360,"original_position":360,"commit_id":"850e3eeb4fd0e2a06bd902c6a5c3d5386b90bbc2","original_commit_id":"850e3eeb4fd0e2a06bd902c6a5c3d5386b90bbc2","user":{"login":"cdaringe","id":1003261,"avatar_url":"https://avatars.githubusercontent.com/u/1003261?v=3","gravatar_id":"","url":"https://api.github.com/users/cdaringe","html_url":"https://github.com/cdaringe","followers_url":"https://api.github.com/users/cdaringe/followers","following_url":"https://api.github.com/users/cdaringe/following{/other_user}","gists_url":"https://api.github.com/users/cdaringe/gists{/gist_id}","starred_url":"https://api.github.com/users/cdaringe/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cdaringe/subscriptions","organizations_url":"https://api.github.com/users/cdaringe/orgs","repos_url":"https://api.github.com/users/cdaringe/repos","events_url":"https://api.github.com/users/cdaringe/events{/privacy}","received_events_url":"https://api.github.com/users/cdaringe/received_events","type":"User","site_admin":false},"body":"or a getById, etc etc","created_at":"2016-02-24T20:41:04Z","updated_at":"2016-02-24T20:41:04Z","html_url":"https://github.com/MRN-Code/coinstac-common/pull/14#discussion_r54002777","pull_request_url":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/14","_links":{"self":{"href":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/comments/54002777"},"html":{"href":"https://github.com/MRN-Code/coinstac-common/pull/14#discussion_r54002777"},"pull_request":{"href":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/14"}}},"pull_request":{"url":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/14","id":60430821,"html_url":"https://github.com/MRN-Code/coinstac-common/pull/14","diff_url":"https://github.com/MRN-Code/coinstac-common/pull/14.diff","patch_url":"https://github.com/MRN-Code/coinstac-common/pull/14.patch","issue_url":"https://api.github.com/repos/MRN-Code/coinstac-common/issues/14","number":14,"state":"open","locked":false,"title":"Implement Computation Registry","user":{"login":"swashcap","id":1858316,"avatar_url":"https://avatars.githubusercontent.com/u/1858316?v=3","gravatar_id":"","url":"https://api.github.com/users/swashcap","html_url":"https://github.com/swashcap","followers_url":"https://api.github.com/users/swashcap/followers","following_url":"https://api.github.com/users/swashcap/following{/other_user}","gists_url":"https://api.github.com/users/swashcap/gists{/gist_id}","starred_url":"https://api.github.com/users/swashcap/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/swashcap/subscriptions","organizations_url":"https://api.github.com/users/swashcap/orgs","repos_url":"https://api.github.com/users/swashcap/repos","events_url":"https://api.github.com/users/swashcap/events{/privacy}","received_events_url":"https://api.github.com/users/swashcap/received_events","type":"User","site_admin":false},"body":"Asana task: https://app.asana.com/0/45796090191289/93385617788731\r\n\r\n* **Problem:** Need grab computation models from definitions and pull ‘em into COINSTAC.\r\n* **Solution:** Make a computation registry and a decentralized computation registry.\r\n* **Todo:**\r\n    - [ ] Implement a callback interface?\r\n    - [ ] Beef up doc blocks\r\n\r\nThe computation registry mirrors the db registry in a couple regards:\r\n\r\n* There’s a `ComputationRegistry` class (_src/services/classes/computation-registry.js_) that _shouldn’t_ be accessed by itself. This exposes a registry-like interface:\r\n    * `ComputationRegistry#add`: add a computation to the internal store.\r\n    * `ComputationRegistry#all`\r\n    * `ComputationRegistry#get`\r\n    * `ComputationRegistry#remove`\r\n* There’s a factory (_src/services/computation-registry-factory.js_) that configures `ComputationRegistry` instances intelligently. Instances are returned via Promise:\r\n\r\n    ```js\r\n    computationRegistryFactory().then(myComputationRegistry => {\r\n        myComputationRegistry.add(/*...*/)\r\n    });\r\n    ```\r\n\r\n    The factory is set for the “local” environment. To configure for the remote environment, simple:\r\n\r\n    ```js\r\n    computationRegistryFactory({ isRemote: true }).then(/*...*/);\r\n    ```\r\n\r\n    We shouldn’t need to configure anything else.\r\n\r\nMore notes about the design:\r\n\r\n* The decentralized computation registry is our way of controlling what computations are allowed in COINSTAC. It’s a collection of entries that look like:\r\n\r\n    ```json\r\n    {\r\n      \"name\": \"the-ravens\",\r\n      \"tags\": [\"1.0.0\", \"2.0.0\", \"3.0.0\"],\r\n      \"url\": \"https://github.com/MRN-Code/the-ravens\"\r\n    }\r\n    ```\r\n\r\n    At the moment, _entries must point to a GitHub URL_ due to how we’re There’s a test (_test/decentralized-computations.js_) that ensures anyone modifying the file sticks to the format.\r\n","created_at":"2016-02-23T23:40:01Z","updated_at":"2016-02-24T20:41:04Z","closed_at":null,"merged_at":null,"merge_commit_sha":"eb324c126d2e516851f5c276b8907b321808408a","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/14/commits","review_comments_url":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/14/comments","review_comment_url":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/comments{/number}","comments_url":"https://api.github.com/repos/MRN-Code/coinstac-common/issues/14/comments","statuses_url":"https://api.github.com/repos/MRN-Code/coinstac-common/statuses/850e3eeb4fd0e2a06bd902c6a5c3d5386b90bbc2","head":{"label":"MRN-Code:feature/computation-registry","ref":"feature/computation-registry","sha":"850e3eeb4fd0e2a06bd902c6a5c3d5386b90bbc2","user":{"login":"MRN-Code","id":5330818,"avatar_url":"https://avatars.githubusercontent.com/u/5330818?v=3","gravatar_id":"","url":"https://api.github.com/users/MRN-Code","html_url":"https://github.com/MRN-Code","followers_url":"https://api.github.com/users/MRN-Code/followers","following_url":"https://api.github.com/users/MRN-Code/following{/other_user}","gists_url":"https://api.github.com/users/MRN-Code/gists{/gist_id}","starred_url":"https://api.github.com/users/MRN-Code/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MRN-Code/subscriptions","organizations_url":"https://api.github.com/users/MRN-Code/orgs","repos_url":"https://api.github.com/users/MRN-Code/repos","events_url":"https://api.github.com/users/MRN-Code/events{/privacy}","received_events_url":"https://api.github.com/users/MRN-Code/received_events","type":"Organization","site_admin":false},"repo":{"id":51021937,"name":"coinstac-common","full_name":"MRN-Code/coinstac-common","owner":{"login":"MRN-Code","id":5330818,"avatar_url":"https://avatars.githubusercontent.com/u/5330818?v=3","gravatar_id":"","url":"https://api.github.com/users/MRN-Code","html_url":"https://github.com/MRN-Code","followers_url":"https://api.github.com/users/MRN-Code/followers","following_url":"https://api.github.com/users/MRN-Code/following{/other_user}","gists_url":"https://api.github.com/users/MRN-Code/gists{/gist_id}","starred_url":"https://api.github.com/users/MRN-Code/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MRN-Code/subscriptions","organizations_url":"https://api.github.com/users/MRN-Code/orgs","repos_url":"https://api.github.com/users/MRN-Code/repos","events_url":"https://api.github.com/users/MRN-Code/events{/privacy}","received_events_url":"https://api.github.com/users/MRN-Code/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/MRN-Code/coinstac-common","description":"common models and utilities shared across the coinstac ecosystem","fork":false,"url":"https://api.github.com/repos/MRN-Code/coinstac-common","forks_url":"https://api.github.com/repos/MRN-Code/coinstac-common/forks","keys_url":"https://api.github.com/repos/MRN-Code/coinstac-common/keys{/key_id}","collaborators_url":"https://api.github.com/repos/MRN-Code/coinstac-common/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/MRN-Code/coinstac-common/teams","hooks_url":"https://api.github.com/repos/MRN-Code/coinstac-common/hooks","issue_events_url":"https://api.github.com/repos/MRN-Code/coinstac-common/issues/events{/number}","events_url":"https://api.github.com/repos/MRN-Code/coinstac-common/events","assignees_url":"https://api.github.com/repos/MRN-Code/coinstac-common/assignees{/user}","branches_url":"https://api.github.com/repos/MRN-Code/coinstac-common/branches{/branch}","tags_url":"https://api.github.com/repos/MRN-Code/coinstac-common/tags","blobs_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/refs{/sha}","trees_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/trees{/sha}","statuses_url":"https://api.github.com/repos/MRN-Code/coinstac-common/statuses/{sha}","languages_url":"https://api.github.com/repos/MRN-Code/coinstac-common/languages","stargazers_url":"https://api.github.com/repos/MRN-Code/coinstac-common/stargazers","contributors_url":"https://api.github.com/repos/MRN-Code/coinstac-common/contributors","subscribers_url":"https://api.github.com/repos/MRN-Code/coinstac-common/subscribers","subscription_url":"https://api.github.com/repos/MRN-Code/coinstac-common/subscription","commits_url":"https://api.github.com/repos/MRN-Code/coinstac-common/commits{/sha}","git_commits_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/commits{/sha}","comments_url":"https://api.github.com/repos/MRN-Code/coinstac-common/comments{/number}","issue_comment_url":"https://api.github.com/repos/MRN-Code/coinstac-common/issues/comments{/number}","contents_url":"https://api.github.com/repos/MRN-Code/coinstac-common/contents/{+path}","compare_url":"https://api.github.com/repos/MRN-Code/coinstac-common/compare/{base}...{head}","merges_url":"https://api.github.com/repos/MRN-Code/coinstac-common/merges","archive_url":"https://api.github.com/repos/MRN-Code/coinstac-common/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/MRN-Code/coinstac-common/downloads","issues_url":"https://api.github.com/repos/MRN-Code/coinstac-common/issues{/number}","pulls_url":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls{/number}","milestones_url":"https://api.github.com/repos/MRN-Code/coinstac-common/milestones{/number}","notifications_url":"https://api.github.com/repos/MRN-Code/coinstac-common/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/MRN-Code/coinstac-common/labels{/name}","releases_url":"https://api.github.com/repos/MRN-Code/coinstac-common/releases{/id}","deployments_url":"https://api.github.com/repos/MRN-Code/coinstac-common/deployments","created_at":"2016-02-03T18:44:42Z","updated_at":"2016-02-15T18:42:02Z","pushed_at":"2016-02-24T20:35:08Z","git_url":"git://github.com/MRN-Code/coinstac-common.git","ssh_url":"git@github.com:MRN-Code/coinstac-common.git","clone_url":"https://github.com/MRN-Code/coinstac-common.git","svn_url":"https://github.com/MRN-Code/coinstac-common","homepage":"http://mrn-code.github.io/coinstac-common/","size":602,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":0,"default_branch":"master"}},"base":{"label":"MRN-Code:master","ref":"master","sha":"8a5c01319c2fc33b09d3d52f731ab5eb0dbc642a","user":{"login":"MRN-Code","id":5330818,"avatar_url":"https://avatars.githubusercontent.com/u/5330818?v=3","gravatar_id":"","url":"https://api.github.com/users/MRN-Code","html_url":"https://github.com/MRN-Code","followers_url":"https://api.github.com/users/MRN-Code/followers","following_url":"https://api.github.com/users/MRN-Code/following{/other_user}","gists_url":"https://api.github.com/users/MRN-Code/gists{/gist_id}","starred_url":"https://api.github.com/users/MRN-Code/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MRN-Code/subscriptions","organizations_url":"https://api.github.com/users/MRN-Code/orgs","repos_url":"https://api.github.com/users/MRN-Code/repos","events_url":"https://api.github.com/users/MRN-Code/events{/privacy}","received_events_url":"https://api.github.com/users/MRN-Code/received_events","type":"Organization","site_admin":false},"repo":{"id":51021937,"name":"coinstac-common","full_name":"MRN-Code/coinstac-common","owner":{"login":"MRN-Code","id":5330818,"avatar_url":"https://avatars.githubusercontent.com/u/5330818?v=3","gravatar_id":"","url":"https://api.github.com/users/MRN-Code","html_url":"https://github.com/MRN-Code","followers_url":"https://api.github.com/users/MRN-Code/followers","following_url":"https://api.github.com/users/MRN-Code/following{/other_user}","gists_url":"https://api.github.com/users/MRN-Code/gists{/gist_id}","starred_url":"https://api.github.com/users/MRN-Code/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MRN-Code/subscriptions","organizations_url":"https://api.github.com/users/MRN-Code/orgs","repos_url":"https://api.github.com/users/MRN-Code/repos","events_url":"https://api.github.com/users/MRN-Code/events{/privacy}","received_events_url":"https://api.github.com/users/MRN-Code/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/MRN-Code/coinstac-common","description":"common models and utilities shared across the coinstac ecosystem","fork":false,"url":"https://api.github.com/repos/MRN-Code/coinstac-common","forks_url":"https://api.github.com/repos/MRN-Code/coinstac-common/forks","keys_url":"https://api.github.com/repos/MRN-Code/coinstac-common/keys{/key_id}","collaborators_url":"https://api.github.com/repos/MRN-Code/coinstac-common/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/MRN-Code/coinstac-common/teams","hooks_url":"https://api.github.com/repos/MRN-Code/coinstac-common/hooks","issue_events_url":"https://api.github.com/repos/MRN-Code/coinstac-common/issues/events{/number}","events_url":"https://api.github.com/repos/MRN-Code/coinstac-common/events","assignees_url":"https://api.github.com/repos/MRN-Code/coinstac-common/assignees{/user}","branches_url":"https://api.github.com/repos/MRN-Code/coinstac-common/branches{/branch}","tags_url":"https://api.github.com/repos/MRN-Code/coinstac-common/tags","blobs_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/refs{/sha}","trees_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/trees{/sha}","statuses_url":"https://api.github.com/repos/MRN-Code/coinstac-common/statuses/{sha}","languages_url":"https://api.github.com/repos/MRN-Code/coinstac-common/languages","stargazers_url":"https://api.github.com/repos/MRN-Code/coinstac-common/stargazers","contributors_url":"https://api.github.com/repos/MRN-Code/coinstac-common/contributors","subscribers_url":"https://api.github.com/repos/MRN-Code/coinstac-common/subscribers","subscription_url":"https://api.github.com/repos/MRN-Code/coinstac-common/subscription","commits_url":"https://api.github.com/repos/MRN-Code/coinstac-common/commits{/sha}","git_commits_url":"https://api.github.com/repos/MRN-Code/coinstac-common/git/commits{/sha}","comments_url":"https://api.github.com/repos/MRN-Code/coinstac-common/comments{/number}","issue_comment_url":"https://api.github.com/repos/MRN-Code/coinstac-common/issues/comments{/number}","contents_url":"https://api.github.com/repos/MRN-Code/coinstac-common/contents/{+path}","compare_url":"https://api.github.com/repos/MRN-Code/coinstac-common/compare/{base}...{head}","merges_url":"https://api.github.com/repos/MRN-Code/coinstac-common/merges","archive_url":"https://api.github.com/repos/MRN-Code/coinstac-common/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/MRN-Code/coinstac-common/downloads","issues_url":"https://api.github.com/repos/MRN-Code/coinstac-common/issues{/number}","pulls_url":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls{/number}","milestones_url":"https://api.github.com/repos/MRN-Code/coinstac-common/milestones{/number}","notifications_url":"https://api.github.com/repos/MRN-Code/coinstac-common/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/MRN-Code/coinstac-common/labels{/name}","releases_url":"https://api.github.com/repos/MRN-Code/coinstac-common/releases{/id}","deployments_url":"https://api.github.com/repos/MRN-Code/coinstac-common/deployments","created_at":"2016-02-03T18:44:42Z","updated_at":"2016-02-15T18:42:02Z","pushed_at":"2016-02-24T20:35:08Z","git_url":"git://github.com/MRN-Code/coinstac-common.git","ssh_url":"git@github.com:MRN-Code/coinstac-common.git","clone_url":"https://github.com/MRN-Code/coinstac-common.git","svn_url":"https://github.com/MRN-Code/coinstac-common","homepage":"http://mrn-code.github.io/coinstac-common/","size":602,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/14"},"html":{"href":"https://github.com/MRN-Code/coinstac-common/pull/14"},"issue":{"href":"https://api.github.com/repos/MRN-Code/coinstac-common/issues/14"},"comments":{"href":"https://api.github.com/repos/MRN-Code/coinstac-common/issues/14/comments"},"review_comments":{"href":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/14/comments"},"review_comment":{"href":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/MRN-Code/coinstac-common/pulls/14/commits"},"statuses":{"href":"https://api.github.com/repos/MRN-Code/coinstac-common/statuses/850e3eeb4fd0e2a06bd902c6a5c3d5386b90bbc2"}}}}