diff --git a/graffiti/api/controllers/v1/resource.py b/graffiti/api/controllers/v1/resource.py index 88d72ca..be49072 100644 --- a/graffiti/api/controllers/v1/resource.py +++ b/graffiti/api/controllers/v1/resource.py @@ -20,9 +20,24 @@ from wsmeext.pecan import wsexpose from graffiti.api.model.v1.resource import Resource +from graffiti.api.model.v1.resource_controller import LocalResourceController + +from graffiti.common.utils import _ + +from oslo.config import cfg + import six -resources = [] + +resource_controller_group = cfg.OptGroup('resource_controller') +resource_controller_opts = [ + cfg.StrOpt('type', + help=_("The resource controller plugin")) +] + +cfg.CONF.register_group(resource_controller_group) +cfg.CONF.register_opts(resource_controller_opts, + group=resource_controller_group) class ResourceController(RestController): @@ -31,29 +46,49 @@ class ResourceController(RestController): self.status = 200 + self._controller = self._load_controller('Local') + + def _load_controller(self, which_one): + controller_type = cfg.CONF.resource_controller.type + controller_type = controller_type if controller_type else 'Local' + + # TODO(lakshmi): Load the controller here + _controller = LocalResourceController() + + return _controller + @wsexpose() def options(): pass @wsexpose(Resource, six.text_type) def get_one(self, id): - global resources - - for res in resources: - if res.id.lower() == id.lower(): - return res + res = self._controller.get_resource(id) + if res: + return res res = Response(Resource(), status_code=404, error="Resource Not Found") return res - @wsexpose([Resource]) - def get_all(self): - global resources + @wsexpose([Resource], six.text_type) + def get_all(self, query_string=None): - return resources + res_list = self._controller.find_resources(query_string) + if res_list: + return res_list + + return [] + + @wsexpose(Resource, six.text_type, Resource) + def put(self, id, resource): + + self._controller.set_resource(id, resource_definition=resource) + + return resource @wsexpose(Resource, Resource) def post(self, resource): - global resources - resources.append(resource) + self._controller.set_resource(resource_definition=resource) + + return resource diff --git a/graffiti/api/hooks.py b/graffiti/api/hooks.py index 389836c..e31dced 100644 --- a/graffiti/api/hooks.py +++ b/graffiti/api/hooks.py @@ -13,7 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import json +#import json from pecan.hooks import PecanHook @@ -30,7 +30,8 @@ class CorsHook(PecanHook): state.response.headers['Content-Length'] = \ str(len(state.response.body)) - if state.response.headers['Content-Type'].find('json') != -1: + # TODO(lakshmi): this fails in Python 3.3, don't know why +# if state.response.headers['Content-Type'].find('json') != -1: # Sort the Response Body's JSON - json_str = json.loads(state.response.body) - state.response.body = json.dumps(json_str, sort_keys=True) +# json_str = json.loads(state.response.body) +# state.response.body = json.dumps(json_str, sort_keys=True) diff --git a/graffiti/api/model/v1/resource_controller.py b/graffiti/api/model/v1/resource_controller.py new file mode 100644 index 0000000..783fae4 --- /dev/null +++ b/graffiti/api/model/v1/resource_controller.py @@ -0,0 +1,60 @@ +# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. +# +# 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. + + +class ResourceControllerBase(object): + + def __init__(self, **kwargs): + super(ResourceControllerBase, self).__init__(**kwargs) + + self._type = 'None' + + def get_resource(self, id): + return None + + def find_resources(self, query_string): + return [] + + def set_resource(self, id=None, resource_definition=None): + pass + + +class LocalResourceController(ResourceControllerBase): + + def __init__(self, **kwargs): + super(LocalResourceController, self).__init__(**kwargs) + + self._type = 'LocalResourceController' + + self._resources = dict() + self._last_id = 0 + + def get_resource(self, id): + return self._resources[id] + + def find_resources(self, query_string): + return self._resources + + def set_resource(self, id=None, resource_definition=None): + if not id: + id = self._generate_id() + + self._resources[id] = resource_definition + + def _generate_id(self): + return_value = self._last_id + self._last_id += 1 + + return return_value diff --git a/graffiti/api/service.py b/graffiti/api/service.py index 1cacfe5..fb5010f 100644 --- a/graffiti/api/service.py +++ b/graffiti/api/service.py @@ -22,4 +22,10 @@ def prepare_service(argv=None): if argv is None: argv = sys.argv - cfg.CONF(argv[3:], project='graffiti') + # when running unit tests, argv is inaccessible for some unknown + # reason; need to revisit this logic again running under Apache2 + # TODO(lakshmi): figure this out + try: + cfg.CONF(argv[3:], project='graffiti') + except BaseException: + pass diff --git a/graffiti/api/tests/test_controller_v1.py b/graffiti/api/tests/test_controller_v1.py index 5dd8a54..a623f60 100644 --- a/graffiti/api/tests/test_controller_v1.py +++ b/graffiti/api/tests/test_controller_v1.py @@ -31,9 +31,7 @@ class TestControllerV1(base.TestCase): def test_v1_exists(self): root = RootController() self.assertIn(hasattr(root, 'v1'), [True]) - pass def test_v1_resource_exists(self): v1 = V1Controller() self.assertIn(hasattr(v1, 'resource'), [True]) - pass diff --git a/graffiti/api/tests/test_graffiti.py b/graffiti/api/tests/test_graffiti.py index f72381d..8aed47e 100644 --- a/graffiti/api/tests/test_graffiti.py +++ b/graffiti/api/tests/test_graffiti.py @@ -19,11 +19,78 @@ test_graffiti Tests for `graffiti` module. """ +import os + +import pecan +import pecan.testing + +from oslo.config import cfg from graffiti.api.tests import base class TestGraffiti(base.TestCase): - def test_something(self): - pass + PATH_PREFIX = '/v1' + + def setUp(self): + super(TestGraffiti, self).setUp() + self.app = self._make_app() + cfg.CONF.set_override(name='type', override='Local', + group='resource_controller') + + def _make_app(self): + root_dir = self.path_get() + self.config = { + 'app': { + 'root': 'graffiti.api.controllers.root.RootController', + 'modules': ['graffiti.api'], + 'template_path': '%s/graffiti/templates' % root_dir, + }, + } + + return pecan.testing.load_test_app(self.config) + + def tearDown(self): + super(TestGraffiti, self).tearDown() + pecan.set_config({}, overwrite=True) + + def path_get(self, project_file=None): + root = os.path.abspath(os.path.join(os.path.dirname(__file__), + '..', + '..', )) + if project_file: + return os.path.join(root, project_file) + else: + return root + + def get_json(self, path, expect_errors=False, headers=None, + extra_environ=None, q=[], **params): + full_path = self.PATH_PREFIX + path + query_params = {'q.field': [], + 'q.value': [], + 'q.op': [], } + for query in q: + for name in ['field', 'op', 'value']: + query_params['q.%s' % name].append(query.get(name, '')) + + all_params = {} + all_params.update(params) + if q: + all_params.update(query_params) + + response = self.app.get(full_path, + params=all_params, + headers=headers, + extra_environ=extra_environ, + expect_errors=expect_errors) + + if not expect_errors: + response = response + + return response + + def test_get_all(self): + response = self.get_json('/resource') + self.assertEqual(response.status_int, 200) + self.assertEqual(response.content_type, 'application/json') diff --git a/graffiti/common/__init__.py b/graffiti/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/graffiti/common/utils.py b/graffiti/common/utils.py new file mode 100644 index 0000000..06911c1 --- /dev/null +++ b/graffiti/common/utils.py @@ -0,0 +1,22 @@ +# Copyright (c) 2014 Hewlett-Packard Development Company, L.P. +# +# 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. + +# look at heat/openstack/common/gettextutils.py when we actually need +# to implement this method + + +# TODO(travis): need localization strategy +def _(msg): + return msg diff --git a/requirements.txt b/requirements.txt index 5a37141..ad31f5f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ pbr>=0.5.21,<1.0 Babel>=0.9.6 pecan>=0.4.4 WSME>=0.6 +oslo.config