From 14f2b3e0c9fd1580d85a83b7eea95fbe893665ac Mon Sep 17 00:00:00 2001 From: Roman Prykhodchenko Date: Thu, 5 Mar 2015 12:11:59 +0100 Subject: [PATCH] Base API facade This patch adds base class for implementing facades that will provide access to appropriate API resources and encapsulate the implementation of the API wrapper. Blueprint: re-thinking-fuel-client Change-Id: I2a8b08204a7848dd99e3f2197be7e3fe9eb218dd --- fuelclient/__init__.py | 32 ++++++++++++++++++ fuelclient/tests/cli/test_v2_engine.py | 20 ++++++++++++ fuelclient/tests/lib/__init__.py | 0 fuelclient/tests/lib/test_api.py | 45 ++++++++++++++++++++++++++ fuelclient/v1/__init__.py | 18 +++++++++++ fuelclient/v1/base_v1.py | 35 ++++++++++++++++++++ 6 files changed, 150 insertions(+) create mode 100644 fuelclient/tests/lib/__init__.py create mode 100644 fuelclient/tests/lib/test_api.py create mode 100644 fuelclient/v1/base_v1.py diff --git a/fuelclient/__init__.py b/fuelclient/__init__.py index 63c217a3..93269c87 100644 --- a/fuelclient/__init__.py +++ b/fuelclient/__init__.py @@ -11,6 +11,9 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. + +from fuelclient import v1 + try: import pkg_resources try: @@ -20,3 +23,32 @@ try: __version__ = "" except ImportError: __version__ = "" + +version_map = { + 'v1': { + } +} + +def get_client(resource, version='v1'): + """Gets an API client for a resource + + python-fuelclient provides access to Fuel's API + through a set of per-resource facades. In order to + get a proper facade it's necessary to specify the name + of the API resource and the version of Fuel's API. + + :param resource: Name of the resource to get a facade for. + :type resource: str + Valid values are environment, node and task + :param version: Version of the Fuel's API + :type version: str, + Available: v1. Default: v1. + :return: Facade to the specified resource that wraps + calls to the specified version of the API. + + """ + try: + return version_map[version][resource].get_client() + except KeyError: + msg = 'Cannot load API client for "{r}" in the API version "{v}".' + raise ValueError(msg.format(r=resource, v=version)) diff --git a/fuelclient/tests/cli/test_v2_engine.py b/fuelclient/tests/cli/test_v2_engine.py index c643c8cd..9afc43e7 100644 --- a/fuelclient/tests/cli/test_v2_engine.py +++ b/fuelclient/tests/cli/test_v2_engine.py @@ -18,11 +18,31 @@ import sys import mock +import fuelclient from fuelclient import main as main_mod from fuelclient.tests import base class BaseCLITest(base.UnitTestCase): + """Base class for testing the new CLI + + It mocks the whole API layer in order to be sure the + tests test only the stuff which is responsible for + the command line application. That allows to find bugs + more precisely and not confuse them with the bugs in the + API wrapper. + + """ + def setUp(self): + self._get_client_patcher = mock.patch.object(fuelclient, 'get_client') + self.m_get_client = self._get_client_patcher.start() + + self.m_client = mock.MagicMock() + self.m_get_client.return_value = self.m_client + + def tearDown(self): + self._get_client_patcher.stop() + def exec_v2_command(self, *args, **kwargs): """Executes fuelclient with the specified arguments.""" diff --git a/fuelclient/tests/lib/__init__.py b/fuelclient/tests/lib/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/fuelclient/tests/lib/test_api.py b/fuelclient/tests/lib/test_api.py new file mode 100644 index 00000000..18d3d562 --- /dev/null +++ b/fuelclient/tests/lib/test_api.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# +# Copyright 2015 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 mock +from mock import patch +import requests_mock as rm +from six.moves.urllib import parse as urlparse + +from fuelclient import client +from fuelclient.tests import base + + +class BaseLibTest(base.UnitTestCase): + def setUp(self): + self.m_request = rm.Mocker() + self.m_request.start() + + self.session_adapter = self.m_request._adapter.register_uri(rm.ANY, + rm.ANY) + + self.api_client_patcher = patch.object(client.Client, + 'auth_required', + new_callable=mock.PropertyMock) + self.m_api_client = self.api_client_patcher.start() + self.m_api_client.return_value = False + + def tearDown(self): + self.m_request.stop() + self.api_client_patcher.stop() + + def get_object_uri(self, collection_path, object_id, attribute='/'): + return urlparse.urljoin(collection_path, str(object_id) + attribute) diff --git a/fuelclient/v1/__init__.py b/fuelclient/v1/__init__.py index e69de29b..9e85042b 100644 --- a/fuelclient/v1/__init__.py +++ b/fuelclient/v1/__init__.py @@ -0,0 +1,18 @@ +# Copyright 2015 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. + + + +__all__ = ( +) diff --git a/fuelclient/v1/base_v1.py b/fuelclient/v1/base_v1.py new file mode 100644 index 00000000..7705a86c --- /dev/null +++ b/fuelclient/v1/base_v1.py @@ -0,0 +1,35 @@ +# Copyright 2015 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 abc + +import six + + +@six.add_metaclass(abc.ABCMeta) +class BaseV1Client(object): + + @abc.abstractproperty + def _entity_wrapper(self): + pass + + def get_all(self): + result = self._entity_wrapper.get_all_data() + + return result + + def get_by_id(self, entity_id): + obj = self._entity_wrapper(obj_id=entity_id) + + return obj.data