Doug Hellmann b9bdddde51 remove or adopt incubated oslo code
Switch to oslo.i18n fully.

Move the old incubated apiclient package to storyboardclient._apiclient
to make it clear it is no longer maintained by the Oslo team.

Remove the configuration file for syncing from the Oslo incubator.

Update the flake8 and coverage settings to ignore the apiclient in its
new home until it is cleaned up and passes completely.

Story: 2000776
Change-Id: I017e965353a20e0af151f0db9dc0ea8da9ff4b2f
Signed-off-by: Doug Hellmann <doug@doughellmann.com>
2016-11-08 13:44:49 -05:00

226 lines
7.2 KiB
Python

# Copyright (c) 2014 Mirantis Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import inspect
import six
from storyboardclient._apiclient import base
from storyboardclient._apiclient import client
from storyboardclient.auth import oauth
DEFAULT_API_URL = "https://storyboard-dev.openstack.org/api/v1"
class BaseClient(client.BaseClient):
def __init__(self, api_url=None, access_token=None, verify=True):
if not api_url:
api_url = DEFAULT_API_URL
self.auth_plugin = oauth.OAuthPlugin(api_url, access_token)
self.http_client = BaseHTTPClient(auth_plugin=self.auth_plugin,
verify=verify)
class BaseHTTPClient(client.HTTPClient):
"""Base class for setting up endpoint and token.
This HTTP client is overriding a client_request method to add
Authorization header if OAuth token is provided.
"""
def client_request(self, client, method, url, **kwargs):
"""Send an http request using `client`'s endpoint and specified `url`.
If request was rejected as unauthorized (possibly because the token is
expired), issue one authorization attempt and send the request once
again.
:param client: instance of BaseClient descendant
:param method: method of HTTP request
:param url: URL of HTTP request
:param kwargs: any other parameter that can be passed to
`HTTPClient.request`
"""
token, endpoint = (self.cached_token, client.cached_endpoint)
if not (token and endpoint):
token, endpoint = self.auth_plugin.token_and_endpoint()
self.cached_token = token
client.cached_endpoint = endpoint
if token:
kwargs.setdefault("headers", {})["Authorization"] = \
"Bearer %s" % token
return self.request(method, self.concat_url(endpoint, url), **kwargs)
class BaseManager(base.CrudManager):
def build_url(self, base_url=None, method=None, **kwargs):
# Overriding to use "url_key" instead of the "collection_key".
# "key_id" is replaced with just "id" when querying a specific object.
url = base_url if base_url is not None else ''
url += '/%s' % self.url_key
entity_id = kwargs.get('id')
if entity_id is not None:
url += '/%s' % entity_id
elif method == 'get':
first = True
for key, value in kwargs.iteritems():
if first:
url += '?'
first = False
else:
url += '&'
url += '%s=%s' % (key, value)
return url
def get(self, id):
"""Get a resource by id.
Get method is accepting id as a positional argument for simplicity.
:param id: The id of resource.
:return: The resource object.
"""
query_kwargs = {"id": id}
return self._get(self.build_url(**query_kwargs), self.key)
def get_all(self, **kwargs):
"""Get resources by properties other than ID."""
kwargs = self._filter_kwargs(kwargs)
return self._get_all(self.build_url(method='get', **kwargs), self.key)
def _get_all(self, url, response_key=None):
"""Get collection of stuff.
Put here because we can't modify base.py in the OpenStack
APIclient (probably)
:param url: a partial URL, e.g., '/servers'
:param response_key: the key to be looked up in response dictionary,
e.g., 'server'. If response_key is None - all response body
will be used.
"""
body = self.client.get(url).json()
data = body[response_key] if response_key is not None else body
return [self.resource_class(self, item, loaded=True) for item in data]
def create(self, **kwargs):
"""Create a resource.
The default implementation is overridden so that the dictionary is
passed 'as is' without any wrapping.
"""
kwargs = self._filter_kwargs(kwargs)
return self._post(self.build_url(**kwargs), kwargs)
def update(self, **kwargs):
"""Update a resource.
The default implementation is overridden so that the dictionary is
passed 'as is' without any wrapping. The id field is removed from the
request body.
"""
kwargs = self._filter_kwargs(kwargs)
params = kwargs.copy()
params.pop('id')
return self._put(self.build_url(**kwargs), params)
class BaseNestedManager(BaseManager):
def __init__(self, client, parent_id):
super(BaseNestedManager, self).__init__(client)
self.parent_id = parent_id
def build_url(self, base_url=None, **kwargs):
# Overriding to use "url_key" instead of the "collection_key".
# "key_id" is replaced with just "id" when querying a specific object.
url = base_url if base_url is not None else ''
url += '/%s/%s/%s' % (self.parent_url_key, self.parent_id,
self.url_key)
entity_id = kwargs.get('id')
if entity_id is not None:
url += '/%s' % entity_id
return url
class BaseObject(base.Resource):
id = None
created_at = None
updated_at = None
def __init__(self, manager, info, loaded=False, parent_id=None):
super(BaseObject, self).__init__(manager, info, loaded)
self._parent_id = parent_id
self._init_nested_managers()
def _add_details(self, info):
for field, value in six.iteritems(info):
# Skip the fields which are not declared in the object
if not hasattr(self, field):
continue
setattr(self, field, value)
def _init_nested_managers(self):
# If an object has nested resource managers, they will be initialized
# here.
manager_instances = {}
for manager_name, manager_class in self._managers():
manager_instance = manager_class(client=self.manager.client,
parent_id=self.id)
# Saving a manager to a dict as self.__dict__ should not be
# changed while iterating
manager_instances[manager_name] = manager_instance
for name, manager_instance in six.iteritems(manager_instances):
# replacing managers declarations with real managers
setattr(self, name, manager_instance)
def _managers(self):
# Iterator over nested managers
for attr in dir(self):
# Skip private fields
if attr.startswith("_"):
continue
val = getattr(self, attr)
if inspect.isclass(val) and issubclass(val, BaseNestedManager):
yield attr, val