Changeset View
Changeset View
Standalone View
Standalone View
swh/graph/server/app.py
Show All 16 Lines | |||||
try: | try: | ||||
from contextlib import asynccontextmanager | from contextlib import asynccontextmanager | ||||
except ImportError: | except ImportError: | ||||
# Compatibility with 3.6 backport | # Compatibility with 3.6 backport | ||||
from async_generator import asynccontextmanager # type: ignore | from async_generator import asynccontextmanager # type: ignore | ||||
MIME_TYPE_HTML = 'text/html' | |||||
MIME_TYPE_JSON = 'application/json' | |||||
MIME_TYPE_NDJSON = 'application/x-ndjson' # see http://ndjson.org/ | |||||
MIME_TYPE_TEXT = 'text/plain' | |||||
seirl: It feels weird to me to put these at the top instead of inlining them, as the constant name is… | |||||
zackAuthorUnsubmitted Done Inline ActionsI've inlined them now. zack: I've inlined them now. | |||||
@asynccontextmanager | @asynccontextmanager | ||||
async def stream_response(request, *args, **kwargs): | async def stream_response(request, content_type=MIME_TYPE_TEXT, | ||||
*args, **kwargs): | |||||
response = aiohttp.web.StreamResponse(*args, **kwargs) | response = aiohttp.web.StreamResponse(*args, **kwargs) | ||||
response.content_type = content_type | |||||
await response.prepare(request) | await response.prepare(request) | ||||
yield response | yield response | ||||
await response.write_eof() | await response.write_eof() | ||||
async def index(request): | async def index(request): | ||||
return aiohttp.web.Response( | return aiohttp.web.Response( | ||||
content_type='text/html', | content_type=MIME_TYPE_HTML, | ||||
body="""<html> | body="""<html> | ||||
<head><title>Software Heritage storage server</title></head> | <head><title>Software Heritage storage server</title></head> | ||||
<body> | <body> | ||||
<p>You have reached the <a href="https://www.softwareheritage.org/"> | <p>You have reached the <a href="https://www.softwareheritage.org/"> | ||||
Software Heritage</a> graph API server.</p> | Software Heritage</a> graph API server.</p> | ||||
<p>See its | <p>See its | ||||
<a href="https://docs.softwareheritage.org/devel/swh-graph/api.html">API | <a href="https://docs.softwareheritage.org/devel/swh-graph/api.html">API | ||||
documentation</a> for more information.</p> | documentation</a> for more information.</p> | ||||
</body> | </body> | ||||
</html>""") | </html>""") | ||||
async def stats(request): | async def stats(request): | ||||
stats = request.app['backend'].stats() | stats = request.app['backend'].stats() | ||||
return aiohttp.web.Response(body=stats, content_type='application/json') | return aiohttp.web.Response(body=stats, content_type=MIME_TYPE_JSON) | ||||
def get_simple_traversal_handler(ttype): | def get_simple_traversal_handler(ttype): | ||||
async def simple_traversal(request): | async def simple_traversal(request): | ||||
backend = request.app['backend'] | backend = request.app['backend'] | ||||
src = request.match_info['src'] | src = request.match_info['src'] | ||||
edges = request.query.get('edges', '*') | edges = request.query.get('edges', '*') | ||||
Show All 36 Lines | async def visit_paths(request): | ||||
backend = request.app['backend'] | backend = request.app['backend'] | ||||
src = request.match_info['src'] | src = request.match_info['src'] | ||||
edges = request.query.get('edges', '*') | edges = request.query.get('edges', '*') | ||||
direction = request.query.get('direction', 'forward') | direction = request.query.get('direction', 'forward') | ||||
src_node = backend.pid2node[src] | src_node = backend.pid2node[src] | ||||
it = backend.visit_paths(direction, edges, src_node) | it = backend.visit_paths(direction, edges, src_node) | ||||
async with stream_response(request) as response: | async with stream_response(request, content_type=MIME_TYPE_NDJSON) \ | ||||
as response: | |||||
async for res_path in it: | async for res_path in it: | ||||
res_path_pid = [backend.node2pid[n] for n in res_path] | res_path_pid = [backend.node2pid[n] for n in res_path] | ||||
line = json.dumps(res_path_pid) | line = json.dumps(res_path_pid) | ||||
await response.write('{}\n'.format(line).encode()) | await response.write('{}\n'.format(line).encode()) | ||||
return response | return response | ||||
def get_count_handler(ttype): | def get_count_handler(ttype): | ||||
async def count(request): | async def count(request): | ||||
loop = asyncio.get_event_loop() | loop = asyncio.get_event_loop() | ||||
backend = request.app['backend'] | backend = request.app['backend'] | ||||
src = request.match_info['src'] | src = request.match_info['src'] | ||||
edges = request.query.get('edges', '*') | edges = request.query.get('edges', '*') | ||||
direction = request.query.get('direction', 'forward') | direction = request.query.get('direction', 'forward') | ||||
src_node = backend.pid2node[src] | src_node = backend.pid2node[src] | ||||
cnt = await loop.run_in_executor( | cnt = await loop.run_in_executor( | ||||
None, backend.count, ttype, direction, edges, src_node) | None, backend.count, ttype, direction, edges, src_node) | ||||
return aiohttp.web.Response(body=str(cnt), | return aiohttp.web.Response(body=str(cnt), | ||||
content_type='application/json') | content_type=MIME_TYPE_JSON) | ||||
return count | return count | ||||
def make_app(backend, **kwargs): | def make_app(backend, **kwargs): | ||||
app = RPCServerApp(**kwargs) | app = RPCServerApp(**kwargs) | ||||
app.router.add_route('GET', '/', index) | app.router.add_route('GET', '/', index) | ||||
app.router.add_route('GET', '/graph/stats', stats) | app.router.add_route('GET', '/graph/stats', stats) | ||||
Show All 19 Lines |
It feels weird to me to put these at the top instead of inlining them, as the constant name is literally reflecting the content of the constant. Feel free to disagree.