From a250d8893f78bc0943276db762f48e3d61594f9f Mon Sep 17 00:00:00 2001 From: Brian Curtin Date: Fri, 14 Nov 2014 21:33:45 -0600 Subject: [PATCH] Build Resource from either existing object or id Allow Resource objects to be built from either an existing object, or be created from scratch with the given id. This is useful at the Proxy level when creating interfaces that must work with a Resource in the end, but will want to be able to either operate on string names or the Resources returned from other APIs. An example is that you may want to create an object in the object store given a previously obtained container object and a string name for the object. You may also have constructed the Resource for the object ahead of time and may want to pass that instead of a string name. Change-Id: I84c2101024849ceb78de677607f6a64bd8134ede --- openstack/resource.py | 16 ++++++++++++++++ openstack/tests/test_resource.py | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/openstack/resource.py b/openstack/resource.py index 3a66b273..a551cf62 100644 --- a/openstack/resource.py +++ b/openstack/resource.py @@ -230,6 +230,22 @@ class Resource(collections.MutableMapping): """ return cls(kwargs, loaded=True) + @classmethod + def from_id(cls, value): + """Create an instance from an ID or return an existing instance. + + Instance creation is done via cls.new. + """ + # This method is useful in the higher level, in cases where operations + # need to depend on having Resource objects, but the API is flexible + # in taking text values which represent those objects. + if isinstance(value, cls): + return value + elif isinstance(value, six.string_types): + return cls.new(**{cls.id_attribute: value}) + else: + raise ValueError("value must be %s instance or id" % cls.__name__) + ## # MUTABLE MAPPING IMPLEMENTATION ## diff --git a/openstack/tests/test_resource.py b/openstack/tests/test_resource.py index f8e673ec..60bf8e3b 100644 --- a/openstack/tests/test_resource.py +++ b/openstack/tests/test_resource.py @@ -370,6 +370,26 @@ class ResourceTests(base.TestTransportBase): del t.id self.assertTrue(Test.id_attribute not in t._attrs) + def test_from_id_with_name(self): + name = "Sandy Koufax" + + obj = FakeResource.from_id(name) + self.assertEqual(obj.id, name) + + def test_from_id_with_object(self): + name = "Mickey Mantle" + obj = FakeResource.new(name=name) + + new_obj = FakeResource.from_id(obj) + self.assertIs(new_obj, obj) + self.assertEqual(new_obj.name, obj.name) + + def test_from_id_with_bad_value(self): + def should_raise(): + FakeResource.from_id(3.14) + + self.assertThat(should_raise, matchers.raises(ValueError)) + class FakeResponse: def __init__(self, response):