├── .gitignore ├── .dockerignore ├── settings.env ├── Dockerfile ├── docker-compose.yml ├── Makefile ├── .eslintrc.json ├── bin ├── createdb └── main ├── package.json ├── test ├── github_tests.js ├── app_tests.js ├── worker_tests.js └── worker_data.json ├── README.md └── lib ├── github.js ├── db.js ├── app.js └── worker.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode 3 | coverage/ 4 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | /node_modules/* 2 | /test/* 3 | database.db 4 | Dockerfile 5 | Makefile 6 | docker-compose.yml 7 | *.md 8 | -------------------------------------------------------------------------------- /settings.env: -------------------------------------------------------------------------------- 1 | PGHOST= 2 | PGDB= 3 | PGUSER= 4 | PGPASSWORD= 5 | APNKEY= 6 | KEYID= 7 | TEAMID= 8 | APNTOPIC= 9 | NODE_ENV=production -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:11 2 | LABEL maintainer="thedillonb@gmail.com" 3 | 4 | WORKDIR /app 5 | COPY . /app 6 | 7 | RUN npm install 8 | 9 | CMD ["node", "/app/bin/main"] 10 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | main: 2 | image: thedillonb/codehub-push:1.0.5 3 | ports: 4 | - "127.0.0.1:9666:3000" 5 | links: 6 | - postgres 7 | env_file: 8 | - settings.env 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION = 1.0.5 2 | 3 | .PHONY: build push test 4 | 5 | build: test 6 | docker build -t thedillonb/codehub-push:$(VERSION) . 7 | push: build 8 | docker push thedillonb/codehub-push:$(VERSION) 9 | test: 10 | npm t 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["airbnb-base", "prettier"], 3 | "env": { 4 | "mocha": true, 5 | "node": true 6 | }, 7 | "rules": { 8 | "no-restricted-syntax": "off", 9 | "no-await-in-loop": "off" 10 | } 11 | } -------------------------------------------------------------------------------- /bin/createdb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const Database = require('../lib/db'); 3 | 4 | async function main() { 5 | const db = new Database(); 6 | await db.sync(); 7 | process.exit(0); 8 | } 9 | 10 | main().catch(err => { 11 | /* eslint-disable-next-line no-console */ 12 | console.error(err); 13 | process.exit(-1); 14 | }); 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "codehub-push", 3 | "version": "1.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node bin/www", 7 | "test": "eslint lib/**/*.js test/*.js bin/* && mocha", 8 | "coverage": "istanbul cover _mocha" 9 | }, 10 | "dependencies": { 11 | "apn": "^2.2.0", 12 | "async": "^2.6.0", 13 | "body-parser": "^1.18.2", 14 | "express": "^4.16.3", 15 | "express-rate-limit": "^2.11.0", 16 | "express-validator": "^5.1.2", 17 | "http-assert": "^1.4.0", 18 | "morgan": "^1.9.1", 19 | "pg-promise": "^8.4.0", 20 | "request": "^2.88.0", 21 | "winston": "^2.4.2" 22 | }, 23 | "devDependencies": { 24 | "chai": "^4.1.2", 25 | "eslint": "^5.14.1", 26 | "eslint-config-airbnb-base": "^13.1.0", 27 | "eslint-config-prettier": "^2.9.0", 28 | "eslint-plugin-import": "^2.16.0", 29 | "istanbul": "^0.4.5", 30 | "mocha": "^6.0.1", 31 | "nock": "^10.0.6", 32 | "prettier": "^1.16.4", 33 | "sinon": "^4.5.0", 34 | "supertest": "^3.4.2" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/github_tests.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock'); 2 | const { expect } = require('chai'); 3 | const github = require('../lib/github'); 4 | 5 | describe('GitHub Client', () => { 6 | it('Should have the right headers', () => { 7 | nock('https://api.github.com') 8 | .matchHeader('Authorization', 'token oauth') 9 | .matchHeader('User-Agent', 'codehub-push') 10 | .get('/tests') 11 | .reply(200); 12 | 13 | const client = new github.Client('oauth'); 14 | return client.get('https://api.github.com/tests'); 15 | }); 16 | 17 | it('Should get notifications', async () => { 18 | nock('https://api.github.com') 19 | .get('/notifications') 20 | .reply(200, [{ name: 'test' }]); 21 | const client = new github.Client('oauth'); 22 | const ret = await client.notifications(); 23 | expect(ret).to.have.length(1); 24 | expect(ret[0]) 25 | .to.have.property('name') 26 | .and.equals('test'); 27 | }); 28 | 29 | it('Should get current user', async () => { 30 | nock('https://api.github.com') 31 | .get('/user') 32 | .reply(200, { name: 'test' }); 33 | const client = new github.Client('oauth'); 34 | const ret = await client.currentUser(); 35 | expect(ret) 36 | .to.have.property('name') 37 | .and.equals('test'); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CodeHub-Push 2 | ============ 3 | 4 | Push notification server built in Node.js for the iOS application CodeHub. 5 | This application has two endpoints: /register and /unregister. Registering requires an iOS device token, a username, and an O-Auth token 6 | for that specific user. Using that information, the server will poll GitHub every X minutes for updates in 7 | that user's notifications. If notifications are found, a push notification is generated for each and pushed out to the device. 8 | 9 | Want to talk about it? Find me on Twitter at @thedillonb, or @codehubapp. 10 | 11 | CodeHub 12 | ----------- 13 | 14 | [CodeHub](https://github.com/thedillonb/CodeHub) is an iOS application that is created and designed using Xamarin.iOS (C#) and is currently available in the [iTunes store](https://itunes.apple.com/us/app/codehub-github-for-ios/id707173885?mt=8). 15 | 16 | Licence 17 | ------------- 18 | 19 | Copyright 2012 Dillon Buchanan 20 | 21 | Licensed under the Apache License, Version 2.0 (the "License"); 22 | you may not use this file except in compliance with the License. 23 | You may obtain a copy of the License at 24 | 25 | http://www.apache.org/licenses/LICENSE-2.0 26 | 27 | Unless required by applicable law or agreed to in writing, software 28 | distributed under the License is distributed on an "AS IS" BASIS, 29 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 30 | See the License for the specific language governing permissions and 31 | limitations under the License. 32 | -------------------------------------------------------------------------------- /test/app_tests.js: -------------------------------------------------------------------------------- 1 | const request = require('supertest'); 2 | const nock = require('nock'); 3 | const winston = require('winston'); 4 | const sinon = require('sinon'); 5 | const { expect } = require('chai'); 6 | const app = require('../lib/app'); 7 | 8 | describe('App', () => { 9 | winston.level = 'fatal'; 10 | 11 | it('Should register a user', async () => { 12 | nock('https://api.github.com') 13 | .get('/notifications') 14 | .reply(200); 15 | nock('https://api.github.com') 16 | .get('/user') 17 | .reply(200, { login: 'moo' }); 18 | const body = { token: 'moo', oauth: 'cow' }; 19 | const insertRegistration = sinon.stub().returns(Promise.resolve()); 20 | const server = app({ insertRegistration }); 21 | await request(server) 22 | .post('/register') 23 | .send(body) 24 | .expect(200); 25 | expect(insertRegistration.called).to.equal(true); 26 | }); 27 | 28 | it('Should unregister a user', async () => { 29 | const removeRegistration = sinon.stub().returns(Promise.resolve()); 30 | const server = app({ removeRegistration }); 31 | await request(server) 32 | .post('/unregister') 33 | .send({ token: 'token', oauth: 'oauth' }) 34 | .expect(200); 35 | expect(removeRegistration.called).to.equal(true); 36 | expect(removeRegistration.getCall(0).args[0]).to.equal('token'); 37 | expect(removeRegistration.getCall(0).args[1]).to.equal('oauth'); 38 | }); 39 | 40 | it('Should not register a user due to valdation', async () => { 41 | const server = app(); 42 | await request(server) 43 | .post('/register') 44 | .send({}) 45 | .expect(401); 46 | await request(server) 47 | .post('/register') 48 | .send({ token: 'something' }) 49 | .expect(401); 50 | await request(server) 51 | .post('/register') 52 | .send({ oauth: 'something' }) 53 | .expect(401); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /bin/main: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const apn = require('apn'); 3 | const logger = require('winston'); 4 | 5 | const worker = require('../lib/worker'); 6 | const Database = require('../lib/db'); 7 | const app = require('../lib/app'); 8 | 9 | process.on('SIGINT', process.exit); 10 | process.on('SIGTERM', process.exit); 11 | 12 | function createApns() { 13 | if (process.env.FAKEAPN) { 14 | return function apnSend(tokens, msg, payload) { 15 | logger.info(`Sending ${msg} - ${payload} to ${tokens}`); 16 | return Promise.resolve({ failed: [] }); 17 | }; 18 | } 19 | 20 | const apnTopic = process.env.APNTOPIC; 21 | const apnProvider = new apn.Provider({ 22 | token: { 23 | key: process.env.APNKEY, 24 | keyId: process.env.KEYID, 25 | teamId: process.env.TEAMID 26 | } 27 | }); 28 | 29 | return function apnSend(tokens, msg, payload) { 30 | const data = new apn.Notification(); 31 | data.alert = msg; 32 | data.sound = 'default'; 33 | data.topic = apnTopic; 34 | data.payload = payload; 35 | return apnProvider.send(data, tokens); 36 | }; 37 | } 38 | 39 | async function main() { 40 | const apnSend = createApns(); 41 | const db = new Database(); 42 | const port = process.env.PORT || 3000; 43 | 44 | await db.ping(); 45 | 46 | app(db).listen(port, () => logger.info(`Listening on ${port}`)); 47 | 48 | // eslint-disable-next-line no-constant-condition 49 | while (true) { 50 | const startTime = new Date(); 51 | logger.info('Starting background work %s', startTime); 52 | const tasks = await worker 53 | .processRecords(db, apnSend) 54 | .catch(err => logger.error('Error during loop processing', err)); 55 | const diff = new Date() - startTime; 56 | logger.info('%s tasks completed in %s minutes', tasks, (diff / 1000 / 60).toFixed(2)); 57 | await new Promise(res => setTimeout(res, 1000 * 60 * 3)); 58 | } 59 | } 60 | 61 | main() 62 | .then(() => logger.warn('This loop should not have ended!'), err => logger.error(err)) 63 | .then(() => process.exit(-1)); 64 | -------------------------------------------------------------------------------- /lib/github.js: -------------------------------------------------------------------------------- 1 | const request = require('request'); 2 | 3 | class HttpError extends Error { 4 | constructor(statusCode, message, extra = {}) { 5 | super(message); 6 | 7 | this.statusCode = statusCode; 8 | this.extra = extra; 9 | } 10 | } 11 | 12 | class Client { 13 | /** 14 | * Constructor 15 | * @param oauth The oauth identification token 16 | */ 17 | constructor(oauth) { 18 | this.oauth = oauth; 19 | this.domain = 'https://api.github.com'; 20 | } 21 | 22 | /** 23 | * Executes a GET request 24 | * @param url The url to execute it against 25 | * @param lastModified The date to test against 26 | * @param callback The callback 27 | * @param args Optional arguments 28 | */ 29 | get(url, args) { 30 | const opts = { 31 | uri: url, 32 | qs: args, 33 | method: 'GET', 34 | json: {}, 35 | timeout: 1000 * 15, 36 | headers: { 37 | 'User-Agent': 'codehub-push', 38 | Authorization: `token ${this.oauth}` 39 | } 40 | }; 41 | 42 | return new Promise((resolve, reject) => { 43 | request(opts, (err, res, body) => { 44 | if (err) { 45 | return reject(err); 46 | } 47 | if (res.statusCode >= 400) { 48 | return reject(new HttpError(res.statusCode, (body || {}).message)); 49 | } 50 | return resolve(body); 51 | }); 52 | }); 53 | } 54 | 55 | /** 56 | * Gets the notifications for the current user and tests it against the modified since date 57 | * @param lastModified The date to test against 58 | * @param callback The callback 59 | */ 60 | notifications(lastModified) { 61 | const args = {}; 62 | 63 | if (lastModified) { 64 | args.since = lastModified.toISOString(); 65 | } 66 | 67 | return this.get(`${this.domain}/notifications`, args); 68 | } 69 | 70 | /** 71 | * Get the current user info 72 | */ 73 | currentUser() { 74 | return this.get(`${this.domain}/user`); 75 | } 76 | } 77 | 78 | module.exports = { 79 | Client, 80 | HttpError 81 | }; 82 | -------------------------------------------------------------------------------- /lib/db.js: -------------------------------------------------------------------------------- 1 | const pg = require('pg-promise')(); 2 | 3 | const syncSql = () => ` 4 | CREATE TABLE IF NOT EXISTS records ( 5 | id serial primary key, 6 | token varchar(128) NOT NULL, 7 | oauth varchar(64) NOT NULL, 8 | username varchar(64) NOT NULL, 9 | created_at timestamp with time zone default (now() at time zone 'utc'), 10 | updated_at timestamp with time zone default (now() at time zone 'utc'), 11 | UNIQUE (token, username) 12 | ); 13 | 14 | CREATE INDEX oauth_idx ON records (oauth); 15 | CREATE INDEX user_idx ON records (username); 16 | `; 17 | 18 | class Database { 19 | constructor() { 20 | this.db = pg({ 21 | host: process.env.PGHOST || '127.0.0.1', 22 | database: process.env.PGDB || 'codehubpush', 23 | user: process.env.PGUSER || 'postgres', 24 | password: process.env.PGPASSWORD, 25 | port: process.env.PGPORT || 5432, 26 | ssl: false, 27 | }); 28 | } 29 | 30 | ping() { 31 | return this.db.any('select 1'); 32 | } 33 | 34 | sync() { 35 | return this.db.none(syncSql()); 36 | } 37 | 38 | insertRegistration(token, auth, user) { 39 | return this.db 40 | .none( 41 | 'insert into records (token, oauth, username) values ($1, $2, $3) ' + 42 | 'on conflict (token, username) do update set oauth = excluded.oauth, ' + 43 | "updated_at = (now() at time zone 'utc')", 44 | [token, auth, user], 45 | ) 46 | .then(() => this.db.none('update records set oauth = $1 where username = $2', [auth, user])); 47 | } 48 | 49 | removeRegistration(token, auth) { 50 | return this.db.none('delete from records where token = $1 and oauth = $2', [token, auth]); 51 | } 52 | 53 | removeRegistrationByUser(token, user) { 54 | return this.db.none('delete from records where token = $1 and username = $2', [token, user]); 55 | } 56 | 57 | getRegistrations() { 58 | return this.db.any('select array_agg(token) as tokens, oauth, username, max(updated_at) as ' + 59 | 'updated_at from records group by oauth, username'); 60 | } 61 | 62 | removeExpiredRegistration(tokens) { 63 | return this.db.none('delete from records where token in ($1)', [tokens]); 64 | } 65 | 66 | removeBadAuth(oauth) { 67 | return this.db.none('delete from records where oauth = $1', [oauth]); 68 | } 69 | 70 | updateUpdatedAt(oauth) { 71 | return this.db.none( 72 | "update records set updated_at = (now() at time zone 'utc') where oauth = $1", 73 | [oauth], 74 | ); 75 | } 76 | } 77 | 78 | module.exports = Database; 79 | -------------------------------------------------------------------------------- /lib/app.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const assert = require('http-assert'); 4 | const morgan = require('morgan'); 5 | const validator = require('express-validator'); 6 | const logger = require('winston'); 7 | const RateLimit = require('express-rate-limit'); 8 | const GitHub = require('./github').Client; 9 | 10 | // Forward morgan logging into winston 11 | logger.stream = { 12 | write(message) { 13 | logger.info(message.trim()); 14 | } 15 | }; 16 | 17 | const wrap = fn => (...args) => fn(...args).catch(args[2]); 18 | 19 | module.exports = function create(db) { 20 | const app = express(); 21 | app.set('etag', false); 22 | app.set('x-powered-by', false); 23 | app.use(morgan('combined', { stream: logger.stream })); 24 | app.use(bodyParser.urlencoded({ extended: false })); 25 | app.use(bodyParser.json()); 26 | app.use(validator(), (req, res, next) => { 27 | req.validate = () => { 28 | const errors = req.validationErrors(); 29 | assert(!errors, 401, 'Validation failure', { errors }); 30 | }; 31 | next(); 32 | }); 33 | 34 | /** 35 | * Get in-app purchase identifiers. Use this to disable purchases for in-app items 36 | * just incase there is an emergency. We don't need people purchasing items they can't 37 | * use! 38 | */ 39 | app.get('/in-app', (req, res) => res.status(200).json(['com.dillonbuchanan.codehub.push'])); 40 | 41 | // Rate limit all subsequent endpoints 42 | app.use( 43 | new RateLimit({ 44 | windowsMs: 15 * 60 * 1000, 45 | max: 100, 46 | delayMs: 0, 47 | handler: (req, res, next) => { 48 | const err = new Error('Too many requests, please try again later.'); 49 | err.status = 429; 50 | next(err); 51 | } 52 | }) 53 | ); 54 | 55 | /** 56 | * Register a user, their token, oauth, and domain in the system 57 | */ 58 | app.post( 59 | '/register', 60 | wrap(async (req, res) => { 61 | req 62 | .checkBody('token') 63 | .notEmpty() 64 | .withMessage('Must have a valid iOS push token'); 65 | req 66 | .checkBody('oauth') 67 | .notEmpty() 68 | .withMessage('Must have a valid GitHub OAuth token'); 69 | req.validate(); 70 | 71 | const client = new GitHub(req.body.oauth); 72 | await client.notifications(); 73 | const user = await client.currentUser(); 74 | await db.insertRegistration(req.body.token, req.body.oauth, user.login); 75 | res.status(200).end(); 76 | }) 77 | ); 78 | 79 | /** 80 | * Unregister a user from push notifications 81 | */ 82 | app.post( 83 | '/unregister', 84 | wrap(async (req, res) => { 85 | req.checkBody('token', 'Invalid token').notEmpty(); 86 | req.checkBody('oauth', 'Invalid oAuth token').notEmpty(); 87 | req.validate(); 88 | await db.removeRegistration(req.body.token, req.body.oauth); 89 | res.status(200).end(); 90 | }) 91 | ); 92 | 93 | app.post( 94 | '/v2/unregister', 95 | wrap(async (req, res) => { 96 | req.checkBody('token', 'Invalid token').notEmpty(); 97 | req.checkBody('username', 'Invalid username').notEmpty(); 98 | req.validate(); 99 | await db.removeRegistrationByUser(req.body.token, req.body.username); 100 | res.status(200).end(); 101 | }) 102 | ); 103 | 104 | /** 105 | * Catch all errors 106 | */ 107 | /* eslint-disable-next-line no-unused-vars */ 108 | app.use((err, req, res, next) => { 109 | res.status(err.status || 500).json({ 110 | message: err.message, 111 | errors: err.errors 112 | }); 113 | }); 114 | 115 | return app; 116 | }; 117 | -------------------------------------------------------------------------------- /lib/worker.js: -------------------------------------------------------------------------------- 1 | const async = require('async'); 2 | const logger = require('winston'); 3 | const { Client, HttpError } = require('./github'); 4 | 5 | async function processNotification(client, username, notification, sendApn) { 6 | const subjectType = notification.subject.type.toLowerCase(); 7 | const lastCommentUrl = notification.subject.latest_comment_url; 8 | const subjectUrl = notification.subject.url; 9 | const stateChange = subjectUrl === lastCommentUrl; 10 | const repositoryName = notification.repository.full_name; 11 | const data = { u: username, r: repositoryName }; 12 | let comment = {}; 13 | 14 | try { 15 | // If there is no comment then this user probably opened the item 16 | if (lastCommentUrl) { 17 | comment = await client.get(lastCommentUrl); 18 | } else if (subjectUrl) { 19 | comment = await client.get(notification.subject.url); 20 | } else { 21 | logger.error(`Unable to locate URL for notification: ${notification}`); 22 | return; 23 | } 24 | } catch (err) { 25 | if (err instanceof HttpError && err.statusCode === 404) { 26 | return; 27 | } 28 | } 29 | 30 | let msg = ''; 31 | 32 | if (subjectType === 'release') { 33 | msg = `${repositoryName} ${notification.subject.title} released`; 34 | } else if (subjectType === 'commit') { 35 | if (stateChange) { 36 | msg = `${comment.author.login} mentioned you on commit ${repositoryName}@${comment.sha.substring(0, 6)}`; 37 | data.c = comment.sha; 38 | } else { 39 | msg = `${comment.user.login} commented on commit ${repositoryName}@${comment.commit_id.substring(0, 6)}`; 40 | data.c = comment.commit_id; 41 | } 42 | } else if (subjectType === 'issue' || subjectType === 'pullrequest') { 43 | // If the two urls are the same then it's most likely that someone 44 | // just created the notification. If they're different it's most likely a comment 45 | const isPullRequest = subjectType === 'pullrequest'; 46 | const num = notification.subject.url.substring(notification.subject.url.lastIndexOf('/') + 1); 47 | const shortNum = num.substring(0, 6); 48 | 49 | if (stateChange) { 50 | if (isPullRequest) { 51 | // Three things could have happened: open, close, merged 52 | if (comment.merged_at) { 53 | msg = `${comment.merged_by.login} merged `; 54 | } else if (comment.closed_at) { 55 | const issue = await client.get(comment.issue_url); 56 | msg = `${issue.closed_by.login} closed `; 57 | } else { 58 | msg = `${comment.user.login} opened `; 59 | } 60 | } else if (comment.closed_at) { 61 | msg = `${comment.closed_by.login} closed `; 62 | } else { 63 | msg = `${comment.user.login} opened `; 64 | } 65 | } else { 66 | msg = `${comment.user.login} commented on `; 67 | } 68 | 69 | data[subjectType.substring(0, 1)] = num; 70 | msg += `${isPullRequest ? 'pull request' : 'issue'} ${repositoryName}#${shortNum}`; 71 | } else { 72 | logger.error(`No support for subject type ${subjectType}`); 73 | return; 74 | } 75 | 76 | await sendApn(msg, data); 77 | } 78 | 79 | async function handleApn(db, result) { 80 | const failedResults = result.failed || []; 81 | for (const e of failedResults) { 82 | if (e.status) { 83 | if (e.status === '410' || e.status === '400') { 84 | await db 85 | .removeExpiredRegistration(e.device) 86 | .then(() => logger.info(`Successfully removed ${e.device}`)) 87 | .catch(err => logger.error(`Error removing expired device ${e.device}`, err)); 88 | } else { 89 | logger.info(`Unhandled status for device ${e.status}: ${e.response.reason}`); 90 | } 91 | } else { 92 | logger.error(e.error); 93 | } 94 | } 95 | } 96 | 97 | async function processRecord(db, record, sendApn) { 98 | const client = new Client(record.oauth); 99 | const { tokens } = record; 100 | 101 | try { 102 | const notifications = await client.notifications(record.updated_at); 103 | for (let i = 0; i < notifications.length; i += 1) { 104 | await processNotification(client, record.username, notifications[i], (msg, data) => 105 | sendApn(tokens, msg, data).then(x => handleApn(db, x)) 106 | ); 107 | } 108 | } catch (err) { 109 | if (err instanceof HttpError) { 110 | if (err.statusCode === 401 || err.statusCode === 403) { 111 | await db.removeBadAuth(record.oauth, record.domain); 112 | return; 113 | } 114 | } 115 | throw err; 116 | } 117 | } 118 | 119 | async function processRecords(db, sendApn) { 120 | const registrations = await db.getRegistrations(); 121 | 122 | const tasks = registrations.map(x => callback => { 123 | processRecord(db, x, sendApn) 124 | .catch(err => logger.error(`Unable to process record ${x.username}`, err)) 125 | .then(() => db.updateUpdatedAt(x.oauth)) 126 | .catch(err => logger.error(`Failed to update UpdatedAt ${x.username}`, err)) 127 | .then(() => callback(), err => callback(err)); 128 | }); 129 | 130 | const totalTasks = tasks.length; 131 | const parallelProcessing = new Promise((resolve, reject) => { 132 | const timeout = setTimeout(() => reject(new Error('Timeout waiting to process records')), 1000 * 60 * 20); 133 | 134 | async.parallelLimit(tasks, 3, err => { 135 | clearTimeout(timeout); 136 | 137 | if (err) { 138 | reject(err); 139 | } else { 140 | resolve(); 141 | } 142 | }); 143 | }); 144 | 145 | await parallelProcessing.catch(e => logger.error('Error during parallel record processing', e)); 146 | return totalTasks; 147 | } 148 | 149 | module.exports = { 150 | processRecords 151 | }; 152 | -------------------------------------------------------------------------------- /test/worker_tests.js: -------------------------------------------------------------------------------- 1 | const nock = require('nock'); 2 | const fs = require('fs'); 3 | const path = require('path'); 4 | const sinon = require('sinon'); 5 | const { expect } = require('chai'); 6 | const worker = require('../lib/worker'); 7 | 8 | describe('Worker', () => { 9 | it('Should remove user on bad credentials', async () => { 10 | nock('https://api.github.com') 11 | .get('/notifications') 12 | .query(true) 13 | .reply(401, { message: 'Bad credentials' }); 14 | 15 | const getRegistrations = sinon.stub().returns( 16 | Promise.resolve([ 17 | { 18 | tokens: ['1'], 19 | oauth: 'auth', 20 | username: 'moo', 21 | updated_at: new Date() 22 | } 23 | ]) 24 | ); 25 | 26 | const updateUpdatedAt = sinon.stub().returns(Promise.resolve()); 27 | const removeBadAuth = sinon.stub().returns(Promise.resolve()); 28 | const db = { getRegistrations, updateUpdatedAt, removeBadAuth }; 29 | 30 | const jobs = await worker.processRecords(db, () => {}); 31 | 32 | expect(getRegistrations.called).to.equal(true); 33 | expect(removeBadAuth.called).to.equal(true); 34 | expect(updateUpdatedAt.called).to.equal(true); 35 | expect(jobs).to.equal(1); 36 | }); 37 | 38 | it('Should process registrations', async () => { 39 | const data = JSON.parse(fs.readFileSync(path.join(__dirname, '/worker_data.json'), 'utf8')); 40 | for (const url of Object.keys(data)) { 41 | if (url === '/notifications') { 42 | nock('https://api.github.com') 43 | .get(url) 44 | .query(true) 45 | .reply(200, data[url]); 46 | } else { 47 | nock('https://api.github.com') 48 | .get(url) 49 | .reply(200, data[url]); 50 | } 51 | } 52 | 53 | const getRegistrations = sinon.stub().returns( 54 | Promise.resolve([ 55 | { 56 | tokens: ['token1', 'token2'], 57 | oauth: 'auth', 58 | username: 'user1', 59 | updated_at: new Date() 60 | } 61 | ]) 62 | ); 63 | 64 | const updateUpdatedAt = sinon.stub().returns(Promise.resolve()); 65 | const db = { getRegistrations, updateUpdatedAt }; 66 | 67 | const apnMessages = []; 68 | await worker.processRecords(db, (tokens, msg, dat) => { 69 | apnMessages.push([tokens, msg, dat]); 70 | return Promise.resolve({}); 71 | }); 72 | 73 | expect(apnMessages).to.have.length(7); 74 | apnMessages.forEach(x => { 75 | expect(x[0]) 76 | .to.have.length(2) 77 | .and.contain('token1') 78 | .and.contain('token2'); 79 | 80 | expect(x[2]) 81 | .to.have.property('u') 82 | .and.equal('user1'); 83 | }); 84 | 85 | expect(apnMessages[0][1]).to.equal('naveensrinivasan commented on pull request octokit/octokit.net#959'); 86 | expect(apnMessages[0][2]) 87 | .to.have.property('r') 88 | .and.equal('octokit/octokit.net'); 89 | expect(apnMessages[0][2]) 90 | .to.have.property('p') 91 | .and.equal('959'); 92 | 93 | expect(apnMessages[1][1]).to.equal('shiftkey merged pull request octokit/octokit.net#807'); 94 | expect(apnMessages[1][2]) 95 | .to.have.property('r') 96 | .and.equal('octokit/octokit.net'); 97 | expect(apnMessages[1][2]) 98 | .to.have.property('p') 99 | .and.equal('807'); 100 | 101 | expect(apnMessages[2][1]).to.equal('shiftkey closed issue octokit/octokit.net#423'); 102 | expect(apnMessages[2][2]) 103 | .to.have.property('r') 104 | .and.equal('octokit/octokit.net'); 105 | expect(apnMessages[2][2]) 106 | .to.have.property('i') 107 | .and.equal('423'); 108 | 109 | expect(apnMessages[3][1]).to.equal('shiftkey closed pull request octokit/octokit.net#935'); 110 | expect(apnMessages[3][2]) 111 | .to.have.property('r') 112 | .and.equal('octokit/octokit.net'); 113 | expect(apnMessages[3][2]) 114 | .to.have.property('p') 115 | .and.equal('935'); 116 | 117 | expect(apnMessages[4][1]).to.equal('dillonb123 commented on commit thedillonb/TestTestTest@fffb24'); 118 | expect(apnMessages[4][2]) 119 | .to.have.property('r') 120 | .and.equal('thedillonb/TestTestTest'); 121 | expect(apnMessages[4][2]) 122 | .to.have.property('c') 123 | .and.equal('fffb244af01b360d6c3a055ade39f44cf3e82cf7'); 124 | 125 | expect(apnMessages[5][1]).to.equal('dillonb123 mentioned you on commit thedillonb/TestTestTest@cd9fdc'); 126 | expect(apnMessages[5][2]) 127 | .to.have.property('r') 128 | .and.equal('thedillonb/TestTestTest'); 129 | expect(apnMessages[5][2]) 130 | .to.have.property('c') 131 | .and.equal('cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66'); 132 | 133 | expect(apnMessages[6][1]).to.equal('octokit/octokit.net v0.1.1 released'); 134 | expect(apnMessages[6][2]) 135 | .to.have.property('r') 136 | .and.equal('octokit/octokit.net'); 137 | }); 138 | 139 | it('Should update time on error', async () => { 140 | const getRegistrations = sinon.stub().returns( 141 | Promise.resolve([ 142 | { 143 | tokens: ['token1'], 144 | oauth: 'auth1', 145 | username: 'user1', 146 | updated_at: new Date() 147 | }, 148 | { 149 | tokens: ['token2'], 150 | oauth: 'auth2', 151 | username: 'user2', 152 | updated_at: new Date() 153 | } 154 | ]) 155 | ); 156 | 157 | const updateUpdatedAt = sinon.stub().returns(Promise.resolve()); 158 | const db = { getRegistrations, updateUpdatedAt }; 159 | 160 | const records = await db.getRegistrations(); 161 | expect(records).to.have.length(2); 162 | 163 | const apnMessages = []; 164 | await worker.processRecords(db, (tokens, msg, data) => apnMessages.push([tokens, msg, data])).catch(() => {}); 165 | expect(updateUpdatedAt.callCount).to.equal(2); 166 | }); 167 | 168 | it('Should remove user on expired device', async () => { 169 | const data = JSON.parse(fs.readFileSync(path.join(__dirname, '/worker_data.json'), 'utf8')); 170 | for (const url in data) { 171 | if (url === '/notifications') { 172 | nock('https://api.github.com') 173 | .get(url) 174 | .query(true) 175 | .reply(200, data[url]); 176 | } else { 177 | nock('https://api.github.com') 178 | .get(url) 179 | .reply(200, data[url]); 180 | } 181 | } 182 | 183 | const getRegistrations = sinon.stub().returns( 184 | Promise.resolve([ 185 | { 186 | tokens: ['1'], 187 | oauth: 'auth', 188 | username: 'moo', 189 | updated_at: new Date() 190 | } 191 | ]) 192 | ); 193 | 194 | const updateUpdatedAt = sinon.stub().returns(Promise.resolve()); 195 | const removeExpiredRegistration = sinon.stub().returns(Promise.resolve()); 196 | const db = { getRegistrations, updateUpdatedAt, removeExpiredRegistration }; 197 | 198 | const jobs = await worker.processRecords(db, () => 199 | Promise.resolve({ 200 | failed: [ 201 | { 202 | device: '1', 203 | status: '410' 204 | } 205 | ] 206 | }) 207 | ); 208 | 209 | expect(getRegistrations.called).to.equal(true); 210 | expect(removeExpiredRegistration.called).to.equal(true); 211 | expect(updateUpdatedAt.called).to.equal(true); 212 | expect(jobs).to.equal(1); 213 | }); 214 | }); 215 | -------------------------------------------------------------------------------- /test/worker_data.json: -------------------------------------------------------------------------------- 1 | { 2 | "/notifications": [ 3 | { 4 | "id": "108039090", 5 | "unread": true, 6 | "reason": "subscribed", 7 | "updated_at": "2015-11-06T18:47:20Z", 8 | "last_read_at": null, 9 | "subject": { 10 | "title": "Fix for client.Repository.Content.GetAllContents for root #800", 11 | "url": "https://api.github.com/repos/octokit/octokit.net/pulls/959", 12 | "latest_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments/154498869", 13 | "type": "PullRequest" 14 | }, 15 | "repository": { 16 | "id": 7528679, 17 | "name": "octokit.net", 18 | "full_name": "octokit/octokit.net", 19 | "owner": { 20 | "login": "octokit", 21 | "id": 3430433, 22 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 23 | "gravatar_id": "", 24 | "url": "https://api.github.com/users/octokit", 25 | "html_url": "https://github.com/octokit", 26 | "followers_url": "https://api.github.com/users/octokit/followers", 27 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 28 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 29 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 30 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 31 | "organizations_url": "https://api.github.com/users/octokit/orgs", 32 | "repos_url": "https://api.github.com/users/octokit/repos", 33 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 34 | "received_events_url": "https://api.github.com/users/octokit/received_events", 35 | "type": "Organization", 36 | "site_admin": false 37 | }, 38 | "private": false, 39 | "html_url": "https://github.com/octokit/octokit.net", 40 | "description": "A GitHub API client library for .NET ", 41 | "fork": false, 42 | "url": "https://api.github.com/repos/octokit/octokit.net", 43 | "forks_url": "https://api.github.com/repos/octokit/octokit.net/forks", 44 | "keys_url": "https://api.github.com/repos/octokit/octokit.net/keys{/key_id}", 45 | "collaborators_url": "https://api.github.com/repos/octokit/octokit.net/collaborators{/collaborator}", 46 | "teams_url": "https://api.github.com/repos/octokit/octokit.net/teams", 47 | "hooks_url": "https://api.github.com/repos/octokit/octokit.net/hooks", 48 | "issue_events_url": "https://api.github.com/repos/octokit/octokit.net/issues/events{/number}", 49 | "events_url": "https://api.github.com/repos/octokit/octokit.net/events", 50 | "assignees_url": "https://api.github.com/repos/octokit/octokit.net/assignees{/user}", 51 | "branches_url": "https://api.github.com/repos/octokit/octokit.net/branches{/branch}", 52 | "tags_url": "https://api.github.com/repos/octokit/octokit.net/tags", 53 | "blobs_url": "https://api.github.com/repos/octokit/octokit.net/git/blobs{/sha}", 54 | "git_tags_url": "https://api.github.com/repos/octokit/octokit.net/git/tags{/sha}", 55 | "git_refs_url": "https://api.github.com/repos/octokit/octokit.net/git/refs{/sha}", 56 | "trees_url": "https://api.github.com/repos/octokit/octokit.net/git/trees{/sha}", 57 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/{sha}", 58 | "languages_url": "https://api.github.com/repos/octokit/octokit.net/languages", 59 | "stargazers_url": "https://api.github.com/repos/octokit/octokit.net/stargazers", 60 | "contributors_url": "https://api.github.com/repos/octokit/octokit.net/contributors", 61 | "subscribers_url": "https://api.github.com/repos/octokit/octokit.net/subscribers", 62 | "subscription_url": "https://api.github.com/repos/octokit/octokit.net/subscription", 63 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/commits{/sha}", 64 | "git_commits_url": "https://api.github.com/repos/octokit/octokit.net/git/commits{/sha}", 65 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/comments{/number}", 66 | "issue_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments{/number}", 67 | "contents_url": "https://api.github.com/repos/octokit/octokit.net/contents/{+path}", 68 | "compare_url": "https://api.github.com/repos/octokit/octokit.net/compare/{base}...{head}", 69 | "merges_url": "https://api.github.com/repos/octokit/octokit.net/merges", 70 | "archive_url": "https://api.github.com/repos/octokit/octokit.net/{archive_format}{/ref}", 71 | "downloads_url": "https://api.github.com/repos/octokit/octokit.net/downloads", 72 | "issues_url": "https://api.github.com/repos/octokit/octokit.net/issues{/number}", 73 | "pulls_url": "https://api.github.com/repos/octokit/octokit.net/pulls{/number}", 74 | "milestones_url": "https://api.github.com/repos/octokit/octokit.net/milestones{/number}", 75 | "notifications_url": "https://api.github.com/repos/octokit/octokit.net/notifications{?since,all,participating}", 76 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/labels{/name}", 77 | "releases_url": "https://api.github.com/repos/octokit/octokit.net/releases{/id}" 78 | }, 79 | "url": "https://api.github.com/notifications/threads/108039090", 80 | "subscription_url": "https://api.github.com/notifications/threads/108039090/subscription" 81 | }, 82 | { 83 | "id": "75548954", 84 | "unread": true, 85 | "reason": "subscribed", 86 | "updated_at": "2015-11-04T22:53:22Z", 87 | "last_read_at": null, 88 | "subject": { 89 | "title": "added a tailored CodeFormatter to Octokit", 90 | "url": "https://api.github.com/repos/octokit/octokit.net/pulls/807", 91 | "latest_comment_url": "https://api.github.com/repos/octokit/octokit.net/pulls/807", 92 | "type": "PullRequest" 93 | }, 94 | "repository": { 95 | "id": 7528679, 96 | "name": "octokit.net", 97 | "full_name": "octokit/octokit.net", 98 | "owner": { 99 | "login": "octokit", 100 | "id": 3430433, 101 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 102 | "gravatar_id": "", 103 | "url": "https://api.github.com/users/octokit", 104 | "html_url": "https://github.com/octokit", 105 | "followers_url": "https://api.github.com/users/octokit/followers", 106 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 107 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 108 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 109 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 110 | "organizations_url": "https://api.github.com/users/octokit/orgs", 111 | "repos_url": "https://api.github.com/users/octokit/repos", 112 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 113 | "received_events_url": "https://api.github.com/users/octokit/received_events", 114 | "type": "Organization", 115 | "site_admin": false 116 | }, 117 | "private": false, 118 | "html_url": "https://github.com/octokit/octokit.net", 119 | "description": "A GitHub API client library for .NET ", 120 | "fork": false, 121 | "url": "https://api.github.com/repos/octokit/octokit.net", 122 | "forks_url": "https://api.github.com/repos/octokit/octokit.net/forks", 123 | "keys_url": "https://api.github.com/repos/octokit/octokit.net/keys{/key_id}", 124 | "collaborators_url": "https://api.github.com/repos/octokit/octokit.net/collaborators{/collaborator}", 125 | "teams_url": "https://api.github.com/repos/octokit/octokit.net/teams", 126 | "hooks_url": "https://api.github.com/repos/octokit/octokit.net/hooks", 127 | "issue_events_url": "https://api.github.com/repos/octokit/octokit.net/issues/events{/number}", 128 | "events_url": "https://api.github.com/repos/octokit/octokit.net/events", 129 | "assignees_url": "https://api.github.com/repos/octokit/octokit.net/assignees{/user}", 130 | "branches_url": "https://api.github.com/repos/octokit/octokit.net/branches{/branch}", 131 | "tags_url": "https://api.github.com/repos/octokit/octokit.net/tags", 132 | "blobs_url": "https://api.github.com/repos/octokit/octokit.net/git/blobs{/sha}", 133 | "git_tags_url": "https://api.github.com/repos/octokit/octokit.net/git/tags{/sha}", 134 | "git_refs_url": "https://api.github.com/repos/octokit/octokit.net/git/refs{/sha}", 135 | "trees_url": "https://api.github.com/repos/octokit/octokit.net/git/trees{/sha}", 136 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/{sha}", 137 | "languages_url": "https://api.github.com/repos/octokit/octokit.net/languages", 138 | "stargazers_url": "https://api.github.com/repos/octokit/octokit.net/stargazers", 139 | "contributors_url": "https://api.github.com/repos/octokit/octokit.net/contributors", 140 | "subscribers_url": "https://api.github.com/repos/octokit/octokit.net/subscribers", 141 | "subscription_url": "https://api.github.com/repos/octokit/octokit.net/subscription", 142 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/commits{/sha}", 143 | "git_commits_url": "https://api.github.com/repos/octokit/octokit.net/git/commits{/sha}", 144 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/comments{/number}", 145 | "issue_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments{/number}", 146 | "contents_url": "https://api.github.com/repos/octokit/octokit.net/contents/{+path}", 147 | "compare_url": "https://api.github.com/repos/octokit/octokit.net/compare/{base}...{head}", 148 | "merges_url": "https://api.github.com/repos/octokit/octokit.net/merges", 149 | "archive_url": "https://api.github.com/repos/octokit/octokit.net/{archive_format}{/ref}", 150 | "downloads_url": "https://api.github.com/repos/octokit/octokit.net/downloads", 151 | "issues_url": "https://api.github.com/repos/octokit/octokit.net/issues{/number}", 152 | "pulls_url": "https://api.github.com/repos/octokit/octokit.net/pulls{/number}", 153 | "milestones_url": "https://api.github.com/repos/octokit/octokit.net/milestones{/number}", 154 | "notifications_url": "https://api.github.com/repos/octokit/octokit.net/notifications{?since,all,participating}", 155 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/labels{/name}", 156 | "releases_url": "https://api.github.com/repos/octokit/octokit.net/releases{/id}" 157 | }, 158 | "url": "https://api.github.com/notifications/threads/75548954", 159 | "subscription_url": "https://api.github.com/notifications/threads/75548954/subscription" 160 | }, 161 | { 162 | "id": "27056597", 163 | "unread": true, 164 | "reason": "subscribed", 165 | "updated_at": "2015-11-04T17:16:51Z", 166 | "last_read_at": null, 167 | "subject": { 168 | "title": "Validation checks on NewRepository.GitignoreTemplate", 169 | "url": "https://api.github.com/repos/octokit/octokit.net/issues/423", 170 | "latest_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/423", 171 | "type": "Issue" 172 | }, 173 | "repository": { 174 | "id": 7528679, 175 | "name": "octokit.net", 176 | "full_name": "octokit/octokit.net", 177 | "owner": { 178 | "login": "octokit", 179 | "id": 3430433, 180 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 181 | "gravatar_id": "", 182 | "url": "https://api.github.com/users/octokit", 183 | "html_url": "https://github.com/octokit", 184 | "followers_url": "https://api.github.com/users/octokit/followers", 185 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 186 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 187 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 188 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 189 | "organizations_url": "https://api.github.com/users/octokit/orgs", 190 | "repos_url": "https://api.github.com/users/octokit/repos", 191 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 192 | "received_events_url": "https://api.github.com/users/octokit/received_events", 193 | "type": "Organization", 194 | "site_admin": false 195 | }, 196 | "private": false, 197 | "html_url": "https://github.com/octokit/octokit.net", 198 | "description": "A GitHub API client library for .NET ", 199 | "fork": false, 200 | "url": "https://api.github.com/repos/octokit/octokit.net", 201 | "forks_url": "https://api.github.com/repos/octokit/octokit.net/forks", 202 | "keys_url": "https://api.github.com/repos/octokit/octokit.net/keys{/key_id}", 203 | "collaborators_url": "https://api.github.com/repos/octokit/octokit.net/collaborators{/collaborator}", 204 | "teams_url": "https://api.github.com/repos/octokit/octokit.net/teams", 205 | "hooks_url": "https://api.github.com/repos/octokit/octokit.net/hooks", 206 | "issue_events_url": "https://api.github.com/repos/octokit/octokit.net/issues/events{/number}", 207 | "events_url": "https://api.github.com/repos/octokit/octokit.net/events", 208 | "assignees_url": "https://api.github.com/repos/octokit/octokit.net/assignees{/user}", 209 | "branches_url": "https://api.github.com/repos/octokit/octokit.net/branches{/branch}", 210 | "tags_url": "https://api.github.com/repos/octokit/octokit.net/tags", 211 | "blobs_url": "https://api.github.com/repos/octokit/octokit.net/git/blobs{/sha}", 212 | "git_tags_url": "https://api.github.com/repos/octokit/octokit.net/git/tags{/sha}", 213 | "git_refs_url": "https://api.github.com/repos/octokit/octokit.net/git/refs{/sha}", 214 | "trees_url": "https://api.github.com/repos/octokit/octokit.net/git/trees{/sha}", 215 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/{sha}", 216 | "languages_url": "https://api.github.com/repos/octokit/octokit.net/languages", 217 | "stargazers_url": "https://api.github.com/repos/octokit/octokit.net/stargazers", 218 | "contributors_url": "https://api.github.com/repos/octokit/octokit.net/contributors", 219 | "subscribers_url": "https://api.github.com/repos/octokit/octokit.net/subscribers", 220 | "subscription_url": "https://api.github.com/repos/octokit/octokit.net/subscription", 221 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/commits{/sha}", 222 | "git_commits_url": "https://api.github.com/repos/octokit/octokit.net/git/commits{/sha}", 223 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/comments{/number}", 224 | "issue_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments{/number}", 225 | "contents_url": "https://api.github.com/repos/octokit/octokit.net/contents/{+path}", 226 | "compare_url": "https://api.github.com/repos/octokit/octokit.net/compare/{base}...{head}", 227 | "merges_url": "https://api.github.com/repos/octokit/octokit.net/merges", 228 | "archive_url": "https://api.github.com/repos/octokit/octokit.net/{archive_format}{/ref}", 229 | "downloads_url": "https://api.github.com/repos/octokit/octokit.net/downloads", 230 | "issues_url": "https://api.github.com/repos/octokit/octokit.net/issues{/number}", 231 | "pulls_url": "https://api.github.com/repos/octokit/octokit.net/pulls{/number}", 232 | "milestones_url": "https://api.github.com/repos/octokit/octokit.net/milestones{/number}", 233 | "notifications_url": "https://api.github.com/repos/octokit/octokit.net/notifications{?since,all,participating}", 234 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/labels{/name}", 235 | "releases_url": "https://api.github.com/repos/octokit/octokit.net/releases{/id}" 236 | }, 237 | "url": "https://api.github.com/notifications/threads/27056597", 238 | "subscription_url": "https://api.github.com/notifications/threads/27056597/subscription" 239 | }, 240 | { 241 | "id": "103918870", 242 | "unread": true, 243 | "reason": "subscribed", 244 | "updated_at": "2015-11-03T19:52:28Z", 245 | "last_read_at": "2015-11-01T05:59:39Z", 246 | "subject": { 247 | "title": "added \"Merged\" in searchissues which allows search repos by merged da…", 248 | "url": "https://api.github.com/repos/octokit/octokit.net/pulls/935", 249 | "latest_comment_url": "https://api.github.com/repos/octokit/octokit.net/pulls/935", 250 | "type": "PullRequest" 251 | }, 252 | "repository": { 253 | "id": 7528679, 254 | "name": "octokit.net", 255 | "full_name": "octokit/octokit.net", 256 | "owner": { 257 | "login": "octokit", 258 | "id": 3430433, 259 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 260 | "gravatar_id": "", 261 | "url": "https://api.github.com/users/octokit", 262 | "html_url": "https://github.com/octokit", 263 | "followers_url": "https://api.github.com/users/octokit/followers", 264 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 265 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 266 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 267 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 268 | "organizations_url": "https://api.github.com/users/octokit/orgs", 269 | "repos_url": "https://api.github.com/users/octokit/repos", 270 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 271 | "received_events_url": "https://api.github.com/users/octokit/received_events", 272 | "type": "Organization", 273 | "site_admin": false 274 | }, 275 | "private": false, 276 | "html_url": "https://github.com/octokit/octokit.net", 277 | "description": "A GitHub API client library for .NET ", 278 | "fork": false, 279 | "url": "https://api.github.com/repos/octokit/octokit.net", 280 | "forks_url": "https://api.github.com/repos/octokit/octokit.net/forks", 281 | "keys_url": "https://api.github.com/repos/octokit/octokit.net/keys{/key_id}", 282 | "collaborators_url": "https://api.github.com/repos/octokit/octokit.net/collaborators{/collaborator}", 283 | "teams_url": "https://api.github.com/repos/octokit/octokit.net/teams", 284 | "hooks_url": "https://api.github.com/repos/octokit/octokit.net/hooks", 285 | "issue_events_url": "https://api.github.com/repos/octokit/octokit.net/issues/events{/number}", 286 | "events_url": "https://api.github.com/repos/octokit/octokit.net/events", 287 | "assignees_url": "https://api.github.com/repos/octokit/octokit.net/assignees{/user}", 288 | "branches_url": "https://api.github.com/repos/octokit/octokit.net/branches{/branch}", 289 | "tags_url": "https://api.github.com/repos/octokit/octokit.net/tags", 290 | "blobs_url": "https://api.github.com/repos/octokit/octokit.net/git/blobs{/sha}", 291 | "git_tags_url": "https://api.github.com/repos/octokit/octokit.net/git/tags{/sha}", 292 | "git_refs_url": "https://api.github.com/repos/octokit/octokit.net/git/refs{/sha}", 293 | "trees_url": "https://api.github.com/repos/octokit/octokit.net/git/trees{/sha}", 294 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/{sha}", 295 | "languages_url": "https://api.github.com/repos/octokit/octokit.net/languages", 296 | "stargazers_url": "https://api.github.com/repos/octokit/octokit.net/stargazers", 297 | "contributors_url": "https://api.github.com/repos/octokit/octokit.net/contributors", 298 | "subscribers_url": "https://api.github.com/repos/octokit/octokit.net/subscribers", 299 | "subscription_url": "https://api.github.com/repos/octokit/octokit.net/subscription", 300 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/commits{/sha}", 301 | "git_commits_url": "https://api.github.com/repos/octokit/octokit.net/git/commits{/sha}", 302 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/comments{/number}", 303 | "issue_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments{/number}", 304 | "contents_url": "https://api.github.com/repos/octokit/octokit.net/contents/{+path}", 305 | "compare_url": "https://api.github.com/repos/octokit/octokit.net/compare/{base}...{head}", 306 | "merges_url": "https://api.github.com/repos/octokit/octokit.net/merges", 307 | "archive_url": "https://api.github.com/repos/octokit/octokit.net/{archive_format}{/ref}", 308 | "downloads_url": "https://api.github.com/repos/octokit/octokit.net/downloads", 309 | "issues_url": "https://api.github.com/repos/octokit/octokit.net/issues{/number}", 310 | "pulls_url": "https://api.github.com/repos/octokit/octokit.net/pulls{/number}", 311 | "milestones_url": "https://api.github.com/repos/octokit/octokit.net/milestones{/number}", 312 | "notifications_url": "https://api.github.com/repos/octokit/octokit.net/notifications{?since,all,participating}", 313 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/labels{/name}", 314 | "releases_url": "https://api.github.com/repos/octokit/octokit.net/releases{/id}" 315 | }, 316 | "url": "https://api.github.com/notifications/threads/103918870", 317 | "subscription_url": "https://api.github.com/notifications/threads/103918870/subscription" 318 | }, 319 | { 320 | "id": "108524592", 321 | "unread": true, 322 | "reason": "author", 323 | "updated_at": "2015-11-07T16:47:00Z", 324 | "last_read_at": null, 325 | "subject": { 326 | "title": "Update acoolnewfile.md", 327 | "url": "https://api.github.com/repos/thedillonb/TestTestTest/commits/fffb244af01b360d6c3a055ade39f44cf3e82cf7", 328 | "latest_comment_url": "https://api.github.com/repos/thedillonb/TestTestTest/comments/14258794", 329 | "type": "Commit" 330 | }, 331 | "repository": { 332 | "id": 25952452, 333 | "name": "TestTestTest", 334 | "full_name": "thedillonb/TestTestTest", 335 | "owner": { 336 | "login": "thedillonb", 337 | "id": 1644068, 338 | "avatar_url": "https://avatars.githubusercontent.com/u/1644068?v=3", 339 | "gravatar_id": "", 340 | "url": "https://api.github.com/users/thedillonb", 341 | "html_url": "https://github.com/thedillonb", 342 | "followers_url": "https://api.github.com/users/thedillonb/followers", 343 | "following_url": "https://api.github.com/users/thedillonb/following{/other_user}", 344 | "gists_url": "https://api.github.com/users/thedillonb/gists{/gist_id}", 345 | "starred_url": "https://api.github.com/users/thedillonb/starred{/owner}{/repo}", 346 | "subscriptions_url": "https://api.github.com/users/thedillonb/subscriptions", 347 | "organizations_url": "https://api.github.com/users/thedillonb/orgs", 348 | "repos_url": "https://api.github.com/users/thedillonb/repos", 349 | "events_url": "https://api.github.com/users/thedillonb/events{/privacy}", 350 | "received_events_url": "https://api.github.com/users/thedillonb/received_events", 351 | "type": "User", 352 | "site_admin": false 353 | }, 354 | "private": false, 355 | "html_url": "https://github.com/thedillonb/TestTestTest", 356 | "description": ":cat: Awesome!!", 357 | "fork": false, 358 | "url": "https://api.github.com/repos/thedillonb/TestTestTest", 359 | "forks_url": "https://api.github.com/repos/thedillonb/TestTestTest/forks", 360 | "keys_url": "https://api.github.com/repos/thedillonb/TestTestTest/keys{/key_id}", 361 | "collaborators_url": "https://api.github.com/repos/thedillonb/TestTestTest/collaborators{/collaborator}", 362 | "teams_url": "https://api.github.com/repos/thedillonb/TestTestTest/teams", 363 | "hooks_url": "https://api.github.com/repos/thedillonb/TestTestTest/hooks", 364 | "issue_events_url": "https://api.github.com/repos/thedillonb/TestTestTest/issues/events{/number}", 365 | "events_url": "https://api.github.com/repos/thedillonb/TestTestTest/events", 366 | "assignees_url": "https://api.github.com/repos/thedillonb/TestTestTest/assignees{/user}", 367 | "branches_url": "https://api.github.com/repos/thedillonb/TestTestTest/branches{/branch}", 368 | "tags_url": "https://api.github.com/repos/thedillonb/TestTestTest/tags", 369 | "blobs_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/blobs{/sha}", 370 | "git_tags_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/tags{/sha}", 371 | "git_refs_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/refs{/sha}", 372 | "trees_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/trees{/sha}", 373 | "statuses_url": "https://api.github.com/repos/thedillonb/TestTestTest/statuses/{sha}", 374 | "languages_url": "https://api.github.com/repos/thedillonb/TestTestTest/languages", 375 | "stargazers_url": "https://api.github.com/repos/thedillonb/TestTestTest/stargazers", 376 | "contributors_url": "https://api.github.com/repos/thedillonb/TestTestTest/contributors", 377 | "subscribers_url": "https://api.github.com/repos/thedillonb/TestTestTest/subscribers", 378 | "subscription_url": "https://api.github.com/repos/thedillonb/TestTestTest/subscription", 379 | "commits_url": "https://api.github.com/repos/thedillonb/TestTestTest/commits{/sha}", 380 | "git_commits_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/commits{/sha}", 381 | "comments_url": "https://api.github.com/repos/thedillonb/TestTestTest/comments{/number}", 382 | "issue_comment_url": "https://api.github.com/repos/thedillonb/TestTestTest/issues/comments{/number}", 383 | "contents_url": "https://api.github.com/repos/thedillonb/TestTestTest/contents/{+path}", 384 | "compare_url": "https://api.github.com/repos/thedillonb/TestTestTest/compare/{base}...{head}", 385 | "merges_url": "https://api.github.com/repos/thedillonb/TestTestTest/merges", 386 | "archive_url": "https://api.github.com/repos/thedillonb/TestTestTest/{archive_format}{/ref}", 387 | "downloads_url": "https://api.github.com/repos/thedillonb/TestTestTest/downloads", 388 | "issues_url": "https://api.github.com/repos/thedillonb/TestTestTest/issues{/number}", 389 | "pulls_url": "https://api.github.com/repos/thedillonb/TestTestTest/pulls{/number}", 390 | "milestones_url": "https://api.github.com/repos/thedillonb/TestTestTest/milestones{/number}", 391 | "notifications_url": "https://api.github.com/repos/thedillonb/TestTestTest/notifications{?since,all,participating}", 392 | "labels_url": "https://api.github.com/repos/thedillonb/TestTestTest/labels{/name}", 393 | "releases_url": "https://api.github.com/repos/thedillonb/TestTestTest/releases{/id}" 394 | }, 395 | "url": "https://api.github.com/notifications/threads/108524592", 396 | "subscription_url": "https://api.github.com/notifications/threads/108524592/subscription" 397 | }, 398 | { 399 | "id": "108547074", 400 | "unread": true, 401 | "reason": "mention", 402 | "updated_at": "2015-11-08T01:48:22Z", 403 | "last_read_at": null, 404 | "subject": { 405 | "title": "moo2", 406 | "url": "https://api.github.com/repos/thedillonb/TestTestTest/commits/cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66", 407 | "latest_comment_url": "https://api.github.com/repos/thedillonb/TestTestTest/commits/cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66", 408 | "type": "Commit" 409 | }, 410 | "repository": { 411 | "id": 25952452, 412 | "name": "TestTestTest", 413 | "full_name": "thedillonb/TestTestTest", 414 | "owner": { 415 | "login": "thedillonb", 416 | "id": 1644068, 417 | "avatar_url": "https://avatars.githubusercontent.com/u/1644068?v=3", 418 | "gravatar_id": "", 419 | "url": "https://api.github.com/users/thedillonb", 420 | "html_url": "https://github.com/thedillonb", 421 | "followers_url": "https://api.github.com/users/thedillonb/followers", 422 | "following_url": "https://api.github.com/users/thedillonb/following{/other_user}", 423 | "gists_url": "https://api.github.com/users/thedillonb/gists{/gist_id}", 424 | "starred_url": "https://api.github.com/users/thedillonb/starred{/owner}{/repo}", 425 | "subscriptions_url": "https://api.github.com/users/thedillonb/subscriptions", 426 | "organizations_url": "https://api.github.com/users/thedillonb/orgs", 427 | "repos_url": "https://api.github.com/users/thedillonb/repos", 428 | "events_url": "https://api.github.com/users/thedillonb/events{/privacy}", 429 | "received_events_url": "https://api.github.com/users/thedillonb/received_events", 430 | "type": "User", 431 | "site_admin": false 432 | }, 433 | "private": false, 434 | "html_url": "https://github.com/thedillonb/TestTestTest", 435 | "description": ":cat: Awesome!!", 436 | "fork": false, 437 | "url": "https://api.github.com/repos/thedillonb/TestTestTest", 438 | "forks_url": "https://api.github.com/repos/thedillonb/TestTestTest/forks", 439 | "keys_url": "https://api.github.com/repos/thedillonb/TestTestTest/keys{/key_id}", 440 | "collaborators_url": "https://api.github.com/repos/thedillonb/TestTestTest/collaborators{/collaborator}", 441 | "teams_url": "https://api.github.com/repos/thedillonb/TestTestTest/teams", 442 | "hooks_url": "https://api.github.com/repos/thedillonb/TestTestTest/hooks", 443 | "issue_events_url": "https://api.github.com/repos/thedillonb/TestTestTest/issues/events{/number}", 444 | "events_url": "https://api.github.com/repos/thedillonb/TestTestTest/events", 445 | "assignees_url": "https://api.github.com/repos/thedillonb/TestTestTest/assignees{/user}", 446 | "branches_url": "https://api.github.com/repos/thedillonb/TestTestTest/branches{/branch}", 447 | "tags_url": "https://api.github.com/repos/thedillonb/TestTestTest/tags", 448 | "blobs_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/blobs{/sha}", 449 | "git_tags_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/tags{/sha}", 450 | "git_refs_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/refs{/sha}", 451 | "trees_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/trees{/sha}", 452 | "statuses_url": "https://api.github.com/repos/thedillonb/TestTestTest/statuses/{sha}", 453 | "languages_url": "https://api.github.com/repos/thedillonb/TestTestTest/languages", 454 | "stargazers_url": "https://api.github.com/repos/thedillonb/TestTestTest/stargazers", 455 | "contributors_url": "https://api.github.com/repos/thedillonb/TestTestTest/contributors", 456 | "subscribers_url": "https://api.github.com/repos/thedillonb/TestTestTest/subscribers", 457 | "subscription_url": "https://api.github.com/repos/thedillonb/TestTestTest/subscription", 458 | "commits_url": "https://api.github.com/repos/thedillonb/TestTestTest/commits{/sha}", 459 | "git_commits_url": "https://api.github.com/repos/thedillonb/TestTestTest/git/commits{/sha}", 460 | "comments_url": "https://api.github.com/repos/thedillonb/TestTestTest/comments{/number}", 461 | "issue_comment_url": "https://api.github.com/repos/thedillonb/TestTestTest/issues/comments{/number}", 462 | "contents_url": "https://api.github.com/repos/thedillonb/TestTestTest/contents/{+path}", 463 | "compare_url": "https://api.github.com/repos/thedillonb/TestTestTest/compare/{base}...{head}", 464 | "merges_url": "https://api.github.com/repos/thedillonb/TestTestTest/merges", 465 | "archive_url": "https://api.github.com/repos/thedillonb/TestTestTest/{archive_format}{/ref}", 466 | "downloads_url": "https://api.github.com/repos/thedillonb/TestTestTest/downloads", 467 | "issues_url": "https://api.github.com/repos/thedillonb/TestTestTest/issues{/number}", 468 | "pulls_url": "https://api.github.com/repos/thedillonb/TestTestTest/pulls{/number}", 469 | "milestones_url": "https://api.github.com/repos/thedillonb/TestTestTest/milestones{/number}", 470 | "notifications_url": "https://api.github.com/repos/thedillonb/TestTestTest/notifications{?since,all,participating}", 471 | "labels_url": "https://api.github.com/repos/thedillonb/TestTestTest/labels{/name}", 472 | "releases_url": "https://api.github.com/repos/thedillonb/TestTestTest/releases{/id}" 473 | }, 474 | "url": "https://api.github.com/notifications/threads/108547074", 475 | "subscription_url": "https://api.github.com/notifications/threads/108547074/subscription" 476 | }, 477 | { 478 | "id": "1234", 479 | "unread": true, 480 | "reason": "subscribed", 481 | "updated_at": "2015-11-06T18:47:20Z", 482 | "last_read_at": null, 483 | "subject": { 484 | "title": "v0.1.1", 485 | "url": "https://api.github.com/repos/octokit/octokit.net/pulls/959", 486 | "latest_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments/154498869", 487 | "type": "Release" 488 | }, 489 | "repository": { 490 | "id": 7528679, 491 | "name": "octokit.net", 492 | "full_name": "octokit/octokit.net", 493 | "owner": { 494 | "login": "octokit", 495 | "id": 3430433, 496 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 497 | "gravatar_id": "", 498 | "url": "https://api.github.com/users/octokit", 499 | "html_url": "https://github.com/octokit", 500 | "followers_url": "https://api.github.com/users/octokit/followers", 501 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 502 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 503 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 504 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 505 | "organizations_url": "https://api.github.com/users/octokit/orgs", 506 | "repos_url": "https://api.github.com/users/octokit/repos", 507 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 508 | "received_events_url": "https://api.github.com/users/octokit/received_events", 509 | "type": "Organization", 510 | "site_admin": false 511 | }, 512 | "private": false, 513 | "html_url": "https://github.com/octokit/octokit.net", 514 | "description": "A GitHub API client library for .NET ", 515 | "fork": false, 516 | "url": "https://api.github.com/repos/octokit/octokit.net", 517 | "forks_url": "https://api.github.com/repos/octokit/octokit.net/forks", 518 | "keys_url": "https://api.github.com/repos/octokit/octokit.net/keys{/key_id}", 519 | "collaborators_url": "https://api.github.com/repos/octokit/octokit.net/collaborators{/collaborator}", 520 | "teams_url": "https://api.github.com/repos/octokit/octokit.net/teams", 521 | "hooks_url": "https://api.github.com/repos/octokit/octokit.net/hooks", 522 | "issue_events_url": "https://api.github.com/repos/octokit/octokit.net/issues/events{/number}", 523 | "events_url": "https://api.github.com/repos/octokit/octokit.net/events", 524 | "assignees_url": "https://api.github.com/repos/octokit/octokit.net/assignees{/user}", 525 | "branches_url": "https://api.github.com/repos/octokit/octokit.net/branches{/branch}", 526 | "tags_url": "https://api.github.com/repos/octokit/octokit.net/tags", 527 | "blobs_url": "https://api.github.com/repos/octokit/octokit.net/git/blobs{/sha}", 528 | "git_tags_url": "https://api.github.com/repos/octokit/octokit.net/git/tags{/sha}", 529 | "git_refs_url": "https://api.github.com/repos/octokit/octokit.net/git/refs{/sha}", 530 | "trees_url": "https://api.github.com/repos/octokit/octokit.net/git/trees{/sha}", 531 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/{sha}", 532 | "languages_url": "https://api.github.com/repos/octokit/octokit.net/languages", 533 | "stargazers_url": "https://api.github.com/repos/octokit/octokit.net/stargazers", 534 | "contributors_url": "https://api.github.com/repos/octokit/octokit.net/contributors", 535 | "subscribers_url": "https://api.github.com/repos/octokit/octokit.net/subscribers", 536 | "subscription_url": "https://api.github.com/repos/octokit/octokit.net/subscription", 537 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/commits{/sha}", 538 | "git_commits_url": "https://api.github.com/repos/octokit/octokit.net/git/commits{/sha}", 539 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/comments{/number}", 540 | "issue_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments{/number}", 541 | "contents_url": "https://api.github.com/repos/octokit/octokit.net/contents/{+path}", 542 | "compare_url": "https://api.github.com/repos/octokit/octokit.net/compare/{base}...{head}", 543 | "merges_url": "https://api.github.com/repos/octokit/octokit.net/merges", 544 | "archive_url": "https://api.github.com/repos/octokit/octokit.net/{archive_format}{/ref}", 545 | "downloads_url": "https://api.github.com/repos/octokit/octokit.net/downloads", 546 | "issues_url": "https://api.github.com/repos/octokit/octokit.net/issues{/number}", 547 | "pulls_url": "https://api.github.com/repos/octokit/octokit.net/pulls{/number}", 548 | "milestones_url": "https://api.github.com/repos/octokit/octokit.net/milestones{/number}", 549 | "notifications_url": "https://api.github.com/repos/octokit/octokit.net/notifications{?since,all,participating}", 550 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/labels{/name}", 551 | "releases_url": "https://api.github.com/repos/octokit/octokit.net/releases{/id}" 552 | }, 553 | "url": "https://api.github.com/notifications/threads/108039090", 554 | "subscription_url": "https://api.github.com/notifications/threads/108039090/subscription" 555 | } 556 | ], 557 | "/repos/octokit/octokit.net/issues/comments/154498869": { 558 | "url": "https://api.github.com/repos/octokit/octokit.net/issues/comments/154498869", 559 | "html_url": "https://github.com/octokit/octokit.net/pull/959#issuecomment-154498869", 560 | "issue_url": "https://api.github.com/repos/octokit/octokit.net/issues/959", 561 | "id": 154498869, 562 | "user": { 563 | "login": "naveensrinivasan", 564 | "id": 172697, 565 | "avatar_url": "https://avatars.githubusercontent.com/u/172697?v=3", 566 | "gravatar_id": "", 567 | "url": "https://api.github.com/users/naveensrinivasan", 568 | "html_url": "https://github.com/naveensrinivasan", 569 | "followers_url": "https://api.github.com/users/naveensrinivasan/followers", 570 | "following_url": "https://api.github.com/users/naveensrinivasan/following{/other_user}", 571 | "gists_url": "https://api.github.com/users/naveensrinivasan/gists{/gist_id}", 572 | "starred_url": "https://api.github.com/users/naveensrinivasan/starred{/owner}{/repo}", 573 | "subscriptions_url": "https://api.github.com/users/naveensrinivasan/subscriptions", 574 | "organizations_url": "https://api.github.com/users/naveensrinivasan/orgs", 575 | "repos_url": "https://api.github.com/users/naveensrinivasan/repos", 576 | "events_url": "https://api.github.com/users/naveensrinivasan/events{/privacy}", 577 | "received_events_url": "https://api.github.com/users/naveensrinivasan/received_events", 578 | "type": "User", 579 | "site_admin": false 580 | }, 581 | "created_at": "2015-11-06T18:47:20Z", 582 | "updated_at": "2015-11-06T18:47:20Z", 583 | "body": "@shiftkey \r\n``` csharp\r\npublic async Task> GetAllContents(string owner, string name, string reference)\r\n```\r\nCannot have this because there is already another overload with the same parameter types\r\n\r\n``` csharp\r\npublic async Task> GetAllContents(string owner, string name, string path)\r\n```\r\n\r\nWhat do you want to do?" 584 | }, 585 | "/repos/octokit/octokit.net/pulls/807": { 586 | "url": "https://api.github.com/repos/octokit/octokit.net/pulls/807", 587 | "id": 35721493, 588 | "html_url": "https://github.com/octokit/octokit.net/pull/807", 589 | "diff_url": "https://github.com/octokit/octokit.net/pull/807.diff", 590 | "patch_url": "https://github.com/octokit/octokit.net/pull/807.patch", 591 | "issue_url": "https://api.github.com/repos/octokit/octokit.net/issues/807", 592 | "number": 807, 593 | "state": "closed", 594 | "locked": false, 595 | "title": "added a tailored CodeFormatter to Octokit", 596 | "user": { 597 | "login": "shiftkey", 598 | "id": 359239, 599 | "avatar_url": "https://avatars.githubusercontent.com/u/359239?v=3", 600 | "gravatar_id": "", 601 | "url": "https://api.github.com/users/shiftkey", 602 | "html_url": "https://github.com/shiftkey", 603 | "followers_url": "https://api.github.com/users/shiftkey/followers", 604 | "following_url": "https://api.github.com/users/shiftkey/following{/other_user}", 605 | "gists_url": "https://api.github.com/users/shiftkey/gists{/gist_id}", 606 | "starred_url": "https://api.github.com/users/shiftkey/starred{/owner}{/repo}", 607 | "subscriptions_url": "https://api.github.com/users/shiftkey/subscriptions", 608 | "organizations_url": "https://api.github.com/users/shiftkey/orgs", 609 | "repos_url": "https://api.github.com/users/shiftkey/repos", 610 | "events_url": "https://api.github.com/users/shiftkey/events{/privacy}", 611 | "received_events_url": "https://api.github.com/users/shiftkey/received_events", 612 | "type": "User", 613 | "site_admin": true 614 | }, 615 | "body": "This is wired up in the build script so you can run it at your leisure:\r\n\r\n`.\\build FormatCode`\r\n\r\nThe source code for the tool: https://github.com/shiftkey/codeformatter/pull/1\r\n\r\nI disabled a couple of rules from the upstream codeformatter repo which go against our coding style:\r\n\r\n - new line above first `using` statement\r\n - specify the visibility of a field/member explicitly\r\n - private fields naming style\r\n\r\nThe net result is that things are actually pretty good, and we get some cleanup of whitespace for free.\r\n\r\n- [x] is the unicode (c) correct inside `AssemblyInfo.cs`?\r\n- ~~[x] update `AssemblyInfo.cs` contents~~ :boot:ed\r\n- [x] braces for empty constructors - what rule is this?\r\n- ~~[ ] remove copyright rename rule~~ :boot:ed\r\n- [x] wait for #799 to land and then rebase this \r\n- [x] docs docs docs\r\n- [x] hold for VS2015 support\r\n- [x] run over codebase once #808 is merged", 616 | "created_at": "2015-05-19T11:45:52Z", 617 | "updated_at": "2015-11-04T22:53:24Z", 618 | "closed_at": "2015-11-04T22:53:22Z", 619 | "merged_at": "2015-11-04T22:53:22Z", 620 | "merge_commit_sha": "b5b995ab75f67e84a3b8764cd97d178a32857d42", 621 | "assignee": null, 622 | "milestone": { 623 | "url": "https://api.github.com/repos/octokit/octokit.net/milestones/4", 624 | "html_url": "https://github.com/octokit/octokit.net/milestones/VS2015%20support", 625 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/milestones/4/labels", 626 | "id": 1226985, 627 | "number": 4, 628 | "title": "VS2015 support", 629 | "description": "", 630 | "creator": { 631 | "login": "shiftkey", 632 | "id": 359239, 633 | "avatar_url": "https://avatars.githubusercontent.com/u/359239?v=3", 634 | "gravatar_id": "", 635 | "url": "https://api.github.com/users/shiftkey", 636 | "html_url": "https://github.com/shiftkey", 637 | "followers_url": "https://api.github.com/users/shiftkey/followers", 638 | "following_url": "https://api.github.com/users/shiftkey/following{/other_user}", 639 | "gists_url": "https://api.github.com/users/shiftkey/gists{/gist_id}", 640 | "starred_url": "https://api.github.com/users/shiftkey/starred{/owner}{/repo}", 641 | "subscriptions_url": "https://api.github.com/users/shiftkey/subscriptions", 642 | "organizations_url": "https://api.github.com/users/shiftkey/orgs", 643 | "repos_url": "https://api.github.com/users/shiftkey/repos", 644 | "events_url": "https://api.github.com/users/shiftkey/events{/privacy}", 645 | "received_events_url": "https://api.github.com/users/shiftkey/received_events", 646 | "type": "User", 647 | "site_admin": true 648 | }, 649 | "open_issues": 6, 650 | "closed_issues": 2, 651 | "state": "open", 652 | "created_at": "2015-07-27T07:36:15Z", 653 | "updated_at": "2015-11-04T22:53:22Z", 654 | "due_on": null, 655 | "closed_at": null 656 | }, 657 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/pulls/807/commits", 658 | "review_comments_url": "https://api.github.com/repos/octokit/octokit.net/pulls/807/comments", 659 | "review_comment_url": "https://api.github.com/repos/octokit/octokit.net/pulls/comments{/number}", 660 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/issues/807/comments", 661 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/80719c003359bed7308e1c698564bc1aeeac4804", 662 | "head": { 663 | "label": "octokit:codeformatter", 664 | "ref": "codeformatter", 665 | "sha": "80719c003359bed7308e1c698564bc1aeeac4804", 666 | "user": { 667 | "login": "octokit", 668 | "id": 3430433, 669 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 670 | "gravatar_id": "", 671 | "url": "https://api.github.com/users/octokit", 672 | "html_url": "https://github.com/octokit", 673 | "followers_url": "https://api.github.com/users/octokit/followers", 674 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 675 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 676 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 677 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 678 | "organizations_url": "https://api.github.com/users/octokit/orgs", 679 | "repos_url": "https://api.github.com/users/octokit/repos", 680 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 681 | "received_events_url": "https://api.github.com/users/octokit/received_events", 682 | "type": "Organization", 683 | "site_admin": false 684 | }, 685 | "repo": { 686 | "id": 7528679, 687 | "name": "octokit.net", 688 | "full_name": "octokit/octokit.net", 689 | "owner": { 690 | "login": "octokit", 691 | "id": 3430433, 692 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 693 | "gravatar_id": "", 694 | "url": "https://api.github.com/users/octokit", 695 | "html_url": "https://github.com/octokit", 696 | "followers_url": "https://api.github.com/users/octokit/followers", 697 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 698 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 699 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 700 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 701 | "organizations_url": "https://api.github.com/users/octokit/orgs", 702 | "repos_url": "https://api.github.com/users/octokit/repos", 703 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 704 | "received_events_url": "https://api.github.com/users/octokit/received_events", 705 | "type": "Organization", 706 | "site_admin": false 707 | }, 708 | "private": false, 709 | "html_url": "https://github.com/octokit/octokit.net", 710 | "description": "A GitHub API client library for .NET ", 711 | "fork": false, 712 | "url": "https://api.github.com/repos/octokit/octokit.net", 713 | "forks_url": "https://api.github.com/repos/octokit/octokit.net/forks", 714 | "keys_url": "https://api.github.com/repos/octokit/octokit.net/keys{/key_id}", 715 | "collaborators_url": "https://api.github.com/repos/octokit/octokit.net/collaborators{/collaborator}", 716 | "teams_url": "https://api.github.com/repos/octokit/octokit.net/teams", 717 | "hooks_url": "https://api.github.com/repos/octokit/octokit.net/hooks", 718 | "issue_events_url": "https://api.github.com/repos/octokit/octokit.net/issues/events{/number}", 719 | "events_url": "https://api.github.com/repos/octokit/octokit.net/events", 720 | "assignees_url": "https://api.github.com/repos/octokit/octokit.net/assignees{/user}", 721 | "branches_url": "https://api.github.com/repos/octokit/octokit.net/branches{/branch}", 722 | "tags_url": "https://api.github.com/repos/octokit/octokit.net/tags", 723 | "blobs_url": "https://api.github.com/repos/octokit/octokit.net/git/blobs{/sha}", 724 | "git_tags_url": "https://api.github.com/repos/octokit/octokit.net/git/tags{/sha}", 725 | "git_refs_url": "https://api.github.com/repos/octokit/octokit.net/git/refs{/sha}", 726 | "trees_url": "https://api.github.com/repos/octokit/octokit.net/git/trees{/sha}", 727 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/{sha}", 728 | "languages_url": "https://api.github.com/repos/octokit/octokit.net/languages", 729 | "stargazers_url": "https://api.github.com/repos/octokit/octokit.net/stargazers", 730 | "contributors_url": "https://api.github.com/repos/octokit/octokit.net/contributors", 731 | "subscribers_url": "https://api.github.com/repos/octokit/octokit.net/subscribers", 732 | "subscription_url": "https://api.github.com/repos/octokit/octokit.net/subscription", 733 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/commits{/sha}", 734 | "git_commits_url": "https://api.github.com/repos/octokit/octokit.net/git/commits{/sha}", 735 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/comments{/number}", 736 | "issue_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments{/number}", 737 | "contents_url": "https://api.github.com/repos/octokit/octokit.net/contents/{+path}", 738 | "compare_url": "https://api.github.com/repos/octokit/octokit.net/compare/{base}...{head}", 739 | "merges_url": "https://api.github.com/repos/octokit/octokit.net/merges", 740 | "archive_url": "https://api.github.com/repos/octokit/octokit.net/{archive_format}{/ref}", 741 | "downloads_url": "https://api.github.com/repos/octokit/octokit.net/downloads", 742 | "issues_url": "https://api.github.com/repos/octokit/octokit.net/issues{/number}", 743 | "pulls_url": "https://api.github.com/repos/octokit/octokit.net/pulls{/number}", 744 | "milestones_url": "https://api.github.com/repos/octokit/octokit.net/milestones{/number}", 745 | "notifications_url": "https://api.github.com/repos/octokit/octokit.net/notifications{?since,all,participating}", 746 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/labels{/name}", 747 | "releases_url": "https://api.github.com/repos/octokit/octokit.net/releases{/id}", 748 | "created_at": "2013-01-09T20:48:45Z", 749 | "updated_at": "2015-11-05T23:42:52Z", 750 | "pushed_at": "2015-11-06T00:51:12Z", 751 | "git_url": "git://github.com/octokit/octokit.net.git", 752 | "ssh_url": "git@github.com:octokit/octokit.net.git", 753 | "clone_url": "https://github.com/octokit/octokit.net.git", 754 | "svn_url": "https://github.com/octokit/octokit.net", 755 | "homepage": null, 756 | "size": 86216, 757 | "stargazers_count": 779, 758 | "watchers_count": 779, 759 | "language": "C#", 760 | "has_issues": true, 761 | "has_downloads": true, 762 | "has_wiki": false, 763 | "has_pages": false, 764 | "forks_count": 377, 765 | "mirror_url": null, 766 | "open_issues_count": 80, 767 | "forks": 377, 768 | "open_issues": 80, 769 | "watchers": 779, 770 | "default_branch": "master" 771 | } 772 | }, 773 | "base": { 774 | "label": "octokit:master", 775 | "ref": "master", 776 | "sha": "7ec44d1017bea41338af1719e077f2b4c72e52d5", 777 | "user": { 778 | "login": "octokit", 779 | "id": 3430433, 780 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 781 | "gravatar_id": "", 782 | "url": "https://api.github.com/users/octokit", 783 | "html_url": "https://github.com/octokit", 784 | "followers_url": "https://api.github.com/users/octokit/followers", 785 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 786 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 787 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 788 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 789 | "organizations_url": "https://api.github.com/users/octokit/orgs", 790 | "repos_url": "https://api.github.com/users/octokit/repos", 791 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 792 | "received_events_url": "https://api.github.com/users/octokit/received_events", 793 | "type": "Organization", 794 | "site_admin": false 795 | }, 796 | "repo": { 797 | "id": 7528679, 798 | "name": "octokit.net", 799 | "full_name": "octokit/octokit.net", 800 | "owner": { 801 | "login": "octokit", 802 | "id": 3430433, 803 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 804 | "gravatar_id": "", 805 | "url": "https://api.github.com/users/octokit", 806 | "html_url": "https://github.com/octokit", 807 | "followers_url": "https://api.github.com/users/octokit/followers", 808 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 809 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 810 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 811 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 812 | "organizations_url": "https://api.github.com/users/octokit/orgs", 813 | "repos_url": "https://api.github.com/users/octokit/repos", 814 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 815 | "received_events_url": "https://api.github.com/users/octokit/received_events", 816 | "type": "Organization", 817 | "site_admin": false 818 | }, 819 | "private": false, 820 | "html_url": "https://github.com/octokit/octokit.net", 821 | "description": "A GitHub API client library for .NET ", 822 | "fork": false, 823 | "url": "https://api.github.com/repos/octokit/octokit.net", 824 | "forks_url": "https://api.github.com/repos/octokit/octokit.net/forks", 825 | "keys_url": "https://api.github.com/repos/octokit/octokit.net/keys{/key_id}", 826 | "collaborators_url": "https://api.github.com/repos/octokit/octokit.net/collaborators{/collaborator}", 827 | "teams_url": "https://api.github.com/repos/octokit/octokit.net/teams", 828 | "hooks_url": "https://api.github.com/repos/octokit/octokit.net/hooks", 829 | "issue_events_url": "https://api.github.com/repos/octokit/octokit.net/issues/events{/number}", 830 | "events_url": "https://api.github.com/repos/octokit/octokit.net/events", 831 | "assignees_url": "https://api.github.com/repos/octokit/octokit.net/assignees{/user}", 832 | "branches_url": "https://api.github.com/repos/octokit/octokit.net/branches{/branch}", 833 | "tags_url": "https://api.github.com/repos/octokit/octokit.net/tags", 834 | "blobs_url": "https://api.github.com/repos/octokit/octokit.net/git/blobs{/sha}", 835 | "git_tags_url": "https://api.github.com/repos/octokit/octokit.net/git/tags{/sha}", 836 | "git_refs_url": "https://api.github.com/repos/octokit/octokit.net/git/refs{/sha}", 837 | "trees_url": "https://api.github.com/repos/octokit/octokit.net/git/trees{/sha}", 838 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/{sha}", 839 | "languages_url": "https://api.github.com/repos/octokit/octokit.net/languages", 840 | "stargazers_url": "https://api.github.com/repos/octokit/octokit.net/stargazers", 841 | "contributors_url": "https://api.github.com/repos/octokit/octokit.net/contributors", 842 | "subscribers_url": "https://api.github.com/repos/octokit/octokit.net/subscribers", 843 | "subscription_url": "https://api.github.com/repos/octokit/octokit.net/subscription", 844 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/commits{/sha}", 845 | "git_commits_url": "https://api.github.com/repos/octokit/octokit.net/git/commits{/sha}", 846 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/comments{/number}", 847 | "issue_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments{/number}", 848 | "contents_url": "https://api.github.com/repos/octokit/octokit.net/contents/{+path}", 849 | "compare_url": "https://api.github.com/repos/octokit/octokit.net/compare/{base}...{head}", 850 | "merges_url": "https://api.github.com/repos/octokit/octokit.net/merges", 851 | "archive_url": "https://api.github.com/repos/octokit/octokit.net/{archive_format}{/ref}", 852 | "downloads_url": "https://api.github.com/repos/octokit/octokit.net/downloads", 853 | "issues_url": "https://api.github.com/repos/octokit/octokit.net/issues{/number}", 854 | "pulls_url": "https://api.github.com/repos/octokit/octokit.net/pulls{/number}", 855 | "milestones_url": "https://api.github.com/repos/octokit/octokit.net/milestones{/number}", 856 | "notifications_url": "https://api.github.com/repos/octokit/octokit.net/notifications{?since,all,participating}", 857 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/labels{/name}", 858 | "releases_url": "https://api.github.com/repos/octokit/octokit.net/releases{/id}", 859 | "created_at": "2013-01-09T20:48:45Z", 860 | "updated_at": "2015-11-05T23:42:52Z", 861 | "pushed_at": "2015-11-06T00:51:12Z", 862 | "git_url": "git://github.com/octokit/octokit.net.git", 863 | "ssh_url": "git@github.com:octokit/octokit.net.git", 864 | "clone_url": "https://github.com/octokit/octokit.net.git", 865 | "svn_url": "https://github.com/octokit/octokit.net", 866 | "homepage": null, 867 | "size": 86216, 868 | "stargazers_count": 779, 869 | "watchers_count": 779, 870 | "language": "C#", 871 | "has_issues": true, 872 | "has_downloads": true, 873 | "has_wiki": false, 874 | "has_pages": false, 875 | "forks_count": 377, 876 | "mirror_url": null, 877 | "open_issues_count": 80, 878 | "forks": 377, 879 | "open_issues": 80, 880 | "watchers": 779, 881 | "default_branch": "master" 882 | } 883 | }, 884 | "_links": { 885 | "self": { 886 | "href": "https://api.github.com/repos/octokit/octokit.net/pulls/807" 887 | }, 888 | "html": { 889 | "href": "https://github.com/octokit/octokit.net/pull/807" 890 | }, 891 | "issue": { 892 | "href": "https://api.github.com/repos/octokit/octokit.net/issues/807" 893 | }, 894 | "comments": { 895 | "href": "https://api.github.com/repos/octokit/octokit.net/issues/807/comments" 896 | }, 897 | "review_comments": { 898 | "href": "https://api.github.com/repos/octokit/octokit.net/pulls/807/comments" 899 | }, 900 | "review_comment": { 901 | "href": "https://api.github.com/repos/octokit/octokit.net/pulls/comments{/number}" 902 | }, 903 | "commits": { 904 | "href": "https://api.github.com/repos/octokit/octokit.net/pulls/807/commits" 905 | }, 906 | "statuses": { 907 | "href": "https://api.github.com/repos/octokit/octokit.net/statuses/80719c003359bed7308e1c698564bc1aeeac4804" 908 | } 909 | }, 910 | "merged": true, 911 | "mergeable": null, 912 | "mergeable_state": "unknown", 913 | "merged_by": { 914 | "login": "shiftkey", 915 | "id": 359239, 916 | "avatar_url": "https://avatars.githubusercontent.com/u/359239?v=3", 917 | "gravatar_id": "", 918 | "url": "https://api.github.com/users/shiftkey", 919 | "html_url": "https://github.com/shiftkey", 920 | "followers_url": "https://api.github.com/users/shiftkey/followers", 921 | "following_url": "https://api.github.com/users/shiftkey/following{/other_user}", 922 | "gists_url": "https://api.github.com/users/shiftkey/gists{/gist_id}", 923 | "starred_url": "https://api.github.com/users/shiftkey/starred{/owner}{/repo}", 924 | "subscriptions_url": "https://api.github.com/users/shiftkey/subscriptions", 925 | "organizations_url": "https://api.github.com/users/shiftkey/orgs", 926 | "repos_url": "https://api.github.com/users/shiftkey/repos", 927 | "events_url": "https://api.github.com/users/shiftkey/events{/privacy}", 928 | "received_events_url": "https://api.github.com/users/shiftkey/received_events", 929 | "type": "User", 930 | "site_admin": true 931 | }, 932 | "comments": 4, 933 | "review_comments": 3, 934 | "commits": 4, 935 | "additions": 461, 936 | "deletions": 438, 937 | "changed_files": 202 938 | }, 939 | "/repos/octokit/octokit.net/issues/423": { 940 | "url": "https://api.github.com/repos/octokit/octokit.net/issues/423", 941 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/issues/423/labels{/name}", 942 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/issues/423/comments", 943 | "events_url": "https://api.github.com/repos/octokit/octokit.net/issues/423/events", 944 | "html_url": "https://github.com/octokit/octokit.net/issues/423", 945 | "id": 28839534, 946 | "number": 423, 947 | "title": "Validation checks on NewRepository.GitignoreTemplate", 948 | "user": { 949 | "login": "shiftkey", 950 | "id": 359239, 951 | "avatar_url": "https://avatars.githubusercontent.com/u/359239?v=3", 952 | "gravatar_id": "", 953 | "url": "https://api.github.com/users/shiftkey", 954 | "html_url": "https://github.com/shiftkey", 955 | "followers_url": "https://api.github.com/users/shiftkey/followers", 956 | "following_url": "https://api.github.com/users/shiftkey/following{/other_user}", 957 | "gists_url": "https://api.github.com/users/shiftkey/gists{/gist_id}", 958 | "starred_url": "https://api.github.com/users/shiftkey/starred{/owner}{/repo}", 959 | "subscriptions_url": "https://api.github.com/users/shiftkey/subscriptions", 960 | "organizations_url": "https://api.github.com/users/shiftkey/orgs", 961 | "repos_url": "https://api.github.com/users/shiftkey/repos", 962 | "events_url": "https://api.github.com/users/shiftkey/events{/privacy}", 963 | "received_events_url": "https://api.github.com/users/shiftkey/received_events", 964 | "type": "User", 965 | "site_admin": true 966 | }, 967 | "labels": [ 968 | { 969 | "url": "https://api.github.com/repos/octokit/octokit.net/labels/easy-fix", 970 | "name": "easy-fix", 971 | "color": "009800" 972 | }, 973 | { 974 | "url": "https://api.github.com/repos/octokit/octokit.net/labels/enhancement", 975 | "name": "enhancement", 976 | "color": "84b6eb" 977 | } 978 | ], 979 | "state": "closed", 980 | "locked": false, 981 | "assignee": null, 982 | "milestone": null, 983 | "comments": 2, 984 | "created_at": "2014-03-06T00:19:04Z", 985 | "updated_at": "2015-11-04T17:16:51Z", 986 | "closed_at": "2015-11-04T17:16:51Z", 987 | "body": "One of our integration tests was failing with this response:\r\n\r\n```\r\n{\r\n \"message\":\"Validation Failed\",\r\n \"documentation_url\":\"http://developer.github.com/v3/repos/#create\",\r\n \"errors\":\r\n [{\r\n \"resource\":\"Repository\",\r\n \"code\":\"custom\",\r\n \"field\":\"gitignore_template\",\r\n \"message\":\"gitignore_template is an unknown gitignore template.\"\r\n }]\r\n}\r\n```\r\n\r\nThis is because we were passing `visualstudio`, rather than `VisualStudio`.\r\n\r\nWe should surface this as a proper exception message in the future...", 988 | "closed_by": { 989 | "login": "shiftkey", 990 | "id": 359239, 991 | "avatar_url": "https://avatars.githubusercontent.com/u/359239?v=3", 992 | "gravatar_id": "", 993 | "url": "https://api.github.com/users/shiftkey", 994 | "html_url": "https://github.com/shiftkey", 995 | "followers_url": "https://api.github.com/users/shiftkey/followers", 996 | "following_url": "https://api.github.com/users/shiftkey/following{/other_user}", 997 | "gists_url": "https://api.github.com/users/shiftkey/gists{/gist_id}", 998 | "starred_url": "https://api.github.com/users/shiftkey/starred{/owner}{/repo}", 999 | "subscriptions_url": "https://api.github.com/users/shiftkey/subscriptions", 1000 | "organizations_url": "https://api.github.com/users/shiftkey/orgs", 1001 | "repos_url": "https://api.github.com/users/shiftkey/repos", 1002 | "events_url": "https://api.github.com/users/shiftkey/events{/privacy}", 1003 | "received_events_url": "https://api.github.com/users/shiftkey/received_events", 1004 | "type": "User", 1005 | "site_admin": true 1006 | } 1007 | }, 1008 | "/repos/octokit/octokit.net/pulls/935": { 1009 | "url": "https://api.github.com/repos/octokit/octokit.net/pulls/935", 1010 | "id": 47324982, 1011 | "html_url": "https://github.com/octokit/octokit.net/pull/935", 1012 | "diff_url": "https://github.com/octokit/octokit.net/pull/935.diff", 1013 | "patch_url": "https://github.com/octokit/octokit.net/pull/935.patch", 1014 | "issue_url": "https://api.github.com/repos/octokit/octokit.net/issues/935", 1015 | "number": 935, 1016 | "state": "closed", 1017 | "locked": false, 1018 | "title": "added \"Merged\" in searchissues which allows search repos by merged da…", 1019 | "user": { 1020 | "login": "chenjiaming93", 1021 | "id": 13130367, 1022 | "avatar_url": "https://avatars.githubusercontent.com/u/13130367?v=3", 1023 | "gravatar_id": "", 1024 | "url": "https://api.github.com/users/chenjiaming93", 1025 | "html_url": "https://github.com/chenjiaming93", 1026 | "followers_url": "https://api.github.com/users/chenjiaming93/followers", 1027 | "following_url": "https://api.github.com/users/chenjiaming93/following{/other_user}", 1028 | "gists_url": "https://api.github.com/users/chenjiaming93/gists{/gist_id}", 1029 | "starred_url": "https://api.github.com/users/chenjiaming93/starred{/owner}{/repo}", 1030 | "subscriptions_url": "https://api.github.com/users/chenjiaming93/subscriptions", 1031 | "organizations_url": "https://api.github.com/users/chenjiaming93/orgs", 1032 | "repos_url": "https://api.github.com/users/chenjiaming93/repos", 1033 | "events_url": "https://api.github.com/users/chenjiaming93/events{/privacy}", 1034 | "received_events_url": "https://api.github.com/users/chenjiaming93/received_events", 1035 | "type": "User", 1036 | "site_admin": false 1037 | }, 1038 | "body": "…te with existing syntax.\r\n\r\nit generates a CA1502 code excessive complexity warning and i suppressed it.\r\n#839 ", 1039 | "created_at": "2015-10-09T22:03:48Z", 1040 | "updated_at": "2015-11-03T19:52:27Z", 1041 | "closed_at": "2015-11-03T19:52:27Z", 1042 | "merged_at": null, 1043 | "merge_commit_sha": "c692d8c4daebfe6282df7faaae38f89ec6ec792a", 1044 | "assignee": null, 1045 | "milestone": null, 1046 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/pulls/935/commits", 1047 | "review_comments_url": "https://api.github.com/repos/octokit/octokit.net/pulls/935/comments", 1048 | "review_comment_url": "https://api.github.com/repos/octokit/octokit.net/pulls/comments{/number}", 1049 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/issues/935/comments", 1050 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/7e6fbd45079e9ed4e10aa50594e623f9b46f1cbf", 1051 | "head": { 1052 | "label": "chenjiaming93:jc01", 1053 | "ref": "jc01", 1054 | "sha": "7e6fbd45079e9ed4e10aa50594e623f9b46f1cbf", 1055 | "user": { 1056 | "login": "chenjiaming93", 1057 | "id": 13130367, 1058 | "avatar_url": "https://avatars.githubusercontent.com/u/13130367?v=3", 1059 | "gravatar_id": "", 1060 | "url": "https://api.github.com/users/chenjiaming93", 1061 | "html_url": "https://github.com/chenjiaming93", 1062 | "followers_url": "https://api.github.com/users/chenjiaming93/followers", 1063 | "following_url": "https://api.github.com/users/chenjiaming93/following{/other_user}", 1064 | "gists_url": "https://api.github.com/users/chenjiaming93/gists{/gist_id}", 1065 | "starred_url": "https://api.github.com/users/chenjiaming93/starred{/owner}{/repo}", 1066 | "subscriptions_url": "https://api.github.com/users/chenjiaming93/subscriptions", 1067 | "organizations_url": "https://api.github.com/users/chenjiaming93/orgs", 1068 | "repos_url": "https://api.github.com/users/chenjiaming93/repos", 1069 | "events_url": "https://api.github.com/users/chenjiaming93/events{/privacy}", 1070 | "received_events_url": "https://api.github.com/users/chenjiaming93/received_events", 1071 | "type": "User", 1072 | "site_admin": false 1073 | }, 1074 | "repo": null 1075 | }, 1076 | "base": { 1077 | "label": "octokit:master", 1078 | "ref": "master", 1079 | "sha": "e59c3cbc2d96e5c6d00520743f39d5e69c4f2dff", 1080 | "user": { 1081 | "login": "octokit", 1082 | "id": 3430433, 1083 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 1084 | "gravatar_id": "", 1085 | "url": "https://api.github.com/users/octokit", 1086 | "html_url": "https://github.com/octokit", 1087 | "followers_url": "https://api.github.com/users/octokit/followers", 1088 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 1089 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 1090 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 1091 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 1092 | "organizations_url": "https://api.github.com/users/octokit/orgs", 1093 | "repos_url": "https://api.github.com/users/octokit/repos", 1094 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 1095 | "received_events_url": "https://api.github.com/users/octokit/received_events", 1096 | "type": "Organization", 1097 | "site_admin": false 1098 | }, 1099 | "repo": { 1100 | "id": 7528679, 1101 | "name": "octokit.net", 1102 | "full_name": "octokit/octokit.net", 1103 | "owner": { 1104 | "login": "octokit", 1105 | "id": 3430433, 1106 | "avatar_url": "https://avatars.githubusercontent.com/u/3430433?v=3", 1107 | "gravatar_id": "", 1108 | "url": "https://api.github.com/users/octokit", 1109 | "html_url": "https://github.com/octokit", 1110 | "followers_url": "https://api.github.com/users/octokit/followers", 1111 | "following_url": "https://api.github.com/users/octokit/following{/other_user}", 1112 | "gists_url": "https://api.github.com/users/octokit/gists{/gist_id}", 1113 | "starred_url": "https://api.github.com/users/octokit/starred{/owner}{/repo}", 1114 | "subscriptions_url": "https://api.github.com/users/octokit/subscriptions", 1115 | "organizations_url": "https://api.github.com/users/octokit/orgs", 1116 | "repos_url": "https://api.github.com/users/octokit/repos", 1117 | "events_url": "https://api.github.com/users/octokit/events{/privacy}", 1118 | "received_events_url": "https://api.github.com/users/octokit/received_events", 1119 | "type": "Organization", 1120 | "site_admin": false 1121 | }, 1122 | "private": false, 1123 | "html_url": "https://github.com/octokit/octokit.net", 1124 | "description": "A GitHub API client library for .NET ", 1125 | "fork": false, 1126 | "url": "https://api.github.com/repos/octokit/octokit.net", 1127 | "forks_url": "https://api.github.com/repos/octokit/octokit.net/forks", 1128 | "keys_url": "https://api.github.com/repos/octokit/octokit.net/keys{/key_id}", 1129 | "collaborators_url": "https://api.github.com/repos/octokit/octokit.net/collaborators{/collaborator}", 1130 | "teams_url": "https://api.github.com/repos/octokit/octokit.net/teams", 1131 | "hooks_url": "https://api.github.com/repos/octokit/octokit.net/hooks", 1132 | "issue_events_url": "https://api.github.com/repos/octokit/octokit.net/issues/events{/number}", 1133 | "events_url": "https://api.github.com/repos/octokit/octokit.net/events", 1134 | "assignees_url": "https://api.github.com/repos/octokit/octokit.net/assignees{/user}", 1135 | "branches_url": "https://api.github.com/repos/octokit/octokit.net/branches{/branch}", 1136 | "tags_url": "https://api.github.com/repos/octokit/octokit.net/tags", 1137 | "blobs_url": "https://api.github.com/repos/octokit/octokit.net/git/blobs{/sha}", 1138 | "git_tags_url": "https://api.github.com/repos/octokit/octokit.net/git/tags{/sha}", 1139 | "git_refs_url": "https://api.github.com/repos/octokit/octokit.net/git/refs{/sha}", 1140 | "trees_url": "https://api.github.com/repos/octokit/octokit.net/git/trees{/sha}", 1141 | "statuses_url": "https://api.github.com/repos/octokit/octokit.net/statuses/{sha}", 1142 | "languages_url": "https://api.github.com/repos/octokit/octokit.net/languages", 1143 | "stargazers_url": "https://api.github.com/repos/octokit/octokit.net/stargazers", 1144 | "contributors_url": "https://api.github.com/repos/octokit/octokit.net/contributors", 1145 | "subscribers_url": "https://api.github.com/repos/octokit/octokit.net/subscribers", 1146 | "subscription_url": "https://api.github.com/repos/octokit/octokit.net/subscription", 1147 | "commits_url": "https://api.github.com/repos/octokit/octokit.net/commits{/sha}", 1148 | "git_commits_url": "https://api.github.com/repos/octokit/octokit.net/git/commits{/sha}", 1149 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/comments{/number}", 1150 | "issue_comment_url": "https://api.github.com/repos/octokit/octokit.net/issues/comments{/number}", 1151 | "contents_url": "https://api.github.com/repos/octokit/octokit.net/contents/{+path}", 1152 | "compare_url": "https://api.github.com/repos/octokit/octokit.net/compare/{base}...{head}", 1153 | "merges_url": "https://api.github.com/repos/octokit/octokit.net/merges", 1154 | "archive_url": "https://api.github.com/repos/octokit/octokit.net/{archive_format}{/ref}", 1155 | "downloads_url": "https://api.github.com/repos/octokit/octokit.net/downloads", 1156 | "issues_url": "https://api.github.com/repos/octokit/octokit.net/issues{/number}", 1157 | "pulls_url": "https://api.github.com/repos/octokit/octokit.net/pulls{/number}", 1158 | "milestones_url": "https://api.github.com/repos/octokit/octokit.net/milestones{/number}", 1159 | "notifications_url": "https://api.github.com/repos/octokit/octokit.net/notifications{?since,all,participating}", 1160 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/labels{/name}", 1161 | "releases_url": "https://api.github.com/repos/octokit/octokit.net/releases{/id}", 1162 | "created_at": "2013-01-09T20:48:45Z", 1163 | "updated_at": "2015-11-05T23:42:52Z", 1164 | "pushed_at": "2015-11-06T00:51:12Z", 1165 | "git_url": "git://github.com/octokit/octokit.net.git", 1166 | "ssh_url": "git@github.com:octokit/octokit.net.git", 1167 | "clone_url": "https://github.com/octokit/octokit.net.git", 1168 | "svn_url": "https://github.com/octokit/octokit.net", 1169 | "homepage": null, 1170 | "size": 86216, 1171 | "stargazers_count": 779, 1172 | "watchers_count": 779, 1173 | "language": "C#", 1174 | "has_issues": true, 1175 | "has_downloads": true, 1176 | "has_wiki": false, 1177 | "has_pages": false, 1178 | "forks_count": 377, 1179 | "mirror_url": null, 1180 | "open_issues_count": 80, 1181 | "forks": 377, 1182 | "open_issues": 80, 1183 | "watchers": 779, 1184 | "default_branch": "master" 1185 | } 1186 | }, 1187 | "_links": { 1188 | "self": { 1189 | "href": "https://api.github.com/repos/octokit/octokit.net/pulls/935" 1190 | }, 1191 | "html": { 1192 | "href": "https://github.com/octokit/octokit.net/pull/935" 1193 | }, 1194 | "issue": { 1195 | "href": "https://api.github.com/repos/octokit/octokit.net/issues/935" 1196 | }, 1197 | "comments": { 1198 | "href": "https://api.github.com/repos/octokit/octokit.net/issues/935/comments" 1199 | }, 1200 | "review_comments": { 1201 | "href": "https://api.github.com/repos/octokit/octokit.net/pulls/935/comments" 1202 | }, 1203 | "review_comment": { 1204 | "href": "https://api.github.com/repos/octokit/octokit.net/pulls/comments{/number}" 1205 | }, 1206 | "commits": { 1207 | "href": "https://api.github.com/repos/octokit/octokit.net/pulls/935/commits" 1208 | }, 1209 | "statuses": { 1210 | "href": "https://api.github.com/repos/octokit/octokit.net/statuses/7e6fbd45079e9ed4e10aa50594e623f9b46f1cbf" 1211 | } 1212 | }, 1213 | "merged": false, 1214 | "mergeable": true, 1215 | "mergeable_state": "clean", 1216 | "merged_by": null, 1217 | "comments": 2, 1218 | "review_comments": 6, 1219 | "commits": 4, 1220 | "additions": 90, 1221 | "deletions": 3, 1222 | "changed_files": 2 1223 | }, 1224 | "/repos/thedillonb/TestTestTest/comments/14258794": { 1225 | "url": "https://api.github.com/repos/thedillonb/TestTestTest/comments/14258794", 1226 | "html_url": "https://github.com/thedillonb/TestTestTest/commit/fffb244af01b360d6c3a055ade39f44cf3e82cf7#commitcomment-14258794", 1227 | "id": 14258794, 1228 | "user": { 1229 | "login": "dillonb123", 1230 | "id": 2868513, 1231 | "avatar_url": "https://avatars.githubusercontent.com/u/2868513?v=3", 1232 | "gravatar_id": "", 1233 | "url": "https://api.github.com/users/dillonb123", 1234 | "html_url": "https://github.com/dillonb123", 1235 | "followers_url": "https://api.github.com/users/dillonb123/followers", 1236 | "following_url": "https://api.github.com/users/dillonb123/following{/other_user}", 1237 | "gists_url": "https://api.github.com/users/dillonb123/gists{/gist_id}", 1238 | "starred_url": "https://api.github.com/users/dillonb123/starred{/owner}{/repo}", 1239 | "subscriptions_url": "https://api.github.com/users/dillonb123/subscriptions", 1240 | "organizations_url": "https://api.github.com/users/dillonb123/orgs", 1241 | "repos_url": "https://api.github.com/users/dillonb123/repos", 1242 | "events_url": "https://api.github.com/users/dillonb123/events{/privacy}", 1243 | "received_events_url": "https://api.github.com/users/dillonb123/received_events", 1244 | "type": "User", 1245 | "site_admin": false 1246 | }, 1247 | "position": null, 1248 | "line": null, 1249 | "path": "", 1250 | "commit_id": "fffb244af01b360d6c3a055ade39f44cf3e82cf7", 1251 | "created_at": "2015-11-07T16:47:00Z", 1252 | "updated_at": "2015-11-07T16:47:00Z", 1253 | "body": "What a great comment!" 1254 | }, 1255 | "/repos/octokit/octokit.net/issues/807": { 1256 | "url": "https://api.github.com/repos/octokit/octokit.net/issues/807", 1257 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/issues/807/labels{/name}", 1258 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/issues/807/comments", 1259 | "events_url": "https://api.github.com/repos/octokit/octokit.net/issues/807/events", 1260 | "html_url": "https://github.com/octokit/octokit.net/pull/807", 1261 | "id": 78055078, 1262 | "number": 807, 1263 | "title": "added a tailored CodeFormatter to Octokit", 1264 | "user": { 1265 | "login": "shiftkey", 1266 | "id": 359239, 1267 | "avatar_url": "https://avatars.githubusercontent.com/u/359239?v=3", 1268 | "gravatar_id": "", 1269 | "url": "https://api.github.com/users/shiftkey", 1270 | "html_url": "https://github.com/shiftkey", 1271 | "followers_url": "https://api.github.com/users/shiftkey/followers", 1272 | "following_url": "https://api.github.com/users/shiftkey/following{/other_user}", 1273 | "gists_url": "https://api.github.com/users/shiftkey/gists{/gist_id}", 1274 | "starred_url": "https://api.github.com/users/shiftkey/starred{/owner}{/repo}", 1275 | "subscriptions_url": "https://api.github.com/users/shiftkey/subscriptions", 1276 | "organizations_url": "https://api.github.com/users/shiftkey/orgs", 1277 | "repos_url": "https://api.github.com/users/shiftkey/repos", 1278 | "events_url": "https://api.github.com/users/shiftkey/events{/privacy}", 1279 | "received_events_url": "https://api.github.com/users/shiftkey/received_events", 1280 | "type": "User", 1281 | "site_admin": true 1282 | }, 1283 | "labels": [], 1284 | "state": "closed", 1285 | "locked": false, 1286 | "assignee": null, 1287 | "milestone": { 1288 | "url": "https://api.github.com/repos/octokit/octokit.net/milestones/4", 1289 | "html_url": "https://github.com/octokit/octokit.net/milestones/VS2015%20support", 1290 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/milestones/4/labels", 1291 | "id": 1226985, 1292 | "number": 4, 1293 | "title": "VS2015 support", 1294 | "description": "", 1295 | "creator": { 1296 | "login": "shiftkey", 1297 | "id": 359239, 1298 | "avatar_url": "https://avatars.githubusercontent.com/u/359239?v=3", 1299 | "gravatar_id": "", 1300 | "url": "https://api.github.com/users/shiftkey", 1301 | "html_url": "https://github.com/shiftkey", 1302 | "followers_url": "https://api.github.com/users/shiftkey/followers", 1303 | "following_url": "https://api.github.com/users/shiftkey/following{/other_user}", 1304 | "gists_url": "https://api.github.com/users/shiftkey/gists{/gist_id}", 1305 | "starred_url": "https://api.github.com/users/shiftkey/starred{/owner}{/repo}", 1306 | "subscriptions_url": "https://api.github.com/users/shiftkey/subscriptions", 1307 | "organizations_url": "https://api.github.com/users/shiftkey/orgs", 1308 | "repos_url": "https://api.github.com/users/shiftkey/repos", 1309 | "events_url": "https://api.github.com/users/shiftkey/events{/privacy}", 1310 | "received_events_url": "https://api.github.com/users/shiftkey/received_events", 1311 | "type": "User", 1312 | "site_admin": true 1313 | }, 1314 | "open_issues": 6, 1315 | "closed_issues": 2, 1316 | "state": "open", 1317 | "created_at": "2015-07-27T07:36:15Z", 1318 | "updated_at": "2015-11-04T22:53:22Z", 1319 | "due_on": null, 1320 | "closed_at": null 1321 | }, 1322 | "comments": 4, 1323 | "created_at": "2015-05-19T11:45:52Z", 1324 | "updated_at": "2015-11-04T22:53:24Z", 1325 | "closed_at": "2015-11-04T22:53:22Z", 1326 | "pull_request": { 1327 | "url": "https://api.github.com/repos/octokit/octokit.net/pulls/807", 1328 | "html_url": "https://github.com/octokit/octokit.net/pull/807", 1329 | "diff_url": "https://github.com/octokit/octokit.net/pull/807.diff", 1330 | "patch_url": "https://github.com/octokit/octokit.net/pull/807.patch" 1331 | }, 1332 | "body": "This is wired up in the build script so you can run it at your leisure:\r\n\r\n`.\\build FormatCode`\r\n\r\nThe source code for the tool: https://github.com/shiftkey/codeformatter/pull/1\r\n\r\nI disabled a couple of rules from the upstream codeformatter repo which go against our coding style:\r\n\r\n - new line above first `using` statement\r\n - specify the visibility of a field/member explicitly\r\n - private fields naming style\r\n\r\nThe net result is that things are actually pretty good, and we get some cleanup of whitespace for free.\r\n\r\n- [x] is the unicode (c) correct inside `AssemblyInfo.cs`?\r\n- ~~[x] update `AssemblyInfo.cs` contents~~ :boot:ed\r\n- [x] braces for empty constructors - what rule is this?\r\n- ~~[ ] remove copyright rename rule~~ :boot:ed\r\n- [x] wait for #799 to land and then rebase this \r\n- [x] docs docs docs\r\n- [x] hold for VS2015 support\r\n- [x] run over codebase once #808 is merged", 1333 | "closed_by": { 1334 | "login": "shiftkey", 1335 | "id": 359239, 1336 | "avatar_url": "https://avatars.githubusercontent.com/u/359239?v=3", 1337 | "gravatar_id": "", 1338 | "url": "https://api.github.com/users/shiftkey", 1339 | "html_url": "https://github.com/shiftkey", 1340 | "followers_url": "https://api.github.com/users/shiftkey/followers", 1341 | "following_url": "https://api.github.com/users/shiftkey/following{/other_user}", 1342 | "gists_url": "https://api.github.com/users/shiftkey/gists{/gist_id}", 1343 | "starred_url": "https://api.github.com/users/shiftkey/starred{/owner}{/repo}", 1344 | "subscriptions_url": "https://api.github.com/users/shiftkey/subscriptions", 1345 | "organizations_url": "https://api.github.com/users/shiftkey/orgs", 1346 | "repos_url": "https://api.github.com/users/shiftkey/repos", 1347 | "events_url": "https://api.github.com/users/shiftkey/events{/privacy}", 1348 | "received_events_url": "https://api.github.com/users/shiftkey/received_events", 1349 | "type": "User", 1350 | "site_admin": true 1351 | } 1352 | }, 1353 | "/repos/octokit/octokit.net/issues/935": { 1354 | "url": "https://api.github.com/repos/octokit/octokit.net/issues/935", 1355 | "labels_url": "https://api.github.com/repos/octokit/octokit.net/issues/935/labels{/name}", 1356 | "comments_url": "https://api.github.com/repos/octokit/octokit.net/issues/935/comments", 1357 | "events_url": "https://api.github.com/repos/octokit/octokit.net/issues/935/events", 1358 | "html_url": "https://github.com/octokit/octokit.net/pull/935", 1359 | "id": 110743482, 1360 | "number": 935, 1361 | "title": "added \"Merged\" in searchissues which allows search repos by merged da…", 1362 | "user": { 1363 | "login": "chenjiaming93", 1364 | "id": 13130367, 1365 | "avatar_url": "https://avatars.githubusercontent.com/u/13130367?v=3", 1366 | "gravatar_id": "", 1367 | "url": "https://api.github.com/users/chenjiaming93", 1368 | "html_url": "https://github.com/chenjiaming93", 1369 | "followers_url": "https://api.github.com/users/chenjiaming93/followers", 1370 | "following_url": "https://api.github.com/users/chenjiaming93/following{/other_user}", 1371 | "gists_url": "https://api.github.com/users/chenjiaming93/gists{/gist_id}", 1372 | "starred_url": "https://api.github.com/users/chenjiaming93/starred{/owner}{/repo}", 1373 | "subscriptions_url": "https://api.github.com/users/chenjiaming93/subscriptions", 1374 | "organizations_url": "https://api.github.com/users/chenjiaming93/orgs", 1375 | "repos_url": "https://api.github.com/users/chenjiaming93/repos", 1376 | "events_url": "https://api.github.com/users/chenjiaming93/events{/privacy}", 1377 | "received_events_url": "https://api.github.com/users/chenjiaming93/received_events", 1378 | "type": "User", 1379 | "site_admin": false 1380 | }, 1381 | "labels": [], 1382 | "state": "closed", 1383 | "locked": false, 1384 | "assignee": null, 1385 | "milestone": null, 1386 | "comments": 2, 1387 | "created_at": "2015-10-09T22:03:48Z", 1388 | "updated_at": "2015-11-03T19:52:27Z", 1389 | "closed_at": "2015-11-03T19:52:27Z", 1390 | "pull_request": { 1391 | "url": "https://api.github.com/repos/octokit/octokit.net/pulls/935", 1392 | "html_url": "https://github.com/octokit/octokit.net/pull/935", 1393 | "diff_url": "https://github.com/octokit/octokit.net/pull/935.diff", 1394 | "patch_url": "https://github.com/octokit/octokit.net/pull/935.patch" 1395 | }, 1396 | "body": "…te with existing syntax.\r\n\r\nit generates a CA1502 code excessive complexity warning and i suppressed it.\r\n#839 ", 1397 | "closed_by": { 1398 | "login": "shiftkey", 1399 | "id": 359239, 1400 | "avatar_url": "https://avatars.githubusercontent.com/u/359239?v=3", 1401 | "gravatar_id": "", 1402 | "url": "https://api.github.com/users/shiftkey", 1403 | "html_url": "https://github.com/shiftkey", 1404 | "followers_url": "https://api.github.com/users/shiftkey/followers", 1405 | "following_url": "https://api.github.com/users/shiftkey/following{/other_user}", 1406 | "gists_url": "https://api.github.com/users/shiftkey/gists{/gist_id}", 1407 | "starred_url": "https://api.github.com/users/shiftkey/starred{/owner}{/repo}", 1408 | "subscriptions_url": "https://api.github.com/users/shiftkey/subscriptions", 1409 | "organizations_url": "https://api.github.com/users/shiftkey/orgs", 1410 | "repos_url": "https://api.github.com/users/shiftkey/repos", 1411 | "events_url": "https://api.github.com/users/shiftkey/events{/privacy}", 1412 | "received_events_url": "https://api.github.com/users/shiftkey/received_events", 1413 | "type": "User", 1414 | "site_admin": true 1415 | } 1416 | }, 1417 | "/repos/thedillonb/TestTestTest/commits/cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66": { 1418 | "sha": "cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66", 1419 | "commit": { 1420 | "author": { 1421 | "name": "dillonb123", 1422 | "email": "dillonb123@users.noreply.github.com", 1423 | "date": "2015-11-08T01:48:22Z" 1424 | }, 1425 | "committer": { 1426 | "name": "dillonb123", 1427 | "email": "dillonb123@users.noreply.github.com", 1428 | "date": "2015-11-08T01:48:22Z" 1429 | }, 1430 | "message": "moo2\n\n@thedillonb", 1431 | "tree": { 1432 | "sha": "f504be00ba4aefb66ba0e6fa3a3f178ad8d654b1", 1433 | "url": "https://api.github.com/repos/thedillonb/TestTestTest/git/trees/f504be00ba4aefb66ba0e6fa3a3f178ad8d654b1" 1434 | }, 1435 | "url": "https://api.github.com/repos/thedillonb/TestTestTest/git/commits/cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66", 1436 | "comment_count": 0 1437 | }, 1438 | "url": "https://api.github.com/repos/thedillonb/TestTestTest/commits/cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66", 1439 | "html_url": "https://github.com/thedillonb/TestTestTest/commit/cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66", 1440 | "comments_url": "https://api.github.com/repos/thedillonb/TestTestTest/commits/cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66/comments", 1441 | "author": { 1442 | "login": "dillonb123", 1443 | "id": 2868513, 1444 | "avatar_url": "https://avatars.githubusercontent.com/u/2868513?v=3", 1445 | "gravatar_id": "", 1446 | "url": "https://api.github.com/users/dillonb123", 1447 | "html_url": "https://github.com/dillonb123", 1448 | "followers_url": "https://api.github.com/users/dillonb123/followers", 1449 | "following_url": "https://api.github.com/users/dillonb123/following{/other_user}", 1450 | "gists_url": "https://api.github.com/users/dillonb123/gists{/gist_id}", 1451 | "starred_url": "https://api.github.com/users/dillonb123/starred{/owner}{/repo}", 1452 | "subscriptions_url": "https://api.github.com/users/dillonb123/subscriptions", 1453 | "organizations_url": "https://api.github.com/users/dillonb123/orgs", 1454 | "repos_url": "https://api.github.com/users/dillonb123/repos", 1455 | "events_url": "https://api.github.com/users/dillonb123/events{/privacy}", 1456 | "received_events_url": "https://api.github.com/users/dillonb123/received_events", 1457 | "type": "User", 1458 | "site_admin": false 1459 | }, 1460 | "committer": { 1461 | "login": "dillonb123", 1462 | "id": 2868513, 1463 | "avatar_url": "https://avatars.githubusercontent.com/u/2868513?v=3", 1464 | "gravatar_id": "", 1465 | "url": "https://api.github.com/users/dillonb123", 1466 | "html_url": "https://github.com/dillonb123", 1467 | "followers_url": "https://api.github.com/users/dillonb123/followers", 1468 | "following_url": "https://api.github.com/users/dillonb123/following{/other_user}", 1469 | "gists_url": "https://api.github.com/users/dillonb123/gists{/gist_id}", 1470 | "starred_url": "https://api.github.com/users/dillonb123/starred{/owner}{/repo}", 1471 | "subscriptions_url": "https://api.github.com/users/dillonb123/subscriptions", 1472 | "organizations_url": "https://api.github.com/users/dillonb123/orgs", 1473 | "repos_url": "https://api.github.com/users/dillonb123/repos", 1474 | "events_url": "https://api.github.com/users/dillonb123/events{/privacy}", 1475 | "received_events_url": "https://api.github.com/users/dillonb123/received_events", 1476 | "type": "User", 1477 | "site_admin": false 1478 | }, 1479 | "parents": [ 1480 | { 1481 | "sha": "d32e41c23377d4795c1a931ef546980bf7d1f040", 1482 | "url": "https://api.github.com/repos/thedillonb/TestTestTest/commits/d32e41c23377d4795c1a931ef546980bf7d1f040", 1483 | "html_url": "https://github.com/thedillonb/TestTestTest/commit/d32e41c23377d4795c1a931ef546980bf7d1f040" 1484 | } 1485 | ], 1486 | "stats": { 1487 | "total": 1, 1488 | "additions": 1, 1489 | "deletions": 0 1490 | }, 1491 | "files": [ 1492 | { 1493 | "sha": "f2e24c937823f388b984a736324b171d55bf5051", 1494 | "filename": "Exceptions.cs", 1495 | "status": "modified", 1496 | "additions": 1, 1497 | "deletions": 0, 1498 | "changes": 1, 1499 | "blob_url": "https://github.com/thedillonb/TestTestTest/blob/cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66/Exceptions.cs", 1500 | "raw_url": "https://github.com/thedillonb/TestTestTest/raw/cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66/Exceptions.cs", 1501 | "contents_url": "https://api.github.com/repos/thedillonb/TestTestTest/contents/Exceptions.cs?ref=cd9fdc3eb74bfea3900913e9cf39ca9eb0ed1d66", 1502 | "patch": "@@ -7,6 +7,7 @@\n \n // Hello!\n // Dillon!\n+Fuidge\n namespace GitHubSharp\n {\n public class ForbiddenException : StatusCodeException" 1503 | } 1504 | ] 1505 | } 1506 | } 1507 | --------------------------------------------------------------------------------