Pass context down to ViewBuilder method

The versioned_method decorator helps add/remove fields in
API responses.

The method reads the request object but does not pass down
the context to the called methods.

The context would be helpful to make decisions regarding
whether the fields are specific to an administrator context
or owner/tenant context.

Change-Id: Ic2e2df305362fa3f353fa2dfd75203afc38afb82
Closes-Bug: #1589516
This commit is contained in:
Goutham Pacha Ravi 2016-06-06 12:00:50 -04:00
parent 05bdaa294f
commit adc958f253
8 changed files with 118 additions and 22 deletions

View File

@ -273,7 +273,8 @@ class ViewBuilder(object):
for method_name in self._detail_version_modifiers:
method = getattr(self, method_name)
if request.api_version_request.matches_versioned_method(method):
method.func(self, resource_dict, resource)
request_context = request.environ['manila.context']
method.func(self, request_context, resource_dict, resource)
@classmethod
def versioned_method(cls, min_ver, max_ver=None, experimental=False):

View File

@ -77,6 +77,7 @@ class ViewBuilder(common.ViewBuilder):
detail=False)
@common.ViewBuilder.versioned_method('2.14')
def add_preferred_path_attribute(self, view_dict, export_location):
def add_preferred_path_attribute(self, context, view_dict,
export_location):
view_dict['preferred'] = strutils.bool_from_string(
export_location['el_metadata'].get('preferred'))

View File

@ -63,16 +63,18 @@ class ViewBuilder(common.ViewBuilder):
return instances_dict
@common.ViewBuilder.versioned_method("2.9")
def remove_export_locations(self, share_instance_dict, share_instance):
def remove_export_locations(self, context, share_instance_dict,
share_instance):
share_instance_dict.pop('export_location')
share_instance_dict.pop('export_locations')
@common.ViewBuilder.versioned_method("2.10")
def add_access_rules_status_field(self, instance_dict, share_instance):
def add_access_rules_status_field(self, context, instance_dict,
share_instance):
instance_dict['access_rules_status'] = (
share_instance.get('access_rules_status')
)
@common.ViewBuilder.versioned_method("2.11")
def add_replication_fields(self, instance_dict, share_instance):
def add_replication_fields(self, context, instance_dict, share_instance):
instance_dict['replica_state'] = share_instance.get('replica_state')

View File

@ -57,17 +57,16 @@ class ViewBuilder(common.ViewBuilder):
'links': self._get_links(request, snapshot['id']),
}
# NOTE(xyang): Only retrieve provider_location for admin.
context = request.environ['manila.context']
if context.is_admin:
self.update_versioned_resource_dict(request, snapshot_dict,
snapshot)
self.update_versioned_resource_dict(request, snapshot_dict, snapshot)
return {'snapshot': snapshot_dict}
@common.ViewBuilder.versioned_method("2.12")
def add_provider_location_field(self, snapshot_dict, snapshot):
snapshot_dict['provider_location'] = snapshot.get('provider_location')
def add_provider_location_field(self, context, snapshot_dict, snapshot):
# NOTE(xyang): Only retrieve provider_location for admin.
if context.is_admin:
snapshot_dict['provider_location'] = snapshot.get(
'provider_location')
def _list_view(self, func, request, snapshots):
"""Provide a view for a list of share snapshots."""

View File

@ -104,22 +104,22 @@ class ViewBuilder(common.ViewBuilder):
return result
@common.ViewBuilder.versioned_method("2.2")
def add_snapshot_support_field(self, share_dict, share):
def add_snapshot_support_field(self, context, share_dict, share):
share_dict['snapshot_support'] = share.get('snapshot_support')
@common.ViewBuilder.versioned_method("2.4")
def add_consistency_group_fields(self, share_dict, share):
def add_consistency_group_fields(self, context, share_dict, share):
share_dict['consistency_group_id'] = share.get(
'consistency_group_id')
share_dict['source_cgsnapshot_member_id'] = share.get(
'source_cgsnapshot_member_id')
@common.ViewBuilder.versioned_method("2.5")
def add_task_state_field(self, share_dict, share):
def add_task_state_field(self, context, share_dict, share):
share_dict['task_state'] = share.get('task_state')
@common.ViewBuilder.versioned_method("2.6")
def modify_share_type_field(self, share_dict, share):
def modify_share_type_field(self, context, share_dict, share):
share_type = share.get('share_type_id')
share_type_name = None
@ -132,21 +132,21 @@ class ViewBuilder(common.ViewBuilder):
})
@common.ViewBuilder.versioned_method("2.9")
def remove_export_locations(self, share_dict, share):
def remove_export_locations(self, context, share_dict, share):
share_dict.pop('export_location')
share_dict.pop('export_locations')
@common.ViewBuilder.versioned_method("2.10")
def add_access_rules_status_field(self, share_dict, share):
def add_access_rules_status_field(self, context, share_dict, share):
share_dict['access_rules_status'] = share.get('access_rules_status')
@common.ViewBuilder.versioned_method('2.11')
def add_replication_fields(self, share_dict, share):
def add_replication_fields(self, context, share_dict, share):
share_dict['replication_type'] = share.get('replication_type')
share_dict['has_replicas'] = share['has_replicas']
@common.ViewBuilder.versioned_method("2.16")
def add_user_id(self, share_dict, share):
def add_user_id(self, context, share_dict, share):
share_dict['user_id'] = share.get('user_id')
def _list_view(self, func, request, shares):

View File

@ -53,12 +53,14 @@ class ViewBuilder(common.ViewBuilder):
return dict(volume_type=trimmed, share_type=trimmed)
@common.ViewBuilder.versioned_method("2.7")
def add_is_public_attr_core_api_like(self, share_type_dict, share_type):
def add_is_public_attr_core_api_like(self, context, share_type_dict,
share_type):
share_type_dict['share_type_access:is_public'] = share_type.get(
'is_public', True)
@common.ViewBuilder.versioned_method("1.0", "2.6")
def add_is_public_attr_extension_like(self, share_type_dict, share_type):
def add_is_public_attr_extension_like(self, context, share_type_dict,
share_type):
share_type_dict['os-share-type-access:is_public'] = share_type.get(
'is_public', True)

View File

@ -21,6 +21,7 @@ import webob
import webob.dec
import webob.request
from manila.api import common as api_common
from manila.api.middleware import auth
from manila.api.middleware import fault
from manila.api.openstack import api_version_request as api_version
@ -282,3 +283,35 @@ def mock_fake_admin_check(context, resource_name, action, *args, **kwargs):
return
else:
raise exception.PolicyNotAuthorized(action=action)
class FakeResourceViewBuilder(api_common.ViewBuilder):
_collection_name = 'fake_resource'
_detail_version_modifiers = [
"add_field_xyzzy",
"add_field_spoon_for_admins",
"remove_field_foo",
]
def view(self, req, resource):
keys = ('id', 'foo', 'fred', 'alice')
resource_dict = {key: resource.get(key) for key in keys}
self.update_versioned_resource_dict(req, resource_dict, resource)
return resource_dict
@api_common.ViewBuilder.versioned_method("1.41")
def add_field_xyzzy(self, context, resource_dict, resource):
resource_dict['xyzzy'] = resource.get('xyzzy')
@api_common.ViewBuilder.versioned_method("1.6")
def add_field_spoon_for_admins(self, context, resource_dict, resource):
if context.is_admin:
resource_dict['spoon'] = resource.get('spoon')
@api_common.ViewBuilder.versioned_method("3.14")
def remove_field_foo(self, context, resource_dict, resource):
resource_dict.pop('foo', None)

View File

@ -17,11 +17,14 @@
Test suites for 'common' code used throughout the OpenStack HTTP API.
"""
import ddt
import webob
import webob.exc
from manila.api import common
from manila import test
from manila.tests.api import fakes
from manila.tests.db import fakes as db_fakes
NS = "{http://docs.openstack.org/compute/api/v1.1}"
@ -240,3 +243,58 @@ class MiscFunctionsTest(test.TestCase):
self.assertRaises(ValueError,
common.remove_version_from_href,
fixture)
@ddt.ddt
class ViewBuilderTest(test.TestCase):
def setUp(self):
super(ViewBuilderTest, self).setUp()
self.expected_resource_dict = {
'id': 'fake_resource_id',
'foo': 'quz',
'fred': 'bob',
'alice': 'waldo',
'spoon': 'spam',
'xyzzy': 'qwerty',
}
self.fake_resource = db_fakes.FakeModel(self.expected_resource_dict)
self.view_builder = fakes.FakeResourceViewBuilder()
@ddt.data('1.0', '1.40')
def test_versioned_method_no_updates(self, version):
req = fakes.HTTPRequest.blank('/my_resource', version=version)
actual_resource = self.view_builder.view(req, self.fake_resource)
self.assertEqual(set({'id', 'foo', 'fred', 'alice'}),
set(actual_resource.keys()))
@ddt.data(True, False)
def test_versioned_method_v1_6(self, is_admin):
req = fakes.HTTPRequest.blank('/my_resource', version='1.6',
use_admin_context=is_admin)
expected_keys = set({'id', 'foo', 'fred', 'alice'})
if is_admin:
expected_keys.add('spoon')
actual_resource = self.view_builder.view(req, self.fake_resource)
self.assertEqual(expected_keys, set(actual_resource.keys()))
@ddt.unpack
@ddt.data({'is_admin': True, 'version': '3.14'},
{'is_admin': False, 'version': '3.14'},
{'is_admin': False, 'version': '6.2'},
{'is_admin': True, 'version': '6.2'})
def test_versioned_method_all_match(self, is_admin, version):
req = fakes.HTTPRequest.blank(
'/my_resource', version=version, use_admin_context=is_admin)
expected_keys = set({'id', 'fred', 'xyzzy', 'alice'})
if is_admin:
expected_keys.add('spoon')
actual_resource = self.view_builder.view(req, self.fake_resource)
self.assertEqual(expected_keys, set(actual_resource.keys()))