From 8b9f9836f8aadf98ff3bcc88de5622903625e7a0 Mon Sep 17 00:00:00 2001 From: Bartosz Fic Date: Wed, 8 Oct 2014 10:47:31 +0200 Subject: [PATCH] GET property which name includes resource type prefix Currently GET call to API to retrieve property details ends with 404 error when property name includes resource type prefix. This patch extends show method to take filters as a parameter. If 'resource_type' is included in filters then the prefix of included resource type is removed from property name. This enables user to look for property name starting with prefix that comes from associated resource type. Change-Id: I3c4d96fbc9ce15016631017bf76089c338ac3cdc Closes-Bug: #1367564 DocImpact Co-Authored-By: Bartosz Fic Co-Authored-By: Pawel Koniszewski --- etc/policy.json | 1 + glance/api/authorization.py | 4 ++ glance/api/policy.py | 4 ++ glance/api/v2/metadef_properties.py | 25 ++++++++++++- glance/db/__init__.py | 13 +++++++ glance/domain/proxy.py | 4 ++ .../functional/v2/test_metadef_properties.py | 37 +++++++++++++++++-- .../tests/unit/v2/test_metadef_resources.py | 31 +++++++++++++++- 8 files changed, 113 insertions(+), 6 deletions(-) diff --git a/etc/policy.json b/etc/policy.json index e72363f6d5..325f00b21f 100644 --- a/etc/policy.json +++ b/etc/policy.json @@ -41,6 +41,7 @@ "add_metadef_object":"", "list_metadef_resource_types":"", + "get_metadef_resource_type":"", "add_metadef_resource_type_association":"", "get_metadef_property":"", diff --git a/glance/api/authorization.py b/glance/api/authorization.py index beef833dbf..c5eb4eb7bd 100644 --- a/glance/api/authorization.py +++ b/glance/api/authorization.py @@ -723,6 +723,10 @@ class MetadefResourceTypeRepoProxy( return [proxy_meta_resource_type(self.context, meta_resource_type) for meta_resource_type in meta_resource_types] + def get(self, *args, **kwargs): + meta_resource_type = self.meta_resource_type_repo.get(*args, **kwargs) + return proxy_meta_resource_type(self.context, meta_resource_type) + # Metadef namespace properties classes def is_namespace_property_mutable(context, namespace_property): diff --git a/glance/api/policy.py b/glance/api/policy.py index 98f5cc44e7..c2df6c1aa7 100644 --- a/glance/api/policy.py +++ b/glance/api/policy.py @@ -580,6 +580,10 @@ class MetadefResourceTypeRepoProxy( self.policy.enforce(self.context, 'list_metadef_resource_types', {}) return super(MetadefResourceTypeRepoProxy, self).list(*args, **kwargs) + def get(self, *args, **kwargs): + self.policy.enforce(self.context, 'get_metadef_resource_type', {}) + return super(MetadefResourceTypeRepoProxy, self).get(*args, **kwargs) + def add(self, resource_type): self.policy.enforce(self.context, 'add_metadef_resource_type_association', {}) diff --git a/glance/api/v2/metadef_properties.py b/glance/api/v2/metadef_properties.py index 5f2760695e..8bf79e3728 100644 --- a/glance/api/v2/metadef_properties.py +++ b/glance/api/v2/metadef_properties.py @@ -79,8 +79,24 @@ class NamespacePropertiesController(object): raise webob.exc.HTTPInternalServerError() return namespace_properties - def show(self, req, namespace, property_name): + def show(self, req, namespace, property_name, filters=None): try: + if filters and filters['resource_type']: + rs_repo = self.gateway.get_metadef_resource_type_repo( + req.context) + db_resource_type = rs_repo.get(filters['resource_type'], + namespace) + prefix = db_resource_type.prefix + if prefix and property_name.startswith(prefix): + property_name = property_name[len(prefix):] + else: + msg = (_("Property %(property_name)s does not start " + "with the expected resource type association " + "prefix of '%(prefix)s'.") + % {'property_name': property_name, + 'prefix': prefix}) + raise exception.NotFound(msg) + prop_repo = self.gateway.get_metadef_property_repo(req.context) db_property = prop_repo.get(namespace, property_name) property = self._to_model(db_property) @@ -185,6 +201,13 @@ class RequestDeserializer(wsgi.JSONRequestDeserializer): property_type = fromjson(PropertyType, body) return dict(property_type=property_type) + def show(self, request): + params = request.params.copy() + query_params = { + 'filters': params + } + return query_params + class ResponseSerializer(wsgi.JSONResponseSerializer): def __init__(self, schema=None): diff --git a/glance/db/__init__.py b/glance/db/__init__.py index f6e402b5ce..08b51785d3 100644 --- a/glance/db/__init__.py +++ b/glance/db/__init__.py @@ -628,6 +628,19 @@ class MetadefResourceTypeRepo(object): self._format_resource_type_to_db(resource_type) ) + def get(self, resource_type, namespace): + namespace_entity = self.meta_namespace_repo.get(namespace) + db_resource_type = ( + self.db_api. + metadef_resource_type_association_get( + self.context, + namespace, + resource_type + ) + ) + return self._format_resource_type_from_db(db_resource_type, + namespace_entity) + def list(self, filters=None): namespace = filters['namespace'] if namespace: diff --git a/glance/domain/proxy.py b/glance/domain/proxy.py index 3cdf35dc4f..dd8bdea32b 100644 --- a/glance/domain/proxy.py +++ b/glance/domain/proxy.py @@ -367,6 +367,10 @@ class MetadefResourceTypeRepo(object): self.base.add(self.resource_type_proxy_helper.unproxy( meta_resource_type)) + def get(self, *args, **kwargs): + resource_type = self.base.get(*args, **kwargs) + return self.resource_type_proxy_helper.proxy(resource_type) + def list(self, *args, **kwargs): resource_types = self.base.list(*args, **kwargs) return [self.resource_type_proxy_helper.proxy(resource_type) diff --git a/glance/tests/functional/v2/test_metadef_properties.py b/glance/tests/functional/v2/test_metadef_properties.py index 7983cf2532..b5e791b69a 100644 --- a/glance/tests/functional/v2/test_metadef_properties.py +++ b/glance/tests/functional/v2/test_metadef_properties.py @@ -55,15 +55,22 @@ class TestNamespaceProperties(functional.FunctionalTest): path = self._url('/v2/metadefs/namespaces') headers = self._headers({'content-type': 'application/json'}) namespace_name = 'MyNamespace' + resource_type_name = 'MyResourceType' + resource_type_prefix = 'MyPrefix' data = jsonutils.dumps({ "namespace": namespace_name, "display_name": "My User Friendly Namespace", "description": "My description", "visibility": "public", "protected": False, - "owner": "The Test Owner" - } - ) + "owner": "The Test Owner", + "resource_type_associations": [ + { + "name": resource_type_name, + "prefix": resource_type_prefix + } + ] + }) response = requests.post(path, headers=headers, data=data) self.assertEqual(201, response.status_code) @@ -105,6 +112,30 @@ class TestNamespaceProperties(functional.FunctionalTest): self.assertEqual(100, property_object['minimum']) self.assertEqual(30000369, property_object['maximum']) + # Get the property with specific resource type association + path = self._url('/v2/metadefs/namespaces/%s/properties/%s%s' % ( + namespace_name, property_name, '='.join(['?resource_type', + resource_type_name]))) + response = requests.get(path, headers=self._headers()) + self.assertEqual(404, response.status_code) + + # Get the property with prefix and specific resource type association + property_name_with_prefix = ''.join([resource_type_prefix, + property_name]) + path = self._url('/v2/metadefs/namespaces/%s/properties/%s%s' % ( + namespace_name, property_name_with_prefix, '='.join([ + '?resource_type', resource_type_name]))) + response = requests.get(path, headers=self._headers()) + self.assertEqual(200, response.status_code) + property_object = jsonutils.loads(response.text) + self.assertEqual("integer", property_object['type']) + self.assertEqual("property1", property_object['title']) + self.assertEqual("property1 description", property_object[ + 'description']) + self.assertEqual('100', property_object['default']) + self.assertEqual(100, property_object['minimum']) + self.assertEqual(30000369, property_object['maximum']) + # Returned property should match the created property property_object = jsonutils.loads(response.text) checked_keys = set([ diff --git a/glance/tests/unit/v2/test_metadef_resources.py b/glance/tests/unit/v2/test_metadef_resources.py index 9bd1599e50..2d82f22a34 100644 --- a/glance/tests/unit/v2/test_metadef_resources.py +++ b/glance/tests/unit/v2/test_metadef_resources.py @@ -38,6 +38,7 @@ NAMESPACE6 = 'Namespace6' PROPERTY1 = 'Property1' PROPERTY2 = 'Property2' PROPERTY3 = 'Property3' +PROPERTY4 = 'Property4' OBJECT1 = 'Object1' OBJECT2 = 'Object2' @@ -46,12 +47,15 @@ OBJECT3 = 'Object3' RESOURCE_TYPE1 = 'ResourceType1' RESOURCE_TYPE2 = 'ResourceType2' RESOURCE_TYPE3 = 'ResourceType3' +RESOURCE_TYPE4 = 'ResourceType4' TENANT1 = '6838eb7b-6ded-434a-882c-b344c77fe8df' TENANT2 = '2c014f32-55eb-467d-8fcb-4bd706012f81' TENANT3 = '5a3e60e8-cfa9-4a9e-a90a-62b42cea92b8' TENANT4 = 'c6c87f25-8a94-47ed-8c83-053c25f42df4' +PREFIX1 = 'pref' + def _db_namespace_fixture(namespace, **kwargs): obj = { @@ -146,6 +150,7 @@ class TestMetadefsControllers(base.IsolatedUnitTest): (NAMESPACE3, _db_property_fixture(PROPERTY1)), (NAMESPACE3, _db_property_fixture(PROPERTY2)), (NAMESPACE1, _db_property_fixture(PROPERTY1)), + (NAMESPACE6, _db_property_fixture(PROPERTY4)), ] [self.db.metadef_property_create(req.context, namespace, property) for namespace, property in self.properties] @@ -165,6 +170,7 @@ class TestMetadefsControllers(base.IsolatedUnitTest): self.resource_types = [ _db_resource_type_fixture(RESOURCE_TYPE1), _db_resource_type_fixture(RESOURCE_TYPE2), + _db_resource_type_fixture(RESOURCE_TYPE4), ] [self.db.metadef_resource_type_create(req.context, resource_type) for resource_type in self.resource_types] @@ -176,6 +182,8 @@ class TestMetadefsControllers(base.IsolatedUnitTest): (NAMESPACE3, _db_namespace_resource_type_fixture(RESOURCE_TYPE1)), (NAMESPACE2, _db_namespace_resource_type_fixture(RESOURCE_TYPE1)), (NAMESPACE2, _db_namespace_resource_type_fixture(RESOURCE_TYPE2)), + (NAMESPACE6, _db_namespace_resource_type_fixture(RESOURCE_TYPE4, + prefix=PREFIX1)), ] [self.db.metadef_resource_type_association_create(req.context, namespace, @@ -526,6 +534,25 @@ class TestMetadefsControllers(base.IsolatedUnitTest): output = self.property_controller.show(request, NAMESPACE3, PROPERTY1) self.assertEqual(output.name, PROPERTY1) + def test_property_show_specific_resource_type(self): + request = unit_test_utils.get_fake_request() + output = self.property_controller.show( + request, NAMESPACE6, ''.join([PREFIX1, PROPERTY4]), + filters={'resource_type': RESOURCE_TYPE4}) + self.assertEqual(output.name, PROPERTY4) + + def test_property_show_prefix_mismatch(self): + request = unit_test_utils.get_fake_request() + self.assertRaises(webob.exc.HTTPNotFound, + self.property_controller.show, request, NAMESPACE6, + PROPERTY4, filters={'resource_type': RESOURCE_TYPE4}) + + def test_property_show_non_existing_resource_type(self): + request = unit_test_utils.get_fake_request() + self.assertRaises(webob.exc.HTTPNotFound, + self.property_controller.show, request, NAMESPACE2, + PROPERTY1, filters={'resource_type': 'test'}) + def test_property_show_non_existing(self): request = unit_test_utils.get_fake_request() self.assertRaises(webob.exc.HTTPNotFound, @@ -958,10 +985,10 @@ class TestMetadefsControllers(base.IsolatedUnitTest): request = unit_test_utils.get_fake_request() output = self.rt_controller.index(request) - self.assertEqual(2, len(output.resource_types)) + self.assertEqual(3, len(output.resource_types)) actual = set([type.name for type in output.resource_types]) - expected = set([RESOURCE_TYPE1, RESOURCE_TYPE2]) + expected = set([RESOURCE_TYPE1, RESOURCE_TYPE2, RESOURCE_TYPE4]) self.assertEqual(actual, expected) def test_resource_type_show(self):