Merge "Fix sort of *list command output"

This commit is contained in:
Zuul 2018-03-30 07:42:52 +00:00 committed by Gerrit Code Review
commit 0353a0ac77
17 changed files with 331 additions and 122 deletions

View File

@ -205,7 +205,7 @@ class ActionCollection(collection.Collection):
collection = ActionCollection()
collection.actions = [Action.convert_with_links(p, expand)
for p in actions]
collection.next = collection.get_next(limit, url=url, **kwargs)
return collection
@classmethod
@ -232,6 +232,10 @@ class ActionsController(rest.RestController):
sort_key, sort_dir, expand=False,
resource_url=None,
action_plan_uuid=None, audit_uuid=None):
additional_fields = ['action_plan_uuid']
api_utils.validate_sort_key(sort_key, list(objects.Action.fields) +
additional_fields)
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
@ -247,7 +251,10 @@ class ActionsController(rest.RestController):
if audit_uuid:
filters['audit_uuid'] = audit_uuid
sort_db_key = sort_key
need_api_sort = api_utils.check_need_api_sort(sort_key,
additional_fields)
sort_db_key = (sort_key if not need_api_sort
else None)
actions = objects.Action.list(pecan.request.context,
limit,
@ -255,11 +262,15 @@ class ActionsController(rest.RestController):
sort_dir=sort_dir,
filters=filters)
return ActionCollection.convert_with_links(actions, limit,
url=resource_url,
expand=expand,
sort_key=sort_key,
sort_dir=sort_dir)
actions_collection = ActionCollection.convert_with_links(
actions, limit, url=resource_url, expand=expand,
sort_key=sort_key, sort_dir=sort_dir)
if need_api_sort:
api_utils.make_api_sort(actions_collection.actions,
sort_key, sort_dir)
return actions_collection
@wsme_pecan.wsexpose(ActionCollection, types.uuid, int,
wtypes.text, wtypes.text, types.uuid,

View File

@ -305,17 +305,6 @@ class ActionPlanCollection(collection.Collection):
ap_collection = ActionPlanCollection()
ap_collection.action_plans = [ActionPlan.convert_with_links(
p, expand) for p in rpc_action_plans]
if 'sort_key' in kwargs:
reverse = False
if kwargs['sort_key'] == 'audit_uuid':
if 'sort_dir' in kwargs:
reverse = True if kwargs['sort_dir'] == 'desc' else False
ap_collection.action_plans = sorted(
ap_collection.action_plans,
key=lambda action_plan: action_plan.audit_uuid,
reverse=reverse)
ap_collection.next = ap_collection.get_next(limit, url=url, **kwargs)
return ap_collection
@ -344,7 +333,10 @@ class ActionPlansController(rest.RestController):
sort_key, sort_dir, expand=False,
resource_url=None, audit_uuid=None,
strategy=None):
additional_fields = ['audit_uuid', 'strategy_uuid', 'strategy_name']
api_utils.validate_sort_key(
sort_key, list(objects.ActionPlan.fields) + additional_fields)
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
@ -363,10 +355,10 @@ class ActionPlansController(rest.RestController):
else:
filters['strategy_name'] = strategy
if sort_key == 'audit_uuid':
sort_db_key = None
else:
sort_db_key = sort_key
need_api_sort = api_utils.check_need_api_sort(sort_key,
additional_fields)
sort_db_key = (sort_key if not need_api_sort
else None)
action_plans = objects.ActionPlan.list(
pecan.request.context,
@ -374,12 +366,15 @@ class ActionPlansController(rest.RestController):
marker_obj, sort_key=sort_db_key,
sort_dir=sort_dir, filters=filters)
return ActionPlanCollection.convert_with_links(
action_plans, limit,
url=resource_url,
expand=expand,
sort_key=sort_key,
sort_dir=sort_dir)
action_plans_collection = ActionPlanCollection.convert_with_links(
action_plans, limit, url=resource_url, expand=expand,
sort_key=sort_key, sort_dir=sort_dir)
if need_api_sort:
api_utils.make_api_sort(action_plans_collection.action_plans,
sort_key, sort_dir)
return action_plans_collection
@wsme_pecan.wsexpose(ActionPlanCollection, types.uuid, int, wtypes.text,
wtypes.text, types.uuid, wtypes.text)

View File

@ -389,17 +389,6 @@ class AuditCollection(collection.Collection):
collection = AuditCollection()
collection.audits = [Audit.convert_with_links(p, expand)
for p in rpc_audits]
if 'sort_key' in kwargs:
reverse = False
if kwargs['sort_key'] == 'goal_uuid':
if 'sort_dir' in kwargs:
reverse = True if kwargs['sort_dir'] == 'desc' else False
collection.audits = sorted(
collection.audits,
key=lambda audit: audit.goal_uuid,
reverse=reverse)
collection.next = collection.get_next(limit, url=url, **kwargs)
return collection
@ -427,8 +416,14 @@ class AuditsController(rest.RestController):
sort_key, sort_dir, expand=False,
resource_url=None, goal=None,
strategy=None):
additional_fields = ["goal_uuid", "goal_name", "strategy_uuid",
"strategy_name"]
api_utils.validate_sort_key(
sort_key, list(objects.Audit.fields) + additional_fields)
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
marker_obj = None
if marker:
marker_obj = objects.Audit.get_by_uuid(pecan.request.context,
@ -449,23 +444,25 @@ class AuditsController(rest.RestController):
# TODO(michaelgugino): add method to get goal by name.
filters['strategy_name'] = strategy
if sort_key == 'goal_uuid':
sort_db_key = 'goal_id'
elif sort_key == 'strategy_uuid':
sort_db_key = 'strategy_id'
else:
sort_db_key = sort_key
need_api_sort = api_utils.check_need_api_sort(sort_key,
additional_fields)
sort_db_key = (sort_key if not need_api_sort
else None)
audits = objects.Audit.list(pecan.request.context,
limit,
marker_obj, sort_key=sort_db_key,
sort_dir=sort_dir, filters=filters)
return AuditCollection.convert_with_links(audits, limit,
url=resource_url,
expand=expand,
sort_key=sort_key,
sort_dir=sort_dir)
audits_collection = AuditCollection.convert_with_links(
audits, limit, url=resource_url, expand=expand,
sort_key=sort_key, sort_dir=sort_dir)
if need_api_sort:
api_utils.make_api_sort(audits_collection.audits, sort_key,
sort_dir)
return audits_collection
@wsme_pecan.wsexpose(AuditCollection, types.uuid, int, wtypes.text,
wtypes.text, wtypes.text, wtypes.text, int)

View File

@ -474,9 +474,13 @@ class AuditTemplatesController(rest.RestController):
def _get_audit_templates_collection(self, filters, marker, limit,
sort_key, sort_dir, expand=False,
resource_url=None):
additional_fields = ["goal_uuid", "goal_name", "strategy_uuid",
"strategy_name"]
api_utils.validate_sort_key(
sort_key, list(objects.AuditTemplate.fields) + additional_fields)
api_utils.validate_search_filters(
filters, list(objects.audit_template.AuditTemplate.fields) +
["goal_uuid", "goal_name", "strategy_uuid", "strategy_name"])
filters, list(objects.AuditTemplate.fields) + additional_fields)
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
@ -486,19 +490,26 @@ class AuditTemplatesController(rest.RestController):
pecan.request.context,
marker)
audit_templates = objects.AuditTemplate.list(
pecan.request.context,
filters,
limit,
marker_obj, sort_key=sort_key,
sort_dir=sort_dir)
need_api_sort = api_utils.check_need_api_sort(sort_key,
additional_fields)
sort_db_key = (sort_key if not need_api_sort
else None)
return AuditTemplateCollection.convert_with_links(audit_templates,
limit,
url=resource_url,
expand=expand,
sort_key=sort_key,
sort_dir=sort_dir)
audit_templates = objects.AuditTemplate.list(
pecan.request.context, filters, limit, marker_obj,
sort_key=sort_db_key, sort_dir=sort_dir)
audit_templates_collection = \
AuditTemplateCollection.convert_with_links(
audit_templates, limit, url=resource_url, expand=expand,
sort_key=sort_key, sort_dir=sort_dir)
if need_api_sort:
api_utils.make_api_sort(
audit_templates_collection.audit_templates, sort_key,
sort_dir)
return audit_templates_collection
@wsme_pecan.wsexpose(AuditTemplateCollection, wtypes.text, wtypes.text,
types.uuid, int, wtypes.text, wtypes.text)

View File

@ -130,17 +130,6 @@ class GoalCollection(collection.Collection):
goal_collection = GoalCollection()
goal_collection.goals = [
Goal.convert_with_links(g, expand) for g in goals]
if 'sort_key' in kwargs:
reverse = False
if kwargs['sort_key'] == 'strategy':
if 'sort_dir' in kwargs:
reverse = True if kwargs['sort_dir'] == 'desc' else False
goal_collection.goals = sorted(
goal_collection.goals,
key=lambda goal: goal.uuid,
reverse=reverse)
goal_collection.next = goal_collection.get_next(
limit, url=url, **kwargs)
return goal_collection
@ -167,17 +156,19 @@ class GoalsController(rest.RestController):
def _get_goals_collection(self, marker, limit, sort_key, sort_dir,
expand=False, resource_url=None):
api_utils.validate_sort_key(
sort_key, list(objects.Goal.fields))
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
sort_db_key = (sort_key if sort_key in objects.Goal.fields
else None)
marker_obj = None
if marker:
marker_obj = objects.Goal.get_by_uuid(
pecan.request.context, marker)
sort_db_key = (sort_key if sort_key in objects.Goal.fields
else None)
goals = objects.Goal.list(pecan.request.context, limit, marker_obj,
sort_key=sort_db_key, sort_dir=sort_dir)

View File

@ -123,17 +123,6 @@ class ScoringEngineCollection(collection.Collection):
collection = ScoringEngineCollection()
collection.scoring_engines = [ScoringEngine.convert_with_links(
se, expand) for se in scoring_engines]
if 'sort_key' in kwargs:
reverse = False
if kwargs['sort_key'] == 'name':
if 'sort_dir' in kwargs:
reverse = True if kwargs['sort_dir'] == 'desc' else False
collection.goals = sorted(
collection.scoring_engines,
key=lambda se: se.name,
reverse=reverse)
collection.next = collection.get_next(limit, url=url, **kwargs)
return collection
@ -160,7 +149,8 @@ class ScoringEngineController(rest.RestController):
def _get_scoring_engines_collection(self, marker, limit,
sort_key, sort_dir, expand=False,
resource_url=None):
api_utils.validate_sort_key(
sort_key, list(objects.ScoringEngine.fields))
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
@ -171,7 +161,8 @@ class ScoringEngineController(rest.RestController):
filters = {}
sort_db_key = sort_key
sort_db_key = (sort_key if sort_key in objects.ScoringEngine.fields
else None)
scoring_engines = objects.ScoringEngine.list(
context=pecan.request.context,

View File

@ -154,17 +154,6 @@ class ServiceCollection(collection.Collection):
service_collection = ServiceCollection()
service_collection.services = [
Service.convert_with_links(g, expand) for g in services]
if 'sort_key' in kwargs:
reverse = False
if kwargs['sort_key'] == 'service':
if 'sort_dir' in kwargs:
reverse = True if kwargs['sort_dir'] == 'desc' else False
service_collection.services = sorted(
service_collection.services,
key=lambda service: service.id,
reverse=reverse)
service_collection.next = service_collection.get_next(
limit, url=url, marker_field='id', **kwargs)
return service_collection
@ -191,17 +180,19 @@ class ServicesController(rest.RestController):
def _get_services_collection(self, marker, limit, sort_key, sort_dir,
expand=False, resource_url=None):
api_utils.validate_sort_key(
sort_key, list(objects.Service.fields))
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
sort_db_key = (sort_key if sort_key in objects.Service.fields
else None)
marker_obj = None
if marker:
marker_obj = objects.Service.get(
pecan.request.context, marker)
sort_db_key = (sort_key if sort_key in objects.Service.fields
else None)
services = objects.Service.list(
pecan.request.context, limit, marker_obj,
sort_key=sort_db_key, sort_dir=sort_dir)

View File

@ -173,17 +173,6 @@ class StrategyCollection(collection.Collection):
strategy_collection = StrategyCollection()
strategy_collection.strategies = [
Strategy.convert_with_links(g, expand) for g in strategies]
if 'sort_key' in kwargs:
reverse = False
if kwargs['sort_key'] == 'strategy':
if 'sort_dir' in kwargs:
reverse = True if kwargs['sort_dir'] == 'desc' else False
strategy_collection.strategies = sorted(
strategy_collection.strategies,
key=lambda strategy: strategy.uuid,
reverse=reverse)
strategy_collection.next = strategy_collection.get_next(
limit, url=url, **kwargs)
return strategy_collection
@ -211,28 +200,39 @@ class StrategiesController(rest.RestController):
def _get_strategies_collection(self, filters, marker, limit, sort_key,
sort_dir, expand=False, resource_url=None):
additional_fields = ["goal_uuid", "goal_name"]
api_utils.validate_sort_key(
sort_key, list(objects.Strategy.fields) + additional_fields)
api_utils.validate_search_filters(
filters, list(objects.strategy.Strategy.fields) +
["goal_uuid", "goal_name"])
filters, list(objects.Strategy.fields) + additional_fields)
limit = api_utils.validate_limit(limit)
api_utils.validate_sort_dir(sort_dir)
sort_db_key = (sort_key if sort_key in objects.Strategy.fields
else None)
marker_obj = None
if marker:
marker_obj = objects.Strategy.get_by_uuid(
pecan.request.context, marker)
need_api_sort = api_utils.check_need_api_sort(sort_key,
additional_fields)
sort_db_key = (sort_key if not need_api_sort
else None)
strategies = objects.Strategy.list(
pecan.request.context, limit, marker_obj, filters=filters,
sort_key=sort_db_key, sort_dir=sort_dir)
return StrategyCollection.convert_with_links(
strategies_collection = StrategyCollection.convert_with_links(
strategies, limit, url=resource_url, expand=expand,
sort_key=sort_key, sort_dir=sort_dir)
if need_api_sort:
api_utils.make_api_sort(strategies_collection.strategies,
sort_key, sort_dir)
return strategies_collection
@wsme_pecan.wsexpose(StrategyCollection, wtypes.text, wtypes.text,
int, wtypes.text, wtypes.text)
def get_all(self, goal=None, marker=None, limit=None,

View File

@ -13,6 +13,8 @@
# License for the specific language governing permissions and limitations
# under the License.
from operator import attrgetter
import jsonpatch
from oslo_config import cfg
from oslo_utils import reflection
@ -54,6 +56,13 @@ def validate_sort_dir(sort_dir):
"'asc' or 'desc'") % sort_dir)
def validate_sort_key(sort_key, allowed_fields):
# Very lightweight validation for now
if sort_key not in allowed_fields:
raise wsme.exc.ClientSideError(
_("Invalid sort key: %s") % sort_key)
def validate_search_filters(filters, allowed_fields):
# Very lightweight validation for now
# todo: improve this (e.g. https://www.parse.com/docs/rest/guide/#queries)
@ -63,6 +72,19 @@ def validate_search_filters(filters, allowed_fields):
_("Invalid filter: %s") % filter_name)
def check_need_api_sort(sort_key, additional_fields):
return sort_key in additional_fields
def make_api_sort(sorting_list, sort_key, sort_dir):
# First sort by uuid field, than sort by sort_key
# sort() ensures stable sorting, so we could
# make lexicographical sort
reverse_direction = (sort_dir == 'desc')
sorting_list.sort(key=attrgetter('uuid'), reverse=reverse_direction)
sorting_list.sort(key=attrgetter(sort_key), reverse=reverse_direction)
def apply_jsonpatch(doc, patch):
for p in patch:
if p['op'] == 'add' and p['path'].count('/') == 1:

View File

@ -11,6 +11,7 @@
# limitations under the License.
import datetime
import itertools
import mock
from oslo_config import cfg
@ -267,6 +268,67 @@ class TestListAction(api_base.FunctionalTest):
response = self.get_json(url, expect_errors=True)
self.assertEqual(400, response.status_int)
def test_many_with_sort_key_uuid(self):
action_plan = obj_utils.create_test_action_plan(
self.context,
uuid=utils.generate_uuid(),
audit_id=self.audit.id)
actions_list = []
for id_ in range(1, 3):
action = obj_utils.create_test_action(
self.context, id=id_,
action_plan_id=action_plan.id,
uuid=utils.generate_uuid())
actions_list.append(action)
response = self.get_json('/actions?sort_key=%s' % 'uuid')
names = [s['uuid'] for s in response['actions']]
self.assertEqual(
sorted([a.uuid for a in actions_list]),
names)
def test_many_with_sort_key_action_plan_uuid(self):
action_plan_1 = obj_utils.create_test_action_plan(
self.context,
uuid=utils.generate_uuid(),
audit_id=self.audit.id)
action_plan_2 = obj_utils.create_test_action_plan(
self.context,
uuid=utils.generate_uuid(),
audit_id=self.audit.id)
action_plans_uuid_list = []
for id_, action_plan_id in enumerate(itertools.chain.from_iterable([
itertools.repeat(action_plan_1.id, 3),
itertools.repeat(action_plan_2.id, 2)]), 1):
action = obj_utils.create_test_action(
self.context, id=id_,
action_plan_id=action_plan_id,
uuid=utils.generate_uuid())
action_plans_uuid_list.append(action.action_plan.uuid)
for direction in ['asc', 'desc']:
response = self.get_json(
'/actions?sort_key={0}&sort_dir={1}'
.format('action_plan_uuid', direction))
action_plan_uuids = \
[s['action_plan_uuid'] for s in response['actions']]
self.assertEqual(
sorted(action_plans_uuid_list, reverse=(direction == 'desc')),
action_plan_uuids,
message='Failed on %s direction' % direction)
def test_sort_key_validation(self):
response = self.get_json(
'/actions?sort_key=%s' % 'bad_name',
expect_errors=True)
self.assertEqual(400, response.status_int)
def test_many_with_soft_deleted_action_plan_uuid(self):
action_plan1 = obj_utils.create_test_action_plan(
self.context,

View File

@ -256,6 +256,12 @@ class TestListActionPlan(api_base.FunctionalTest):
uuids = [s['audit_uuid'] for s in response['action_plans']]
self.assertEqual(sorted(audit_list), uuids)
def test_sort_key_validation(self):
response = self.get_json(
'/action_plans?sort_key=%s' % 'bad_name',
expect_errors=True)
self.assertEqual(400, response.status_int)
def test_links(self):
uuid = utils.generate_uuid()
obj_utils.create_test_action_plan(self.context, id=1, uuid=uuid)

View File

@ -294,6 +294,50 @@ class TestListAuditTemplate(FunctionalTestWithSetup):
'/audit_templates?strategy=%s' % self.fake_strategy2.name)
self.assertEqual(2, len(response['audit_templates']))
def test_many_with_sort_key_name(self):
audit_template_list = []
for id_ in range(1, 6):
audit_template = obj_utils.create_test_audit_template(
self.context, id=id_, uuid=utils.generate_uuid(),
name='My Audit Template {0}'.format(id_))
audit_template_list.append(audit_template)
response = self.get_json('/audit_templates?sort_key=%s' % 'name')
names = [s['name'] for s in response['audit_templates']]
self.assertEqual(
sorted([at.name for at in audit_template_list]),
names)
def test_many_with_sort_key_goal_name(self):
goal_names_list = []
for id_, goal_id in enumerate(itertools.chain.from_iterable([
itertools.repeat(self.fake_goal1.id, 3),
itertools.repeat(self.fake_goal2.id, 2)]), 1):
audit_template = obj_utils.create_test_audit_template(
self.context, id=id_, uuid=utils.generate_uuid(),
name='My Audit Template {0}'.format(id_),
goal_id=goal_id)
goal_names_list.append(audit_template.goal.name)
for direction in ['asc', 'desc']:
response = self.get_json(
'/audit_templates?sort_key={0}&sort_dir={1}'
.format('goal_name', direction))
goal_names = [s['goal_name'] for s in response['audit_templates']]
self.assertEqual(
sorted(goal_names_list, reverse=(direction == 'desc')),
goal_names)
def test_sort_key_validation(self):
response = self.get_json(
'/audit_templates?sort_key=%s' % 'goal_bad_name',
expect_errors=True)
self.assertEqual(400, response.status_int)
class TestPatch(FunctionalTestWithSetup):

View File

@ -217,6 +217,12 @@ class TestListAudit(api_base.FunctionalTest):
uuids = [s['goal_uuid'] for s in response['audits']]
self.assertEqual(sorted(goal_list), uuids)
def test_sort_key_validation(self):
response = self.get_json(
'/audits?sort_key=%s' % 'bad_name',
expect_errors=True)
self.assertEqual(400, response.status_int)
def test_links(self):
uuid = utils.generate_uuid()
obj_utils.create_test_audit(

View File

@ -120,6 +120,27 @@ class TestListGoal(api_base.FunctionalTest):
response = self.get_json('/goals')
self.assertEqual(3, len(response['goals']))
def test_many_with_sort_key_uuid(self):
goal_list = []
for idx in range(1, 6):
goal = obj_utils.create_test_goal(
self.context, id=idx,
uuid=utils.generate_uuid(),
name='GOAL_{0}'.format(idx))
goal_list.append(goal.uuid)
response = self.get_json('/goals/?sort_key=uuid')
self.assertEqual(5, len(response['goals']))
uuids = [s['uuid'] for s in response['goals']]
self.assertEqual(sorted(goal_list), uuids)
def test_sort_key_validation(self):
response = self.get_json(
'/goals?sort_key=%s' % 'bad_name',
expect_errors=True)
self.assertEqual(400, response.status_int)
class TestGoalPolicyEnforcement(api_base.FunctionalTest):

View File

@ -113,6 +113,26 @@ class TestListScoringEngine(api_base.FunctionalTest):
response = self.get_json('/scoring_engines')
self.assertEqual(3, len(response['scoring_engines']))
def test_many_with_sort_key_uuid(self):
scoring_engine_list = []
for idx in range(1, 6):
scoring_engine = obj_utils.create_test_scoring_engine(
self.context, id=idx, uuid=utils.generate_uuid(),
name=str(idx), description='SE_{0}'.format(idx))
scoring_engine_list.append(scoring_engine.uuid)
response = self.get_json('/scoring_engines/?sort_key=uuid')
self.assertEqual(5, len(response['scoring_engines']))
uuids = [s['uuid'] for s in response['scoring_engines']]
self.assertEqual(sorted(scoring_engine_list), uuids)
def test_sort_key_validation(self):
response = self.get_json(
'/goals?sort_key=%s' % 'bad_name',
expect_errors=True)
self.assertEqual(400, response.status_int)
class TestScoringEnginePolicyEnforcement(api_base.FunctionalTest):

View File

@ -131,6 +131,26 @@ class TestListService(api_base.FunctionalTest):
response = self.get_json('/services')
self.assertEqual(3, len(response['services']))
def test_many_with_sort_key_name(self):
service_list = []
for id_ in range(1, 4):
service = obj_utils.create_test_service(
self.context, id=id_, host='CONTROLLER',
name='SERVICE_{0}'.format(id_))
service_list.append(service.name)
response = self.get_json('/services/?sort_key=name')
self.assertEqual(3, len(response['services']))
names = [s['name'] for s in response['services']]
self.assertEqual(sorted(service_list), names)
def test_sort_key_validation(self):
response = self.get_json(
'/services?sort_key=%s' % 'bad_name',
expect_errors=True)
self.assertEqual(400, response.status_int)
class TestServicePolicyEnforcement(api_base.FunctionalTest):

View File

@ -221,6 +221,27 @@ class TestListStrategy(api_base.FunctionalTest):
for strategy in strategies:
self.assertEqual(goal1['uuid'], strategy['goal_uuid'])
def test_many_with_sort_key_goal_uuid(self):
goals_uuid_list = []
for idx in range(1, 6):
strategy = obj_utils.create_test_strategy(
self.context, id=idx,
uuid=utils.generate_uuid(),
name='STRATEGY_{0}'.format(idx))
goals_uuid_list.append(strategy.goal.uuid)
response = self.get_json('/strategies/?sort_key=goal_uuid')
self.assertEqual(5, len(response['strategies']))
goal_uuids = [s['goal_uuid'] for s in response['strategies']]
self.assertEqual(sorted(goals_uuid_list), goal_uuids)
def test_sort_key_validation(self):
response = self.get_json(
'/strategies?sort_key=%s' % 'bad_name',
expect_errors=True)
self.assertEqual(400, response.status_int)
class TestStrategyPolicyEnforcement(api_base.FunctionalTest):