Token refresh
This commit is contained in:
parent
97b4dac813
commit
4f2562cbc3
4 changed files with 118 additions and 54 deletions
33
feedler/migrations/0007_auto_20171013_1515.py
Normal file
33
feedler/migrations/0007_auto_20171013_1515.py
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.11.6 on 2017-10-13 13:15
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('feedler', '0006_auto_20171012_1458'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='feedlysettings',
|
||||||
|
old_name='feedly_stream',
|
||||||
|
new_name='streams',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='feedlysettings',
|
||||||
|
name='feedly_auth',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='feedlysettings',
|
||||||
|
name='feedly_pages',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='feedlysettings',
|
||||||
|
name='token',
|
||||||
|
field=models.CharField(blank=True, help_text='Access Token from developer.feedly.com', max_length=255),
|
||||||
|
),
|
||||||
|
]
|
|
@ -1,7 +1,5 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import requests, json, codecs
|
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -10,66 +8,20 @@ from django.dispatch import receiver
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
|
|
||||||
from wagtail.contrib.settings.models import BaseSetting, register_setting
|
from wagtail.contrib.settings.models import BaseSetting, register_setting
|
||||||
from wagtail.wagtailadmin import messages
|
|
||||||
|
|
||||||
from .models import Entry, Stream
|
from .models import Stream
|
||||||
import feedler.feedparser as feedparser
|
|
||||||
|
|
||||||
import logging
|
from feedler.refresh import refresh_streams
|
||||||
logger = logging.getLogger('feedler')
|
|
||||||
|
|
||||||
# Feedly integration module
|
|
||||||
|
|
||||||
@register_setting
|
@register_setting
|
||||||
class FeedlySettings(BaseSetting):
|
class FeedlySettings(BaseSetting):
|
||||||
feedly_auth = models.TextField(
|
streams = models.ManyToManyField(Stream,
|
||||||
help_text='Your developer authorization key', blank=True)
|
|
||||||
feedly_pages = models.IntegerField(
|
|
||||||
choices=(
|
|
||||||
(1, '2'),
|
|
||||||
(2, '5'),
|
|
||||||
(3, '10'),
|
|
||||||
(4, '50'),
|
|
||||||
), blank=True, null=True, editable=False,
|
|
||||||
help_text='How many pages to fetch?'
|
|
||||||
)
|
|
||||||
feedly_stream = models.ManyToManyField(Stream,
|
|
||||||
help_text='Which streams to update')
|
help_text='Which streams to update')
|
||||||
|
token = models.CharField(max_length=255, blank=True,
|
||||||
|
help_text='Access Token from developer.feedly.com')
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Feedly'
|
verbose_name = 'Feedly'
|
||||||
|
|
||||||
API_BASEURL = 'https://cloud.feedly.com/v3/streams/contents?streamId='
|
|
||||||
|
|
||||||
@receiver(pre_save, sender=FeedlySettings)
|
@receiver(pre_save, sender=FeedlySettings)
|
||||||
def handle_save_settings(sender, instance, *args, **kwargs):
|
def handle_save_settings(sender, instance, *args, **kwargs):
|
||||||
if instance.feedly_auth:
|
if instance.token: refresh_streams(instance)
|
||||||
streams = instance.feedly_stream.all()
|
|
||||||
for stream in streams:
|
|
||||||
# Start a request to download the feed
|
|
||||||
logger.info("Processing stream %s" % stream.title)
|
|
||||||
url = API_BASEURL + stream.ident
|
|
||||||
headers = {
|
|
||||||
'Authorization': 'OAuth ' + instance.feedly_auth
|
|
||||||
}
|
|
||||||
contents = requests.get(url, headers=headers).json()
|
|
||||||
if 'errorMessage' in contents:
|
|
||||||
# Usually this is a token expired
|
|
||||||
if 'token expired' in contents['errorMessage']:
|
|
||||||
# TODO: request new token
|
|
||||||
pass
|
|
||||||
logger.error(contents['errorMessage'])
|
|
||||||
messages.error(sender, "Failed to fetch items: %s" % contents['errorMessage'])
|
|
||||||
return
|
|
||||||
for raw_entry in contents['items']:
|
|
||||||
eid = raw_entry['id']
|
|
||||||
# Create or update data
|
|
||||||
try:
|
|
||||||
entry = Entry.objects.get(entry_id=eid)
|
|
||||||
logger.info("Updating entry '%s'" % eid)
|
|
||||||
except Entry.DoesNotExist:
|
|
||||||
logger.info("Adding entry '%s'" % eid)
|
|
||||||
entry = Entry()
|
|
||||||
# Parse the Feedly object
|
|
||||||
entry = feedparser.parse(entry, raw_entry, stream)
|
|
||||||
# Persist resulting object
|
|
||||||
entry.save()
|
|
||||||
|
|
73
feedler/refresh.py
Normal file
73
feedler/refresh.py
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import requests, json, codecs
|
||||||
|
|
||||||
|
import logging
|
||||||
|
logger = logging.getLogger('feedler')
|
||||||
|
|
||||||
|
from .models import Entry
|
||||||
|
from feedler import feedparser
|
||||||
|
|
||||||
|
API_BASEURL = 'https://cloud.feedly.com/v3/'
|
||||||
|
API_STREAMS = API_BASEURL + 'streams/contents?streamId='
|
||||||
|
API_TOKENS = API_BASEURL + 'auth/token'
|
||||||
|
|
||||||
|
def refresh_streams(settings):
|
||||||
|
# Iterate through all saved streams
|
||||||
|
logger.warn("Refreshing all streams")
|
||||||
|
for stream in settings.streams.all():
|
||||||
|
if not refresh_stream(stream, settings):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_headers(settings):
|
||||||
|
return {
|
||||||
|
'Authorization': 'OAuth ' + settings.token
|
||||||
|
}
|
||||||
|
|
||||||
|
def refresh_token(settings):
|
||||||
|
# Request a new token
|
||||||
|
url = API_TOKENS
|
||||||
|
logger.warn("Refreshing Feedly access token")
|
||||||
|
payload = {
|
||||||
|
'refresh_token': settings.token,
|
||||||
|
'client_id': 'feedlydev',
|
||||||
|
'client_secret': 'feedlydev',
|
||||||
|
'grant_type': 'refresh_token'
|
||||||
|
}
|
||||||
|
contents = requests.get(url, data=payload, headers=get_headers(settings)).json()
|
||||||
|
if not 'access_token' in contents or not contents['access_token']:
|
||||||
|
logger.error("Access token could not be refreshed.")
|
||||||
|
return False
|
||||||
|
settings.token = contents['access_token']
|
||||||
|
settings.save()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def refresh_stream(stream, settings, retry=False):
|
||||||
|
# Start a request to download the feed for a particular stream
|
||||||
|
logger.warn("Processing stream %s" % stream.title)
|
||||||
|
url = API_STREAMS + stream.ident
|
||||||
|
contents = requests.get(url, headers=get_headers(settings)).json()
|
||||||
|
if 'errorMessage' in contents:
|
||||||
|
# Usually this is a token expired
|
||||||
|
if 'token expired' in contents['errorMessage'] or 'unauthorized' in contents['errorMessage']:
|
||||||
|
if not refresh_token(settings): return False
|
||||||
|
# Make another attempt
|
||||||
|
if retry or not refresh_stream(stream, settings, True):
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
logger.error(contents['errorMessage'])
|
||||||
|
return False
|
||||||
|
for raw_entry in contents['items']:
|
||||||
|
eid = raw_entry['id']
|
||||||
|
# Create or update data
|
||||||
|
try:
|
||||||
|
entry = Entry.objects.get(entry_id=eid)
|
||||||
|
logger.info("Updating entry '%s'" % eid)
|
||||||
|
except Entry.DoesNotExist:
|
||||||
|
logger.info("Adding entry '%s'" % eid)
|
||||||
|
entry = Entry()
|
||||||
|
# Parse the Feedly object
|
||||||
|
entry = feedparser.parse(entry, raw_entry, stream)
|
||||||
|
# Persist resulting object
|
||||||
|
entry.save()
|
|
@ -156,6 +156,12 @@ LOGGING = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'loggers': {
|
'loggers': {
|
||||||
|
'feedler': {
|
||||||
|
'handlers': [],
|
||||||
|
'level': 'INFO',
|
||||||
|
'propagate': False,
|
||||||
|
'formatter': 'verbose',
|
||||||
|
},
|
||||||
'publichealth': {
|
'publichealth': {
|
||||||
'handlers': [],
|
'handlers': [],
|
||||||
'level': 'INFO',
|
'level': 'INFO',
|
||||||
|
|
Loading…
Reference in a new issue