| |
@@ -22,86 +22,36 @@
|
| |
'''
|
| |
Top level of the mdapi aiohttp application.
|
| |
'''
|
| |
- import functools
|
| |
- import json
|
| |
- import logging
|
| |
import os
|
| |
+ import logging
|
| |
|
| |
- import asyncio
|
| |
- import werkzeug
|
| |
- from aiohttp import web
|
| |
+ from flask import Flask, jsonify, send_from_directory, abort
|
| |
|
| |
import mdapi.lib as mdapilib
|
| |
|
| |
|
| |
- CONFIG = dict()
|
| |
- obj = werkzeug.import_string('mdapi.default_config')
|
| |
- for key in dir(obj):
|
| |
- if key.isupper():
|
| |
- CONFIG[key] = getattr(obj, key)
|
| |
-
|
| |
-
|
| |
- if 'MDAPI_CONFIG' in os.environ and os.path.exists(os.environ['MDAPI_CONFIG']):
|
| |
- with open(os.environ['MDAPI_CONFIG']) as config_file:
|
| |
- exec(compile(
|
| |
- config_file.read(), os.environ['MDAPI_CONFIG'], 'exec'), CONFIG)
|
| |
-
|
| |
- indexfile = os.path.join(
|
| |
- os.path.dirname(os.path.abspath(__file__)), 'index.html')
|
| |
- INDEX = ''
|
| |
- with open(indexfile) as stream:
|
| |
- INDEX = stream.read()
|
| |
- INDEX = INDEX.replace('$PREFIX', CONFIG.get('PREFIX', ''))
|
| |
-
|
| |
-
|
| |
- _log = logging.getLogger(__name__)
|
| |
-
|
| |
-
|
| |
- def allows_jsonp(function):
|
| |
- ''' Add support for JSONP queries to the endpoint decorated. '''
|
| |
+ app = Flask(__name__)
|
| |
+ app.config["DB_FOLDER"] = "/var/tmp"
|
| |
|
| |
- @functools.wraps(function)
|
| |
- def wrapper(request, *args, **kwargs):
|
| |
- ''' Actually does the job with the arguments provided.
|
| |
+ gunicorn_logger = logging.getLogger("gunicorn.error")
|
| |
+ app.logger.handlers = gunicorn_logger.handlers
|
| |
+ app.logger.setLevel(gunicorn_logger.level)
|
| |
|
| |
- :arg request: the request that was called that we want to add JSONP
|
| |
- support to
|
| |
- :type request: aiohttp.web_request.Request
|
| |
|
| |
- '''
|
| |
- response = yield from function(request, *args, **kwargs)
|
| |
- url_arg = request.query
|
| |
- callback = url_arg.get('callback')
|
| |
- if callback and request.method == 'GET':
|
| |
- if isinstance(callback, list):
|
| |
- callback = callback[0]
|
| |
- response.mimetype = 'application/javascript'
|
| |
- response.content_type = 'application/javascript'
|
| |
- response.text = '%s(%s);' % (callback, response.text)
|
| |
-
|
| |
- return response
|
| |
-
|
| |
- return wrapper
|
| |
-
|
| |
-
|
| |
- @asyncio.coroutine
|
| |
def _get_pkg(branch, name=None, action=None, srcname=None):
|
| |
''' Return the pkg information for the given package in the specified
|
| |
branch or raise an aiohttp exception.
|
| |
'''
|
| |
- if (not name and not srcname) or (name and srcname):
|
| |
- raise web.HTTPBadRequest()
|
| |
-
|
| |
pkg = None
|
| |
wrongdb = False
|
| |
for repotype in ['updates-testing', 'updates', 'testing', None]:
|
| |
|
| |
if repotype:
|
| |
dbfile = '%s/mdapi-%s-%s-primary.sqlite' % (
|
| |
- CONFIG['DB_FOLDER'], branch, repotype)
|
| |
+ app.config['DB_FOLDER'], branch, repotype)
|
| |
else:
|
| |
dbfile = '%s/mdapi-%s-primary.sqlite' % (
|
| |
- CONFIG['DB_FOLDER'], branch)
|
| |
+ app.config['DB_FOLDER'], branch)
|
| |
|
| |
if not os.path.exists(dbfile):
|
| |
wrongdb = True
|
| |
@@ -109,41 +59,29 @@
|
| |
|
| |
wrongdb = False
|
| |
|
| |
- session = yield from mdapilib.create_session(
|
| |
+ session = mdapilib.create_session(
|
| |
'sqlite:///%s' % dbfile)
|
| |
if name:
|
| |
if action:
|
| |
- pkg = yield from mdapilib.get_package_by(
|
| |
+ pkg = mdapilib.get_package_by(
|
| |
session, action, name)
|
| |
else:
|
| |
- pkg = yield from mdapilib.get_package(session, name)
|
| |
+ pkg = mdapilib.get_package(session, name)
|
| |
elif srcname:
|
| |
- pkg = yield from mdapilib.get_package_by_src(session, srcname)
|
| |
+ pkg = mdapilib.get_package_by_src(session, srcname)
|
| |
session.close()
|
| |
if pkg:
|
| |
break
|
| |
|
| |
if wrongdb:
|
| |
- raise web.HTTPBadRequest()
|
| |
+ abort(400)
|
| |
|
| |
if not pkg:
|
| |
- raise web.HTTPNotFound()
|
| |
+ abort(404)
|
| |
|
| |
return (pkg, repotype)
|
| |
|
| |
|
| |
- def _get_pretty(request):
|
| |
- pretty = False
|
| |
- params = request.query
|
| |
- if params.get('pretty') in ['1', 'true']:
|
| |
- pretty = True
|
| |
- # Assume pretty if html is requested and pretty is not disabled
|
| |
- elif 'text/html' in request.headers.get('ACCEPT', ''):
|
| |
- pretty = True
|
| |
- return pretty
|
| |
-
|
| |
-
|
| |
- @asyncio.coroutine
|
| |
def _expand_pkg_info(pkgs, branch, repotype=None):
|
| |
''' Return a JSON blob containing all the information we want to return
|
| |
for the provided package or packages.
|
| |
@@ -156,15 +94,15 @@
|
| |
for pkg in pkgs:
|
| |
out = pkg.to_json()
|
| |
dbfile = '%s/mdapi-%s%s-primary.sqlite' % (
|
| |
- CONFIG['DB_FOLDER'], branch, '-%s' % repotype if repotype else '')
|
| |
+ app.config['DB_FOLDER'], branch, '-%s' % repotype if repotype else '')
|
| |
|
| |
- session = yield from mdapilib.create_session(
|
| |
+ session = mdapilib.create_session(
|
| |
'sqlite:///%s' % dbfile)
|
| |
# Fill in some extra info
|
| |
|
| |
# Basic infos, always present regardless of the version of the repo
|
| |
for datatype in ['conflicts', 'obsoletes', 'provides', 'requires']:
|
| |
- data = yield from mdapilib.get_package_info(
|
| |
+ data = mdapilib.get_package_info(
|
| |
session, pkg.pkgKey, datatype.capitalize())
|
| |
if data:
|
| |
out[datatype] = [item.to_json() for item in data]
|
| |
@@ -174,7 +112,7 @@
|
| |
# New meta-data present for soft dependency management in RPM
|
| |
for datatype in [
|
| |
'enhances', 'recommends', 'suggests', 'supplements']:
|
| |
- data = yield from mdapilib.get_package_info(
|
| |
+ data = mdapilib.get_package_info(
|
| |
session, pkg.pkgKey, datatype.capitalize())
|
| |
if data:
|
| |
out[datatype] = [item.to_json() for item in data]
|
| |
@@ -183,7 +121,7 @@
|
| |
|
| |
# Add the list of packages built from the same src.rpm
|
| |
if pkg.rpm_sourcerpm:
|
| |
- copkgs = yield from mdapilib.get_co_packages(
|
| |
+ copkgs = mdapilib.get_co_packages(
|
| |
session, pkg.rpm_sourcerpm)
|
| |
out['co-packages'] = list(set([
|
| |
cpkg.name for cpkg in copkgs
|
| |
@@ -199,248 +137,129 @@
|
| |
return output
|
| |
|
| |
|
| |
- @asyncio.coroutine
|
| |
- @allows_jsonp
|
| |
- def get_pkg(request):
|
| |
- _log.info('get_pkg %s', request)
|
| |
- branch = request.match_info.get('branch')
|
| |
- pretty = _get_pretty(request)
|
| |
- name = request.match_info.get('name')
|
| |
- pkg, repotype = yield from _get_pkg(branch, name)
|
| |
-
|
| |
- output = yield from _expand_pkg_info(pkg, branch, repotype)
|
| |
+ @app.route("/<branch>/pkg/<name>")
|
| |
+ def get_pkg(branch, name):
|
| |
+ pkg, repotype = _get_pkg(branch, name)
|
| |
+ output = _expand_pkg_info(pkg, branch, repotype)
|
| |
+ return jsonify(output)
|
| |
|
| |
- args = {}
|
| |
- if pretty:
|
| |
- args = dict(sort_keys=True, indent=4, separators=(',', ': '))
|
| |
|
| |
- output = web.Response(
|
| |
- body=json.dumps(output, **args).encode('utf-8'),
|
| |
- content_type='application/json')
|
| |
- return output
|
| |
+ @app.route("/<branch>/srcpkg/<name>")
|
| |
+ def get_src_pkg(branch, name):
|
| |
+ pkg, repotype = _get_pkg(branch, srcname=name)
|
| |
+ output = _expand_pkg_info(pkg, branch, repotype)
|
| |
|
| |
+ return jsonify(output)
|
| |
|
| |
- @asyncio.coroutine
|
| |
- @allows_jsonp
|
| |
- def get_src_pkg(request):
|
| |
- _log.info('get_src_pkg %s', request)
|
| |
- branch = request.match_info.get('branch')
|
| |
- pretty = _get_pretty(request)
|
| |
- name = request.match_info.get('name')
|
| |
- pkg, repotype = yield from _get_pkg(branch, srcname=name)
|
| |
|
| |
- output = yield from _expand_pkg_info(pkg, branch, repotype)
|
| |
-
|
| |
- args = {}
|
| |
- if pretty:
|
| |
- args = dict(sort_keys=True, indent=4, separators=(',', ': '))
|
| |
-
|
| |
- return web.Response(
|
| |
- body=json.dumps(output, **args).encode('utf-8'),
|
| |
- content_type='application/json')
|
| |
-
|
| |
-
|
| |
- @asyncio.coroutine
|
| |
- @allows_jsonp
|
| |
- def get_pkg_files(request):
|
| |
- _log.info('get_pkg_files %s', request)
|
| |
- branch = request.match_info.get('branch')
|
| |
- name = request.match_info.get('name')
|
| |
- pretty = _get_pretty(request)
|
| |
- pkg, repotype = yield from _get_pkg(branch, name)
|
| |
+ @app.route("/<branch>/files/<name>")
|
| |
+ def get_pkg_files(branch, name):
|
| |
+ pkg, repotype = _get_pkg(branch, name)
|
| |
|
| |
dbfile = '%s/mdapi-%s%s-filelists.sqlite' % (
|
| |
- CONFIG['DB_FOLDER'], branch, '-%s' % repotype if repotype else '')
|
| |
+ app.config['DB_FOLDER'], branch, '-%s' % repotype if repotype else '')
|
| |
if not os.path.exists(dbfile):
|
| |
- raise web.HTTPBadRequest()
|
| |
+ abort(400)
|
| |
|
| |
- session2 = yield from mdapilib.create_session(
|
| |
+ session2 = mdapilib.create_session(
|
| |
'sqlite:///%s' % dbfile)
|
| |
- filelist = yield from mdapilib.get_files(session2, pkg.pkgId)
|
| |
+ filelist = mdapilib.get_files(session2, pkg.pkgId)
|
| |
session2.close()
|
| |
|
| |
output = {
|
| |
'files': [fileinfo.to_json() for fileinfo in filelist],
|
| |
'repo': repotype if repotype else 'release',
|
| |
}
|
| |
- args = {}
|
| |
- if pretty:
|
| |
- args = dict(sort_keys=True, indent=4, separators=(',', ': '))
|
| |
-
|
| |
- return web.Response(
|
| |
- body=json.dumps(output, **args).encode('utf-8'),
|
| |
- content_type='application/json')
|
| |
+ return jsonify(output)
|
| |
|
| |
|
| |
- @asyncio.coroutine
|
| |
- @allows_jsonp
|
| |
- def get_pkg_changelog(request):
|
| |
- _log.info('get_pkg_changelog %s', request)
|
| |
- branch = request.match_info.get('branch')
|
| |
- name = request.match_info.get('name')
|
| |
- pretty = _get_pretty(request)
|
| |
- pkg, repotype = yield from _get_pkg(branch, name)
|
| |
+ @app.route("/<branch>/changelog/<name>")
|
| |
+ def get_pkg_changelog(branch, name):
|
| |
+ pkg, repotype = _get_pkg(branch, name)
|
| |
|
| |
dbfile = '%s/mdapi-%s%s-other.sqlite' % (
|
| |
- CONFIG['DB_FOLDER'], branch, '-%s' % repotype if repotype else '')
|
| |
+ app.config['DB_FOLDER'], branch, '-%s' % repotype if repotype else '')
|
| |
if not os.path.exists(dbfile):
|
| |
- raise web.HTTPBadRequest()
|
| |
+ abort(400)
|
| |
|
| |
- session2 = yield from mdapilib.create_session(
|
| |
+ session2 = mdapilib.create_session(
|
| |
'sqlite:///%s' % dbfile)
|
| |
- changelogs = yield from mdapilib.get_changelog(session2, pkg.pkgId)
|
| |
+ changelogs = mdapilib.get_changelog(session2, pkg.pkgId)
|
| |
session2.close()
|
| |
|
| |
output = {
|
| |
'changelogs': [changelog.to_json() for changelog in changelogs],
|
| |
'repo': repotype if repotype else 'release',
|
| |
}
|
| |
- args = {}
|
| |
- if pretty:
|
| |
- args = dict(sort_keys=True, indent=4, separators=(',', ': '))
|
| |
|
| |
- return web.Response(
|
| |
- body=json.dumps(output, **args).encode('utf-8'),
|
| |
- content_type='application/json')
|
| |
+ return jsonify(output)
|
| |
|
| |
|
| |
- @asyncio.coroutine
|
| |
- def list_branches(request):
|
| |
+ @app.route("/branches")
|
| |
+ def list_branches():
|
| |
''' Return the list of all branches currently supported by mdapi
|
| |
'''
|
| |
- _log.info('list_branches: %s', request)
|
| |
- pretty = _get_pretty(request)
|
| |
output = sorted(list(set([
|
| |
# Remove the front part `mdapi-` and the end part -<type>.sqlite
|
| |
filename.replace('mdapi-', '').rsplit('-', 2)[0].replace(
|
| |
'-updates', '')
|
| |
- for filename in os.listdir(CONFIG['DB_FOLDER'])
|
| |
+ for filename in os.listdir(app.config['DB_FOLDER'])
|
| |
if filename.startswith('mdapi') and filename.endswith('.sqlite')
|
| |
])))
|
| |
|
| |
- args = {}
|
| |
- if pretty:
|
| |
- args = dict(sort_keys=True, indent=4, separators=(',', ': '))
|
| |
+ return jsonify(output)
|
| |
|
| |
- response = web.Response(body=json.dumps(output, **args).encode('utf-8'),
|
| |
- content_type='application/json')
|
| |
|
| |
- # The decorator doesn't work for this endpoint, so do it manually here
|
| |
- # I am not really sure what doesn't work but it seems this endpoint is
|
| |
- # returning an object instead of the expected generator despite it being
|
| |
- # flagged as an asyncio coroutine
|
| |
- url_arg = request.query
|
| |
- callback = url_arg.get('callback')
|
| |
- if callback and request.method == 'GET':
|
| |
- if isinstance(callback, list):
|
| |
- callback = callback[0]
|
| |
- response.mimetype = 'application/javascript'
|
| |
- response.content_type = 'application/javascript'
|
| |
- response.text = '%s(%s);' % (callback, response.text)
|
| |
-
|
| |
- return response
|
| |
-
|
| |
-
|
| |
- @asyncio.coroutine
|
| |
- @allows_jsonp
|
| |
- def process_dep(request, action):
|
| |
+ def process_dep(branch, name, action):
|
| |
''' Return the information about the packages having the specified
|
| |
action (provides, requires, obsoletes...)
|
| |
'''
|
| |
- _log.info('process_dep %s: %s', action, request)
|
| |
- branch = request.match_info.get('branch')
|
| |
- pretty = _get_pretty(request)
|
| |
- name = request.match_info.get('name')
|
| |
-
|
| |
- try:
|
| |
- pkg, repotype = yield from _get_pkg(branch, name, action=action)
|
| |
- except:
|
| |
- raise web.HTTPBadRequest()
|
| |
-
|
| |
- output = yield from _expand_pkg_info(pkg, branch, repotype)
|
| |
-
|
| |
- args = {}
|
| |
- if pretty:
|
| |
- args = dict(sort_keys=True, indent=4, separators=(',', ': '))
|
| |
-
|
| |
- return web.Response(body=json.dumps(output, **args).encode('utf-8'),
|
| |
- content_type='application/json')
|
| |
-
|
| |
-
|
| |
- @asyncio.coroutine
|
| |
- def get_provides(request):
|
| |
- return process_dep(request, 'provides')
|
| |
-
|
| |
-
|
| |
- @asyncio.coroutine
|
| |
- def get_requires(request):
|
| |
- return process_dep(request, 'requires')
|
| |
-
|
| |
+ pkg, repotype = _get_pkg(branch, name, action=action)
|
| |
+ output = _expand_pkg_info(pkg, branch, repotype)
|
| |
|
| |
- @asyncio.coroutine
|
| |
- def get_obsoletes(request):
|
| |
- return process_dep(request, 'obsoletes')
|
| |
+ return jsonify(output)
|
| |
|
| |
|
| |
- @asyncio.coroutine
|
| |
- def get_conflicts(request):
|
| |
- return process_dep(request, 'conflicts')
|
| |
+ @app.route("/<branch>/provides/<name>")
|
| |
+ def get_provides(branch, name):
|
| |
+ return process_dep(branch, name, 'provides')
|
| |
|
| |
|
| |
- @asyncio.coroutine
|
| |
- def get_enhances(request):
|
| |
- return process_dep(request, 'enhances')
|
| |
+ @app.route("/<branch>/requires/<name>")
|
| |
+ def get_requires(branch, name):
|
| |
+ return process_dep(branch, name, 'requires')
|
| |
|
| |
|
| |
- @asyncio.coroutine
|
| |
- def get_recommends(request):
|
| |
- return process_dep(request, 'recommends')
|
| |
+ @app.route("/<branch>/obsoletes/<name>")
|
| |
+ def get_obsoletes(branch, name):
|
| |
+ return process_dep(branch, name, 'obsoletes')
|
| |
|
| |
|
| |
- @asyncio.coroutine
|
| |
- def get_suggests(request):
|
| |
- return process_dep(request, 'suggests')
|
| |
+ @app.route("/<branch>/conflicts/<name>")
|
| |
+ def get_conflicts(branch, name):
|
| |
+ return process_dep(branch, name, 'conflicts')
|
| |
|
| |
|
| |
- @asyncio.coroutine
|
| |
- def get_supplements(request):
|
| |
- return process_dep(request, 'supplements')
|
| |
+ @app.route("/<branch>/enhances/<name>")
|
| |
+ def get_enhances(branch, name):
|
| |
+ return process_dep(branch, name, 'enhances')
|
| |
|
| |
|
| |
- @asyncio.coroutine
|
| |
- def index(request):
|
| |
- _log.info('index %s', request)
|
| |
- return web.Response(
|
| |
- body=INDEX.encode('utf-8'),
|
| |
- content_type='text/html',
|
| |
- charset='utf-8')
|
| |
+ @app.route("/<branch>/recommends/<name>")
|
| |
+ def get_recommends(branch, name):
|
| |
+ return process_dep(branch, name, 'recommends')
|
| |
|
| |
|
| |
- def _set_routes(app):
|
| |
- routes = []
|
| |
- prefix = CONFIG.get('PREFIX', '')
|
| |
- if prefix:
|
| |
- routes.append(('', index))
|
| |
+ @app.route("/<branch>/suggests/<name>")
|
| |
+ def get_suggests(branch, name):
|
| |
+ return process_dep(branch, name, 'suggests')
|
| |
|
| |
- routes.extend([
|
| |
- ('/', index),
|
| |
- ('/branches', list_branches),
|
| |
- ('/{branch}/pkg/{name}', get_pkg),
|
| |
- ('/{branch}/srcpkg/{name}', get_src_pkg),
|
| |
|
| |
- ('/{branch}/provides/{name}', get_provides),
|
| |
- ('/{branch}/requires/{name}', get_requires),
|
| |
- ('/{branch}/obsoletes/{name}', get_obsoletes),
|
| |
- ('/{branch}/conflicts/{name}', get_conflicts),
|
| |
+ @app.route("/<branch>/supplements/<name>")
|
| |
+ def get_supplements(branch, name):
|
| |
+ return process_dep(branch, name, 'supplements')
|
| |
|
| |
- ('/{branch}/enhances/{name}', get_enhances),
|
| |
- ('/{branch}/recommends/{name}', get_recommends),
|
| |
- ('/{branch}/suggests/{name}', get_suggests),
|
| |
- ('/{branch}/supplements/{name}', get_supplements),
|
| |
|
| |
- ('/{branch}/files/{name}', get_pkg_files),
|
| |
- ('/{branch}/changelog/{name}', get_pkg_changelog),
|
| |
- ])
|
| |
- for route in routes:
|
| |
- app.router.add_route('GET', prefix + route[0], route[1])
|
| |
- return app
|
| |
+ @app.route("/")
|
| |
+ def index():
|
| |
+ return send_from_directory(directory="", filename="index.html")
|
| |
This commit moves mdapi from the aiohttp framework to flask. This
is done since there is little interest in using aiohttp because all
the call to sqlite are io blocking. There is no asyncio driver for sqlalchemy
and sqlite.
Also performance with Flask and 4 gunicorn workers are better than the
performances achieved with aiohttp.
Signed-off-by: Clement Verna cverna@tutanota.com