added ghapi app
added GithubAPIMiddleware added ghapi.api.GitHub API calls can be done via request.github.get/get_iter or GitHub().get/get_iter.
This commit is contained in:
		
							parent
							
								
									f11032202f
								
							
						
					
					
						commit
						fdeab7bee0
					
				
					 5 changed files with 125 additions and 0 deletions
				
			
		
							
								
								
									
										1
									
								
								ghapi/__init__.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								ghapi/__init__.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | |||
| # -*- coding: utf-8 -*- | ||||
							
								
								
									
										49
									
								
								ghapi/api.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								ghapi/api.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import urlparse | ||||
| from django.http import QueryDict | ||||
| from ghapi.linkheader import parse_link_value | ||||
| import requests | ||||
| 
 | ||||
| def get_next_page(response): | ||||
|     link = response.headers.get('link', None) | ||||
|     if not link: | ||||
|         return None | ||||
|     output = parse_link_value(link) | ||||
|     for url, info in output.items(): | ||||
|         if info.get('rel', None) == 'next': | ||||
|             return QueryDict(urlparse.urlparse(url).query)['page'] | ||||
|     return None | ||||
| 
 | ||||
| class GitHub(object): | ||||
|     def __init__(self, token=None): | ||||
|         headers = {} | ||||
|         if token: | ||||
|             headers['Authorization'] = 'token %s' % token | ||||
|         self.session = requests.session(headers=headers) | ||||
| 
 | ||||
|     def get(self, path, params=None): | ||||
|         """ | ||||
|         Gets a resource, eg 'users/ojii'. | ||||
| 
 | ||||
|         Returns tuple (jsondata, response) | ||||
|         """ | ||||
|         if params is None: | ||||
|             params = {} | ||||
|         params['per_page'] = 100 | ||||
|         response = self.session.get('https://api.github.com/%s' % path, params=params) | ||||
|         response.raise_for_status() | ||||
|         return response.json, response | ||||
| 
 | ||||
|     def get_iter(self, path, params=None): | ||||
|         """ | ||||
|         Returns an iterator over a resource, eg 'repos/divio/django-cms/watchers' that automatically handles | ||||
|         pagination. | ||||
|         """ | ||||
|         data, response = self.get(path, params) | ||||
|         for thing in data: | ||||
|             yield thing | ||||
|         next_page = get_next_page(response) | ||||
|         if next_page: | ||||
|             params['page'] = next_page | ||||
|             for thing in self.get(path, params): | ||||
|                 yield thing | ||||
							
								
								
									
										54
									
								
								ghapi/linkheader.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								ghapi/linkheader.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,54 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import re | ||||
| 
 | ||||
| TOKEN = r'(?:[^\(\)<>@,;:\\"/\[\]\?={} \t]+?)' | ||||
| QUOTED_STRING = r'(?:"(?:\\"|[^"])*")' | ||||
| PARAMETER = r'(?:%(TOKEN)s(?:=(?:%(TOKEN)s|%(QUOTED_STRING)s))?)' % locals() | ||||
| LINK = r'<[^>]*>\s*(?:;\s*%(PARAMETER)s?\s*)*' % locals() | ||||
| COMMA = r'(?:\s*(?:,\s*)+)' | ||||
| LINK_SPLIT = r'%s(?=%s|\s*$)' % (LINK, COMMA) | ||||
| 
 | ||||
| def _unquotestring(instr): | ||||
|     if instr[0] == instr[-1] == '"': | ||||
|         instr = instr[1:-1] | ||||
|         instr = re.sub(r'\\(.)', r'\1', instr) | ||||
|     return instr | ||||
| def _splitstring(instr, item, split): | ||||
|     if not instr: | ||||
|         return [] | ||||
|     return [ h.strip() for h in re.findall(r'%s(?=%s|\s*$)' % (item, split), instr)] | ||||
| 
 | ||||
| link_splitter = re.compile(LINK_SPLIT) | ||||
| 
 | ||||
| def parse_link_value(instr): | ||||
|     """ | ||||
|     Given a link-value (i.e., after separating the header-value on commas),  | ||||
|     return a dictionary whose keys are link URLs and values are dictionaries | ||||
|     of the parameters for their associated links. | ||||
|      | ||||
|     Note that internationalised parameters (e.g., title*) are  | ||||
|     NOT percent-decoded. | ||||
|      | ||||
|     Also, only the last instance of a given parameter will be included. | ||||
|      | ||||
|     For example,  | ||||
|      | ||||
|     >>> parse_link_value('</foo>; rel="self"; title*=utf-8\'de\'letztes%20Kapitel') | ||||
|     {'/foo': {'title*': "utf-8'de'letztes%20Kapitel", 'rel': 'self'}} | ||||
|      | ||||
|     """ | ||||
|     out = {} | ||||
|     if not instr: | ||||
|         return out | ||||
|     for link in [h.strip() for h in link_splitter.findall(instr)]: | ||||
|         url, params = link.split(">", 1) | ||||
|         url = url[1:] | ||||
|         param_dict = {} | ||||
|         for param in _splitstring(params, PARAMETER, "\s*;\s*"): | ||||
|             try: | ||||
|                 a, v = param.split("=", 1) | ||||
|                 param_dict[a.lower()] = _unquotestring(v) | ||||
|             except ValueError: | ||||
|                 param_dict[param.lower()] = None | ||||
|         out[url] = param_dict | ||||
|     return out | ||||
							
								
								
									
										20
									
								
								ghapi/middlewares.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								ghapi/middlewares.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| from django.utils.functional import SimpleLazyObject | ||||
| from ghapi.api import GitHub | ||||
| from social_auth.db.django_models import UserSocialAuth | ||||
| 
 | ||||
| 
 | ||||
| def get_github(request): | ||||
|     if not request.user.is_authenticated(): | ||||
|         return GitHub() | ||||
|     try: | ||||
|         social = UserSocialAuth.objects.get(provider='github', user_id=request.user.pk) | ||||
|     except UserSocialAuth.DoesNotExist: | ||||
|         return GitHub() | ||||
|     token = social.tokens.get('access_token', None) | ||||
|     return GitHub(token) | ||||
| 
 | ||||
| 
 | ||||
| class GithubAPIMiddleware(object): | ||||
|     def process_request(self, request): | ||||
|         request.user = SimpleLazyObject(lambda  : get_github(request)) | ||||
|  | @ -41,6 +41,7 @@ MIDDLEWARE_CLASSES = [ | |||
|     'django.middleware.csrf.CsrfViewMiddleware', | ||||
|     'django.contrib.auth.middleware.AuthenticationMiddleware', | ||||
|     'django.contrib.messages.middleware.MessageMiddleware', | ||||
|     'ghapi.middlewares.GithubAPIMiddleware', | ||||
| ] | ||||
| 
 | ||||
| TEMPLATE_CONTEXT_PROCESSORS = [ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Jonas Obrist
						Jonas Obrist