Skip to content

Commit 9f48159

Browse files
committed
[explorer/nodewatch]: Added query params for get node list endpoint
1 parent 4ad67d2 commit 9f48159

File tree

3 files changed

+95
-13
lines changed

3 files changed

+95
-13
lines changed

explorer/nodewatch/nodewatch/RoutesFacade.py

+27-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import asyncio
22
import datetime
3+
import random
34

45
from zenlog import log
56

@@ -68,15 +69,35 @@ def html_nodes(self):
6869
'explorer_endpoint': self.explorer_endpoint
6970
})
7071

71-
def json_nodes(self, role, exact_match=False):
72-
"""Returns all nodes with matching role."""
72+
def json_nodes(self, **kwargs):
73+
"""Returns all nodes with condition."""
7374

74-
def role_filter(descriptor):
75-
return role == descriptor.roles if exact_match else role == (role & descriptor.roles)
75+
role = kwargs.get('role')
76+
exact_match = kwargs.get('exact_match')
7677

77-
return list(map(
78+
limit = kwargs.get('limit')
79+
only_ssl = kwargs.get('only_ssl')
80+
order = kwargs.get('order')
81+
82+
def custom_filter(descriptor):
83+
role_condition = True
84+
85+
if role is not None:
86+
role_condition = role == descriptor.roles if exact_match else role == (role & descriptor.roles)
87+
88+
if only_ssl:
89+
return role_condition and descriptor.is_ssl_enabled
90+
91+
return role_condition
92+
93+
nodes = list(map(
7894
lambda descriptor: descriptor.to_json(),
79-
filter(role_filter, self.repository.node_descriptors)))
95+
filter(custom_filter, self.repository.node_descriptors)))
96+
97+
if order == 'random':
98+
random.shuffle(nodes)
99+
100+
return nodes if limit == 0 else nodes[:limit]
80101

81102
def json_node(self, filter_field, public_key):
82103
"""Returns a node with matching main or node public key."""

explorer/nodewatch/nodewatch/__init__.py

+14-3
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def nem_summary(): # pylint: disable=unused-variable
6161

6262
@app.route('/api/nem/nodes')
6363
def api_nem_nodes(): # pylint: disable=unused-variable
64-
return jsonify(nem_routes_facade.json_nodes(1))
64+
return jsonify(nem_routes_facade.json_nodes(role=1))
6565

6666
@app.route('/api/nem/chart/height')
6767
def api_nem_chart_height(): # pylint: disable=unused-variable
@@ -91,6 +91,17 @@ def symbol_summary(): # pylint: disable=unused-variable
9191
template_name, context = symbol_routes_facade.html_summary()
9292
return render_template(template_name, **context)
9393

94+
def _get_json_nodes(role, exact_match, request_args):
95+
only_ssl = None
96+
if 'only_ssl' in request_args:
97+
only_ssl = True
98+
99+
order = request_args.get('order', None)
100+
101+
limit = int(request_args.get('limit', 0))
102+
103+
return jsonify(symbol_routes_facade.json_nodes(role=role, exact_match=exact_match, only_ssl=only_ssl, limit=limit, order=order))
104+
94105
def _get_json_node(result):
95106
if not result:
96107
abort(404)
@@ -105,11 +116,11 @@ def _validate_public_key(public_key):
105116

106117
@app.route('/api/symbol/nodes/api')
107118
def api_symbol_nodes_api(): # pylint: disable=unused-variable
108-
return jsonify(symbol_routes_facade.json_nodes(2, exact_match=True))
119+
return _get_json_nodes(2, True, request.args)
109120

110121
@app.route('/api/symbol/nodes/peer')
111122
def api_symbol_nodes_peer(): # pylint: disable=unused-variable
112-
return jsonify(symbol_routes_facade.json_nodes(1))
123+
return _get_json_nodes(1, False, request.args)
113124

114125
@app.route('/api/symbol/nodes/mainPublicKey/<main_public_key>')
115126
def api_symbol_nodes_get_main_public_key(main_public_key): # pylint: disable=unused-variable

explorer/nodewatch/tests/test_RoutesFacade.py

+54-4
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def test_can_generate_nodes_json(self):
150150
facade.reload_all(Path('tests/resources'), True)
151151

152152
# Act:
153-
node_descriptors = facade.json_nodes(1)
153+
node_descriptors = facade.json_nodes(role=1)
154154

155155
# Assert: spot check names and roles
156156
self.assertEqual(4, len(node_descriptors))
@@ -361,7 +361,7 @@ def test_can_generate_nodes_json(self):
361361
facade.reload_all(Path('tests/resources'), True)
362362

363363
# Act:
364-
node_descriptors = facade.json_nodes(1)
364+
node_descriptors = facade.json_nodes(role=1)
365365

366366
# Assert: spot check names and roles
367367
self.assertEqual(5, len(node_descriptors))
@@ -378,7 +378,7 @@ def test_can_generate_nodes_json_filtered(self):
378378
facade.reload_all(Path('tests/resources'), True)
379379

380380
# Act: select nodes with api role (role 2)
381-
node_descriptors = facade.json_nodes(2)
381+
node_descriptors = facade.json_nodes(role=2)
382382

383383
# Assert: spot check names and roles
384384
self.assertEqual(5, len(node_descriptors))
@@ -395,7 +395,7 @@ def test_can_generate_nodes_json_filtered_exact_match(self):
395395
facade.reload_all(Path('tests/resources'), True)
396396

397397
# Act: select nodes with only api role (role 2)
398-
node_descriptors = facade.json_nodes(2, True)
398+
node_descriptors = facade.json_nodes(role=2, exact_match=True)
399399

400400
# Assert: spot check names and roles
401401
self.assertEqual(1, len(node_descriptors))
@@ -406,6 +406,56 @@ def test_can_generate_nodes_json_filtered_exact_match(self):
406406
[2],
407407
list(map(lambda descriptor: descriptor['roles'], node_descriptors)))
408408

409+
def test_can_generate_nodes_json_filtered_only_ssl(self):
410+
# Arrange:
411+
facade = SymbolRoutesFacade(SymbolNetwork.MAINNET, '<symbol_explorer>')
412+
facade.reload_all(Path('tests/resources'), True)
413+
414+
# Act: select nodes with only ssl enabled
415+
node_descriptors = facade.json_nodes(only_ssl=True)
416+
417+
# Assert: spot check names and roles
418+
self.assertEqual(2, len(node_descriptors))
419+
self.assertEqual(
420+
['Allnodes250', 'ibone74'],
421+
list(map(lambda descriptor: descriptor['name'], node_descriptors)))
422+
423+
def test_can_generate_nodes_json_filtered_order_random_subset(self):
424+
# Arrange:
425+
facade = SymbolRoutesFacade(SymbolNetwork.MAINNET, '<symbol_explorer>')
426+
facade.reload_all(Path('tests/resources'), True)
427+
428+
# Act: select 2 nodes with order random
429+
node_descriptors = facade.json_nodes(limit=2, order='random')
430+
431+
# Assert:
432+
all_node_descriptors = facade.json_nodes()
433+
434+
full_node_names = list(map(lambda descriptor: descriptor['name'], all_node_descriptors))
435+
random_node_names = list(map(lambda descriptor: descriptor['name'], node_descriptors))
436+
437+
self.assertEqual(2, len(node_descriptors))
438+
self.assertEqual(2, len(set(random_node_names)))
439+
for name in random_node_names:
440+
self.assertIn(name, full_node_names)
441+
442+
def test_can_generate_nodes_json_filtered_limit_5(self):
443+
# Arrange:
444+
facade = SymbolRoutesFacade(SymbolNetwork.MAINNET, '<symbol_explorer>')
445+
facade.reload_all(Path('tests/resources'), True)
446+
447+
# Act:
448+
node_descriptors = facade.json_nodes(limit=5)
449+
450+
# Assert: spot check names
451+
self.assertEqual(5, len(node_descriptors))
452+
self.assertEqual(
453+
['Allnodes250', 'Apple', 'Shin-Kuma-Node', 'ibone74', 'jaguar'],
454+
list(map(lambda descriptor: descriptor['name'], node_descriptors)))
455+
self.assertEqual(
456+
[2, 7, 3, 3, 5],
457+
list(map(lambda descriptor: descriptor['roles'], node_descriptors)))
458+
409459
def test_can_find_known_node_by_main_public_key(self): # pylint: disable=invalid-name
410460
# Arrange:
411461
facade = SymbolRoutesFacade(SymbolNetwork.MAINNET, '<symbol_explorer>')

0 commit comments

Comments
 (0)