Skip to content

Commit

Permalink
Merge pull request #1361 from erikoqvist/add_pagination_to_rest_api
Browse files Browse the repository at this point in the history
rest: Add pagination support
  • Loading branch information
garberg authored Nov 13, 2023
2 parents 4db9908 + 2bef229 commit 67d2196
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 11 deletions.
28 changes: 21 additions & 7 deletions nipap/nipap/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,19 @@
# read-only from _prefix_spec
_prefix_attrs = {k: v for k, v in _prefix_spec.items() if not _prefix_spec[k]['ro']}


# list of all available search options on a prefix
prefix_search_options_spec = {
'parents_depth': 0,
'children_depth': 0,
'include_all_parents': False,
'include_all_children': False,
'include_neighbors': False,
'max_result': 50,
'offset': 0
}


# list of all attributes on a vrf, including both writable and read-only values
_vrf_spec = {
'avps': {
Expand Down Expand Up @@ -3091,6 +3104,7 @@ def search_prefix(self, auth, query, search_options=None):
* :attr:`children_depth` - How many levels of children to return. Set to :data:`-1` to include all children.
* :attr:`include_all_parents` - Include all parents, no matter what depth is specified.
* :attr:`include_all_children` - Include all children, no matter what depth is specified.
* :attr:`include_neighbors` - Include neighbors.
* :attr:`max_result` - The maximum number of prefixes to return (default :data:`50`).
* :attr:`offset` - Offset the result list this many prefixes (default :data:`0`).
Expand Down Expand Up @@ -3124,7 +3138,7 @@ def search_prefix(self, auth, query, search_options=None):

# include_parents
if 'include_all_parents' not in search_options:
search_options['include_all_parents'] = False
search_options['include_all_parents'] = prefix_search_options_spec['include_all_parents']
else:
if search_options['include_all_parents'] not in (True, False):
raise NipapValueError(
Expand All @@ -3133,15 +3147,15 @@ def search_prefix(self, auth, query, search_options=None):

# include_children
if 'include_all_children' not in search_options:
search_options['include_all_children'] = False
search_options['include_all_children'] = prefix_search_options_spec['include_all_children']
else:
if search_options['include_all_children'] not in (True, False):
raise NipapValueError("Invalid value for option 'include_all_children'. Only true and false valid. "
"Supplied value: '{}'".format(search_options['include_all_children']))

# parents_depth
if 'parents_depth' not in search_options:
search_options['parents_depth'] = 0
search_options['parents_depth'] = prefix_search_options_spec['parents_depth']
else:
try:
search_options['parents_depth'] = int(search_options['parents_depth'])
Expand All @@ -3150,7 +3164,7 @@ def search_prefix(self, auth, query, search_options=None):

# children_depth
if 'children_depth' not in search_options:
search_options['children_depth'] = 0
search_options['children_depth'] = prefix_search_options_spec['children_depth']
else:
try:
search_options['children_depth'] = int(search_options['children_depth'])
Expand All @@ -3159,15 +3173,15 @@ def search_prefix(self, auth, query, search_options=None):

# include_neighbors
if 'include_neighbors' not in search_options:
search_options['include_neighbors'] = False
search_options['include_neighbors'] = prefix_search_options_spec['include_neighbors']
else:
if search_options['include_neighbors'] not in (True, False):
raise NipapValueError("Invalid value for option 'include_neighbors'. Only true and false valid. "
"Supplied value: '{}'".format(search_options['include_neighbors']))

# max_result
if 'max_result' not in search_options:
search_options['max_result'] = 50
search_options['max_result'] = prefix_search_options_spec['max_result']
else:
if search_options['max_result'] in (False, None):
search_options['max_result'] = None
Expand All @@ -3179,7 +3193,7 @@ def search_prefix(self, auth, query, search_options=None):

# offset
if 'offset' not in search_options:
search_options['offset'] = 0
search_options['offset'] = prefix_search_options_spec['offset']
else:
try:
search_options['offset'] = int(search_options['offset'])
Expand Down
12 changes: 8 additions & 4 deletions nipap/nipap/rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from flask import Flask, request, Response, got_request_exception, jsonify
from flask_restful import Resource, Api, abort

from .backend import Nipap, NipapError
from .backend import Nipap, NipapError, prefix_search_options_spec
import nipap
from .authlib import AuthFactory, AuthError

Expand Down Expand Up @@ -219,11 +219,15 @@ def get(self, args):

query = args.get('prefix')
search_query = {}
search_options = {}
if query is not None:
# Create search query dict from request params
query_parts = []
for field, search_value in list(query.items()):
query_parts.append(get_query_for_field(field, search_value))
for field, value in list(query.items()):
if field in prefix_search_options_spec.keys():
search_options[field] = value
else:
query_parts.append(get_query_for_field(field, value))
search_query = query_parts[0]
for query_part in query_parts[1:]:
search_query = {
Expand All @@ -233,7 +237,7 @@ def get(self, args):
}

try:
result = self.nip.search_prefix(args.get('auth'), search_query)
result = self.nip.search_prefix(args.get('auth'), search_query, search_options)

# mangle result
result['result'] = [ _mangle_prefix(prefix) for prefix in result['result'] ]
Expand Down
39 changes: 39 additions & 0 deletions tests/test_rest.py
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,45 @@ def test_prefix_search_error_message(self):

self.assertTrue(get_prefix_request.text.__contains__('\'prefixeere\' unknown'))

def test_prefix_pagination(self):
""" Add prefixes with the same order_id, expect different number of prefixes in result
using max_result and offset
"""

# add test prefixes 1.33.[0-59].0/24
attr = {}
attr['description'] = 'test edit prefix'
attr['type'] = 'assignment'
attr['order_id'] = 'test_rest'
for i in range(0, 60):
attr['prefix'] = '1.33.' + str(i) + '.0/24'
self._add_prefix(attr)

parameters = {'order_id': 'test_rest'}

# Test default max result of 50 amd offset 0
get_prefix_request = requests.get(self.server_url, headers=self.headers, params=parameters)
result = json.loads(get_prefix_request.text)
self.assertEqual(50, len(result))
self.assertEqual(result[0]['prefix'], '1.33.0.0/24')
self.assertEqual(result[49]['prefix'], '1.33.49.0/24')

# Test max result 100
parameters['max_result'] = 60
get_prefix_request = requests.get(self.server_url, headers=self.headers, params=parameters)
result = json.loads(get_prefix_request.text)
self.assertEqual(60, len(result))
self.assertEqual(result[0]['prefix'], '1.33.0.0/24')
self.assertEqual(result[59]['prefix'], '1.33.59.0/24')

# Test offset 75
parameters['offset'] = 35
get_prefix_request = requests.get(self.server_url, headers=self.headers, params=parameters)
result = json.loads(get_prefix_request.text)
self.assertEqual(25, len(result))
self.assertEqual(result[0]['prefix'], '1.33.35.0/24')
self.assertEqual(result[24]['prefix'], '1.33.59.0/24')


if __name__ == '__main__':

Expand Down

0 comments on commit 67d2196

Please sign in to comment.