diff --git a/README.md b/README.md index 77b4fef..f6f4ef3 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,22 @@ Combines data from ZenHub.io and GitHub to make some interesting charts. 1. Create a new application in Google App Engine 2. Copy the application ID into `.gaeid` +3. Create a `config.yaml` as below. 3. From the root folder run `dev/update` + +### `config.yaml` + +``` +# Github application credentials +github: + id: + secret: + +# URL of the appspot host +host: .appspot.com + +# An app-specific secret session key +webapp2_extras.sessions: + secret_key: +``` + diff --git a/github.py b/github.py index ef5b4c3..d37513b 100644 --- a/github.py +++ b/github.py @@ -1,15 +1,12 @@ -import webapp2 -import sys -import urllib, urlparse -from urllib2 import Request, urlopen, URLError, HTTPError +import webapp2, sys, urllib, urlparse, random import base64, json, logging, datetime, time +from urllib2 import Request, urlopen, HTTPError from google.appengine.ext import deferred from google.appengine.ext import vendor vendor.add('lib') # uritemplate is vendored import uritemplate -import random from model import Issue, Repo -from config import config +import zenhub githubUrl = 'https://api.github.com' @@ -92,13 +89,13 @@ def syncIssues(self, repoKey): issue = Issue(repo = repo.key, number = number) issue.github = self.issue(repo, number, issue.github) issue.githubUpdate = updateTime - issue.zenhubUpdate = None # github update means zenhub update + issue.zenhubUpdate = None # mark for Zenhub update logging.info("Upserting issue %s" % issue.number) issue.upsert() + + # After a github sync, start a zenhub sync + zenhub.syncIssues(repo.key) - #TODO: Go get zenhub updates (separately so it can be metered safely)? - #getZenhubIssues(repo.key, keysToRefresh) - def newIssueNumbers(self, repo, until): """Return issues that changed after issueTime, along with most recent update time""" numbers = set() diff --git a/html/base.html b/html/base.html index 1521ec6..b147bf0 100644 --- a/html/base.html +++ b/html/base.html @@ -4,8 +4,8 @@ {% if user %}

Welcome {{ user.name }} (Logout)

{% else %} -
-
Github Personal Access Token:
+ +
{% endif %} diff --git a/zenchart.py b/zenchart.py index 05b8a20..2f40d45 100755 --- a/zenchart.py +++ b/zenchart.py @@ -1,7 +1,7 @@ #!/usr/bin/python -import os +import os, random import webapp2, jinja2, logging -import urllib, cgi, json +import urllib, cgi, json, urllib2, urlparse from google.appengine.ext import ndb from google.appengine.api import taskqueue import github @@ -53,7 +53,9 @@ def session(self): class MainPage(BaseHandler): def get(self): - user = self.user() + user = None + if 'user' in self.session: + user = self.session['user'] # List repositories that we know about if user: @@ -79,9 +81,9 @@ def post(self): repo.name = self.request.get('repo') repo.auth = Auth(zenhubToken = self.request.get('zenhubToken')) repo.data = Github(user).repo(repo.name) + logging.info("Adding repo %s" % repo) repo.put() - logging.info("Putting repo %s" % repo) - # TODO: defer get all issues for this repo + deferred.defer(github.syncIssues, user, repo.key) self.redirect(repo.url()) class RepoPage(BaseHandler): @@ -92,10 +94,6 @@ def get(self, id): logging.info("Deferring github repo issue sync") deferred.defer(github.syncIssues, user, repo.key) self.redirect(repo.url()) - elif self.request.get('z'): - logging.info("Deferring zenhub repo issue sync") - deferred.defer(zenhub.syncIssues, repo.key) - self.redirect(repo.url()) elif self.request.get('f'): deferred.defer(github.getAllIssues, user, repo.key) self.redirect(repo.url()) @@ -134,8 +132,6 @@ def post(self): repo.name = name repo.auth = Auth() repo.auth.zenhubToken = self.request.get('zenhubToken') - repo.auth.githubToken = self.request.get('githubToken') - repo.auth.githubUser = self.request.get('githubUser') repo.put() deferred.defer(github.getAllIssues, repo) self.redirect(repo.url()) @@ -174,10 +170,20 @@ def post(self, id): class LoginPage(BaseHandler): def get(self): self.session['return'] = self.request.get('r') - # TODO: If user is missing we should allow redirection back to the current route - url = github.authUrl(self.session) - logging.info('Redirecting to %s to get user info' % url) - self.redirect(url) + + if self.request.get('paToken'): + user = session.user() + self.redirect('/') + else: + # Github login + state = '%030x' % random.randrange(16**30) + url = 'https://github.com/login/oauth/authorize?%s' % urllib.urlencode({ + 'client_id': config['github']['id'], + 'scope': 'repo', + 'state': state}) + self.session['state'] = state + logging.info('Redirecting to %s to get user info' % url) + self.redirect(url) class LogoutPage(BaseHandler): def get(self): @@ -186,16 +192,29 @@ def get(self): class GithubAuthPage(BaseHandler): def get(self): - # TODO: Verify the 'state' code = self.request.GET['code'] state = self.request.GET['state'] - github.getAccessToken(self.session, code, state) - if 'return' in self.session: - returnTo = self.session['return'] - del session['return'] + if state != self.session['state']: + self.response.set_status(403) else: - returnTo = '/' - self.redirect(returnTo) + request = urllib2.Request('https://github.com/login/oauth/access_token', urllib.urlencode({ + 'client_id': config['github']['id'], + 'client_secret': config['github']['secret'], + 'code': code + })) + data = urlparse.parse_qs(urllib2.urlopen(request).read()) + logging.info('result is %s' % data) + aToken = data['access_token'][0] + user = Github({'aToken': aToken}).user() + user['aToken'] = aToken + self.session['user'] = user + if 'return' in self.session and self.session['return']: + returnTo = self.session['return'] + logging.info("Login complete for user %s, returning to %s" % (json.dumps(user), returnTo)) + del self.session['return'] + else: + returnTo = '/' + self.redirect(returnTo) app = webapp2.WSGIApplication([ ('/', MainPage), diff --git a/zenhub.py b/zenhub.py index 242dc79..896abd6 100644 --- a/zenhub.py +++ b/zenhub.py @@ -1,5 +1,7 @@ from urllib2 import Request, urlopen, HTTPError import json, datetime, logging +from google.appengine.ext import deferred +from model import Repo zenhubUrl = 'https://api.zenhub.io/p1'