Added beginning of API layer required for PoC, plus unit tests

Change-Id: Ifb6586c97f5388471b4eb5012f729fdeab9cb174
This commit is contained in:
whisenhu 2014-03-17 17:19:16 +00:00
parent 31d937a62c
commit ba6696b111
9 changed files with 211 additions and 21 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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')

View File

22
graffiti/common/utils.py Normal file
View File

@ -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

View File

@ -2,3 +2,4 @@ pbr>=0.5.21,<1.0
Babel>=0.9.6
pecan>=0.4.4
WSME>=0.6
oslo.config