Reorganize url creation.

Make build_url extract used parameters from keyword arguments so they
are not sent as the body to create, or as query parameters.
Allow specifying a class level base_url that is used unless one is
specifically provided.
Break filtering function into a decorator as it seems the perfect
usecase and it prevents a dictionary copy.

Fixes: bug 1198772
Change-Id: I6d370ed504c300b9997199f351322e3083650c69
This commit is contained in:
Jamie Lennox
2013-07-08 12:00:37 +10:00
committed by Jamie Lennox
parent c74401ab56
commit 50e405dfe6

View File

@@ -19,6 +19,7 @@ Base utilities to build API operation managers and objects on top of.
""" """
import abc import abc
import functools
import urllib import urllib
from keystoneclient import exceptions from keystoneclient import exceptions
@@ -50,6 +51,29 @@ def getid(obj):
return obj return obj
def filter_kwargs(f):
@functools.wraps(f)
def func(*args, **kwargs):
for key, ref in kwargs.items():
if ref is None:
# drop null values
del kwargs[key]
continue
id_value = getid(ref)
if id_value == ref:
continue
# if an object with an id was passed remove the object
# from params and replace it with just the id.
# e.g user: User(id=1) becomes user_id: 1
del kwargs[key]
kwargs['%s_id' % key] = id_value
return f(*args, **kwargs)
return func
class Manager(object): class Manager(object):
""" """
Managers interact with a particular type of API (servers, flavors, images, Managers interact with a particular type of API (servers, flavors, images,
@@ -178,8 +202,9 @@ class CrudManager(Manager):
""" """
collection_key = None collection_key = None
key = None key = None
base_url = None
def build_url(self, base_url=None, **kwargs): def build_url(self, dict_args_in_out=None):
"""Builds a resource URL for the given kwargs. """Builds a resource URL for the given kwargs.
Given an example collection where `collection_key = 'entities'` and Given an example collection where `collection_key = 'entities'` and
@@ -197,89 +222,79 @@ class CrudManager(Manager):
If a `base_url` is provided, the generated URL will be appended to it. If a `base_url` is provided, the generated URL will be appended to it.
""" """
url = base_url if base_url is not None else '' if dict_args_in_out is None:
dict_args_in_out = {}
url = dict_args_in_out.pop('base_url', None) or self.base_url or ''
url += '/%s' % self.collection_key url += '/%s' % self.collection_key
# do we have a specific entity? # do we have a specific entity?
entity_id = kwargs.get('%s_id' % self.key) entity_id = dict_args_in_out.pop('%s_id' % self.key, None)
if entity_id is not None: if entity_id is not None:
url += '/%s' % entity_id url += '/%s' % entity_id
return url return url
def _filter_kwargs(self, kwargs): @filter_kwargs
# drop null values
for key, ref in kwargs.copy().iteritems():
if ref is None:
kwargs.pop(key)
else:
id_value = getid(ref)
if id_value != ref:
kwargs.pop(key)
kwargs['%s_id' % key] = id_value
return kwargs
def create(self, **kwargs): def create(self, **kwargs):
kwargs = self._filter_kwargs(kwargs) url = self.build_url(dict_args_in_out=kwargs)
return self._create( return self._create(
self.build_url(**kwargs), url,
{self.key: kwargs}, {self.key: kwargs},
self.key) self.key)
@filter_kwargs
def get(self, **kwargs): def get(self, **kwargs):
kwargs = self._filter_kwargs(kwargs)
return self._get( return self._get(
self.build_url(**kwargs), self.build_url(dict_args_in_out=kwargs),
self.key) self.key)
@filter_kwargs
def head(self, **kwargs): def head(self, **kwargs):
kwargs = self._filter_kwargs(kwargs) return self._head(self.build_url(dict_args_in_out=kwargs))
return self._head(self.build_url(**kwargs))
def list(self, base_url=None, **kwargs): @filter_kwargs
kwargs = self._filter_kwargs(kwargs) def list(self, **kwargs):
url = self.build_url(dict_args_in_out=kwargs)
return self._list( return self._list(
'%(base_url)s%(query)s' % { '%(url)s%(query)s' % {
'base_url': self.build_url(base_url=base_url, **kwargs), 'url': url,
'query': '?%s' % urllib.urlencode(kwargs) if kwargs else '', 'query': '?%s' % urllib.urlencode(kwargs) if kwargs else '',
}, },
self.collection_key) self.collection_key)
def put(self, base_url=None, **kwargs): @filter_kwargs
kwargs = self._filter_kwargs(kwargs) def put(self, **kwargs):
return self._update( return self._update(
self.build_url(base_url=base_url, **kwargs), self.build_url(dict_args_in_out=kwargs),
method='PUT') method='PUT')
@filter_kwargs
def update(self, **kwargs): def update(self, **kwargs):
kwargs = self._filter_kwargs(kwargs) url = self.build_url(dict_args_in_out=kwargs)
params = kwargs.copy()
params.pop('%s_id' % self.key)
return self._update( return self._update(
self.build_url(**kwargs), url,
{self.key: params}, {self.key: kwargs},
self.key, self.key,
method='PATCH') method='PATCH')
@filter_kwargs
def delete(self, **kwargs): def delete(self, **kwargs):
kwargs = self._filter_kwargs(kwargs)
return self._delete( return self._delete(
self.build_url(**kwargs)) self.build_url(dict_args_in_out=kwargs))
def find(self, base_url=None, **kwargs): @filter_kwargs
def find(self, **kwargs):
""" """
Find a single item with attributes matching ``**kwargs``. Find a single item with attributes matching ``**kwargs``.
""" """
kwargs = self._filter_kwargs(kwargs) url = self.build_url(dict_args_in_out=kwargs)
rl = self._list( rl = self._list(
'%(base_url)s%(query)s' % { '%(url)s%(query)s' % {
'base_url': self.build_url(base_url=base_url, **kwargs), 'url': url,
'query': '?%s' % urllib.urlencode(kwargs) if kwargs else '', 'query': '?%s' % urllib.urlencode(kwargs) if kwargs else '',
}, },
self.collection_key) self.collection_key)