Some code simplifications & logging for Zencoder notifications.

This commit is contained in:
Sybren A. Stüvel 2016-03-25 17:21:18 +01:00
parent d7ee2121d9
commit fd5bcaec52
2 changed files with 102 additions and 45 deletions

View File

@ -1,58 +1,65 @@
import logging
from bson import ObjectId from bson import ObjectId
from eve.methods.put import put_internal from eve.methods.put import put_internal
from flask import Blueprint from flask import Blueprint
from flask import abort from flask import abort
from flask import request from flask import request
from application import app from application import app
from application import utils
encoding = Blueprint('encoding', __name__) encoding = Blueprint('encoding', __name__)
log = logging.getLogger(__name__)
@encoding.route('/zencoder/notifications', methods=['POST']) @encoding.route('/zencoder/notifications', methods=['POST'])
def zencoder_notifications(): def zencoder_notifications():
if app.config['ENCODING_BACKEND'] == 'zencoder': if app.config['ENCODING_BACKEND'] != 'zencoder':
if not app.config['DEBUG']: log.warning('Received notification from Zencoder but app not configured for Zencoder.')
# If we are in production, look for the Zencoder header secret
try:
notification_secret_request = request.headers[
'X-Zencoder-Notification-Secret']
except KeyError:
return abort(401)
# If the header is found, check it agains the one in the config
notification_secret = app.config['ZENCODER_NOTIFICATIONS_SECRET']
if notification_secret_request != notification_secret:
return abort(401)
# Cast request data into a dict
data = request.get_json()
files_collection = app.data.driver.db['files']
# Find the file object based on processing backend and job_id
lookup = {'processing.backend': 'zencoder', 'processing.job_id': str(
data['job']['id'])}
f = files_collection.find_one(lookup)
if f:
file_id = f['_id']
# Remove internal keys (so that we can run put internal)
internal_fields = ['_id', '_etag', '_updated', '_created', '_status']
for field in internal_fields:
f.pop(field, None)
# Update processing status
f['processing']['status'] = data['job']['state']
# For every variation encoded, try to update the file object
for output in data['outputs']:
format = output['format']
# Change the zencoder 'mpeg4' format to 'mp4' used internally
format = 'mp4' if format == 'mpeg4' else format
# Find a variation matching format and resolution
variation = next((v for v in f['variations'] if v['format'] == format \
and v['width'] == output['width']), None)
# If found, update with delivered file size
# TODO: calculate md5 on the storage
if variation:
variation['length'] = output['file_size_in_bytes']
r = put_internal('files', f, **{'_id': ObjectId(file_id)})
return ''
else:
return abort(404)
else:
return abort(403) return abort(403)
if not app.config['DEBUG']:
# If we are in production, look for the Zencoder header secret
try:
notification_secret_request = request.headers[
'X-Zencoder-Notification-Secret']
except KeyError:
log.warning('Received Zencoder notification without secret.')
return abort(401)
# If the header is found, check it agains the one in the config
notification_secret = app.config['ZENCODER_NOTIFICATIONS_SECRET']
if notification_secret_request != notification_secret:
log.warning('Received Zencoder notification with incorrect secret.')
return abort(401)
# Cast request data into a dict
data = request.get_json()
files_collection = app.data.driver.db['files']
# Find the file object based on processing backend and job_id
lookup = {'processing.backend': 'zencoder', 'processing.job_id': str(data['job']['id'])}
f = files_collection.find_one(lookup)
if not f:
log.warning('Unknown Zencoder job id %r', data['job']['id'])
return abort(404)
file_id = f['_id']
# Remove internal keys (so that we can run put internal)
f = utils.remove_private_keys(f)
# Update processing status
f['processing']['status'] = data['job']['state']
# For every variation encoded, try to update the file object
for output in data['outputs']:
format = output['format']
# Change the zencoder 'mpeg4' format to 'mp4' used internally
format = 'mp4' if format == 'mpeg4' else format
# Find a variation matching format and resolution
variation = next((v for v in f['variations'] if v['format'] == format \
and v['width'] == output['width']), None)
# If found, update with delivered file size
# TODO: calculate md5 on the storage
if variation:
variation['length'] = output['file_size_in_bytes']
put_internal('files', f, _id=ObjectId(file_id))
return ''

50
tests/test_encoding.py Normal file
View File

@ -0,0 +1,50 @@
"""Test cases for the zencoder notifications."""
import json
from common_test_class import AbstractPillarTest
class ZencoderNotificationTest(AbstractPillarTest):
def test_missing_secret(self):
with self.app.test_request_context():
resp = self.client.post('/encoding/zencoder/notifications')
self.assertEqual(401, resp.status_code)
def test_wrong_secret(self):
with self.app.test_request_context():
resp = self.client.post('/encoding/zencoder/notifications',
headers={'X-Zencoder-Notification-Secret': 'koro'})
self.assertEqual(401, resp.status_code)
def test_good_secret_missing_file(self):
with self.app.test_request_context():
secret = self.app.config['ZENCODER_NOTIFICATIONS_SECRET']
resp = self.client.post('/encoding/zencoder/notifications',
data=json.dumps({'job': {'id': 'koro-007'}}),
headers={'X-Zencoder-Notification-Secret': secret,
'Content-Type': 'application/json'})
self.assertEqual(404, resp.status_code)
def test_good_secret_existing_file(self):
self.ensure_file_exists(file_overrides={
'processing': {'backend': 'zencoder',
'job_id': 'koro-007',
'status': 'processing'}
})
with self.app.test_request_context():
secret = self.app.config['ZENCODER_NOTIFICATIONS_SECRET']
resp = self.client.post('/encoding/zencoder/notifications',
data=json.dumps({'job': {'id': 'koro-007',
'state': 'done'},
'outputs': [{
'format': 'jpg',
'width': 2048,
'file_size_in_bytes': 15,
}]}),
headers={'X-Zencoder-Notification-Secret': secret,
'Content-Type': 'application/json'})
# TODO: check that the file in MongoDB is actually updated properly.
self.assertEqual(200, resp.status_code)