From f411b79d9eecb734461405722068451371e74394 Mon Sep 17 00:00:00 2001 From: kindly Date: Tue, 7 Aug 2012 16:32:34 +0100 Subject: [PATCH] [#2733] parse and validate sort --- ckanext/datastore/db.py | 47 ++++++++++++++++++++--- ckanext/datastore/tests/test_datastore.py | 4 +- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/ckanext/datastore/db.py b/ckanext/datastore/db.py index 4c8e17ec351..f38911f9bab 100644 --- a/ckanext/datastore/db.py +++ b/ckanext/datastore/db.py @@ -2,6 +2,7 @@ import ckan.plugins as p import json import datetime +import shlex _pg_types = {} _type_names = set() @@ -304,7 +305,7 @@ def _where(field_ids, data_dict): q = data_dict.get('q') if q: - where_clauses.append('_full_text @@ to_tsquery(%s)'.format(q)) + where_clauses.append('_full_text @@ to_tsquery(%s)') values.append(q) where_clause = ' and '.join(where_clauses) @@ -312,6 +313,45 @@ def _where(field_ids, data_dict): where_clause = 'where ' + where_clause return where_clause, values +def _sort(context, sort, field_ids): + + if not sort: + return '' + + if isinstance(sort, basestring): + clauses = sort.split(',') + elif isinstance(sort, list): + clauses = sort + else: + raise p.toolkit.ValidationError({ + 'sort': 'sort is not a list or a string' + }) + + clause_parsed = [] + + for clause in clauses: + clause_parts = shlex.split(clause) + if len(clause_parts) == 1: + field, sort = clause_parts[0], 'asc' + elif len(clause_parts) == 2: + field, sort = clause_parts + else: + raise p.toolkit.ValidationError({ + 'sort': 'not valid syntax for sort clause' + }) + if field not in field_ids: + raise p.toolkit.ValidationError({ + 'sort': 'field {} not it table'.format(field) + }) + if sort.lower() not in ('asc', 'desc'): + raise p.toolkit.ValidationError({ + 'sort': 'sorting can only be asc or desc' + }) + clause_parsed.append('"{}" {}'.format(field, sort)) + + if clause_parsed: + return "order by " + ", ".join(clause_parsed) + def delete_data(context, data_dict): fields = _get_fields(context, data_dict) @@ -354,10 +394,7 @@ def search_data(context, data_dict): _validate_int(limit, 'limit') _validate_int(offset, 'offset') - if data_dict.get('sort'): - sort = 'order by {}'.format(data_dict['sort']) - else: - sort = '' + sort = _sort(context, data_dict.get('sort'), field_ids) sql_string = '''select {}, count(*) over() as full_count from "{}" {} {} limit {} offset {}'''\ diff --git a/ckanext/datastore/tests/test_datastore.py b/ckanext/datastore/tests/test_datastore.py index 40136f8fc15..85b1d961857 100644 --- a/ckanext/datastore/tests/test_datastore.py +++ b/ckanext/datastore/tests/test_datastore.py @@ -532,7 +532,7 @@ def test_search_filters(self): def test_search_sort(self): data = {'resource_id': self.data['resource_id'], - 'sort': 'book asc'} + 'sort': 'book asc, author desc'} postparams = '%s=1' % json.dumps(data) auth = {'Authorization': str(self.sysadmin_user.apikey)} res = self.app.post('/api/action/datastore_search', params=postparams, @@ -549,7 +549,7 @@ def test_search_sort(self): assert result['records'] == expected_records data = {'resource_id': self.data['resource_id'], - 'sort': 'book desc'} + 'sort': ['book desc', '"author" asc']} postparams = '%s=1' % json.dumps(data) res = self.app.post('/api/action/datastore_search', params=postparams, extra_environ=auth)