Skip to content
Snippets Groups Projects
Commit e8f9f50b authored by Stefano Zacchiroli's avatar Stefano Zacchiroli
Browse files

API: add /node and /edge endpoints to check for node/edge existence

parent e869080d
No related branches found
No related tags found
No related merge requests found
......@@ -48,6 +48,50 @@ Examples
- ``"*:rel"`` node types allowing all edges to releases.
Node & edge existence
---------------------
.. http:get:: /graph/node/:src
Check whether a given node exists in the graph or not.
:param string src: source node specified as a SWH PID
:statuscode 200: success
:statuscode 400: invalid PID
:statuscode 404: node not found
.. sourcecode:: http
GET /graph/node/swh:1:rev:f39d7d78b70e0f39facb1e4fab77ad3df5c52a35
HTTP/1.1 200 OK
Content-Type: text/plain
swh:1:rev:f39d7d78b70e0f39facb1e4fab77ad3df5c52a35
.. http:get:: /graph/edge/:src/:dst
Check whether a given edge exists in the graph or not.
:param string src: source node specified as a SWH PID
:param string dst: destination node specified as a SWH PID
:statuscode 200: success
:statuscode 400: invalid PID(s)
:statuscode 404: node(s) not found
.. sourcecode:: http
GET /graph/node/swh:1:rev:f39d7d78b70e0f39facb1e4fab77ad3df5c52a35/swh:1:dir:b5d2aa0746b70300ebbca82a8132af386cc5986d
HTTP/1.1 200 OK
Content-Type: text/plain
swh:1:rev:f39d7d78b70e0f39facb1e4fab77ad3df5c52a35 swh:1:dir:b5d2aa0746b70300ebbca82a8132af386cc5986d
Leaves
------
......
......@@ -5,7 +5,7 @@
import json
from swh.core.api import RPCClient
from swh.core.api import RPCClient, RemoteException
class GraphAPIError(Exception):
......@@ -36,6 +36,24 @@ class RemoteGraphClient(RPCClient):
def stats(self):
return self.get('stats')
def node(self, src):
try:
list(self.get_lines('node/{}'.format(src)))
return True
except RemoteException:
return False
def edge(self, src, dst, edges="*", direction="forward"):
try:
list(self.get_lines('edge/{}/{}'.format(src, dst),
params={
'edges': edges,
'direction': direction
}))
return True
except RemoteException:
return False
def leaves(self, src, edges="*", direction="forward"):
return self.get_lines(
'leaves/{}'.format(src),
......
......@@ -105,6 +105,37 @@ def pid_of_node(node, backend):
body=f'reverse lookup failed for node id: {node}')
async def node(request):
"""check if a node exists in the graph"""
backend = request.app['backend']
src = request.match_info['src']
node_of_pid(src, backend) # will barf if node doesn't exist
return aiohttp.web.Response(body=f'{src}', content_type='text/plain')
async def edge(request):
"""check if an edge exists in the graph"""
backend = request.app['backend']
edges = get_edges(request)
direction = get_direction(request)
src = request.match_info['src']
dst = request.match_info['dst']
src_node = node_of_pid(src, backend)
dst_node = node_of_pid(dst, backend)
async for res_node in backend.simple_traversal(
'neighbors', direction, edges, src_node
):
if res_node == dst_node:
return aiohttp.web.Response(body=f'{src} {dst}',
content_type='text/plain')
raise aiohttp.web.HTTPNotFound(body=f'edge not found: {src} -> {dst}')
def get_simple_traversal_handler(ttype):
async def simple_traversal(request):
backend = request.app['backend']
......@@ -199,6 +230,9 @@ def make_app(backend, **kwargs):
app.router.add_get('/graph', index)
app.router.add_get('/graph/stats', stats)
app.router.add_get('/graph/node/{src}', node)
app.router.add_get('/graph/edge/{src}/{dst}', edge)
app.router.add_get('/graph/leaves/{src}',
get_simple_traversal_handler('leaves'))
app.router.add_get('/graph/neighbors/{src}',
......
......@@ -32,6 +32,57 @@ def test_stats(graph_client):
assert isinstance(stats['outdegree']['avg'], float)
def test_node(graph_client):
existing_nodes = [
'swh:1:cnt:0000000000000000000000000000000000000005',
'swh:1:dir:0000000000000000000000000000000000000017',
'swh:1:ori:0000000000000000000000000000000000000021',
'swh:1:rel:0000000000000000000000000000000000000019',
'swh:1:rev:0000000000000000000000000000000000000018',
'swh:1:snp:0000000000000000000000000000000000000020',
]
non_existing_nodes = [
'swh:1:cnt:00f0000000000000000000000000000000000005',
'swh:1:dir:000f000000000000000000000000000000000017',
'swh:1:ori:0000f00000000000000000000000000000000021',
'swh:1:rel:00000f0000000000000000000000000000000019',
'swh:1:rev:000000f000000000000000000000000000000018',
'swh:1:snp:0000000f00000000000000000000000000000020',
'swh:2:inv:00invalidpidbwawaaaaaaa00000000000000020',
]
for node in existing_nodes:
assert graph_client.node(node)
for node in non_existing_nodes:
assert not(graph_client.node(node))
def test_edge(graph_client):
existing_edges = [
('swh:1:dir:0000000000000000000000000000000000000002',
'swh:1:cnt:0000000000000000000000000000000000000001'),
('swh:1:rev:0000000000000000000000000000000000000003',
'swh:1:dir:0000000000000000000000000000000000000002'),
('swh:1:rev:0000000000000000000000000000000000000009',
'swh:1:dir:0000000000000000000000000000000000000008'),
('swh:1:snp:0000000000000000000000000000000000000020',
'swh:1:rel:0000000000000000000000000000000000000010'),
('swh:1:ori:0000000000000000000000000000000000000021',
'swh:1:snp:0000000000000000000000000000000000000020'),
]
non_existing_edges = [
('swh:1:dir:0000000000000000000000000000000000000002',
'swh:1:ori:0000000000000000000000000000000000000021'),
('swh:1:rev:0000000000000000000000000000000000000018',
'swh:1:cnt:0000000000000000000000000000000000000001'),
('swh:1:ori:0000000000000000000000000000000000000021',
'swh:2:inv:00invalidpidbwawaaaaaaa00000000000000020'),
]
for (src, dst) in existing_edges:
assert graph_client.edge(src, dst)
for (src, dst) in non_existing_edges:
assert not(graph_client.edge(src, dst))
def test_leaves(graph_client):
actual = list(graph_client.leaves(
'swh:1:ori:0000000000000000000000000000000000000021'
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment