Add API prototype on Pecan

Add API prototype on Pecan.
Add functioanal tests.
Update documentation for running new API.
Delete old API and unit tests for it.

Change-Id: I42b3539d8420b2b5edf44d5265654b738e7ffa5e
This commit is contained in:
Vladislav Kuzmin 2014-12-26 14:07:32 +04:00 committed by sslypushenko
parent a11dcb8487
commit c76114ea23
11 changed files with 239 additions and 17 deletions

View File

@ -38,10 +38,7 @@ We use nginx and gunicorn, you may use something else if you so desire.
For the most basic setup that you can try right now, just kick off For the most basic setup that you can try right now, just kick off
gunicorn: gunicorn:
`gunicorn -b 0.0.0.0:8000 refstack.web:app` `gunicorn_pecan refstack/api/config.py`
To actually configure refstack, check out the config section and Now available http://localhost:8000/ with JSON response {'Root': 'OK'}
crack open refstack.cfg in your preffered editor. and http://localhost:8000/v1/results/ with JSON response {'Results': 'OK'}.
`vim refstack.cfg`
Now browse to http://localhost:8000

0
refstack/api/__init__.py Normal file
View File

21
refstack/tests/unit/tests.py → refstack/api/app.py Executable file → Normal file
View File

@ -1,5 +1,5 @@
# # Copyright (c) 2015 Mirantis, Inc.
# Copyright (c) 2014 Piston Cloud Computing, Inc. All Rights Reserved. # All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # 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 # not use this file except in compliance with the License. You may obtain
@ -12,15 +12,16 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
#
import unittest from pecan import make_app
class TestSequenceFunctions(unittest.TestCase): def setup_app(config):
def test_nothing(self): app_conf = dict(config.app)
# make sure the shuffled sequence does not lose any elements
pass
if __name__ == '__main__': return make_app(
unittest.main() app_conf.pop('root'),
logging=getattr(config, 'logging', {}),
**app_conf
)

63
refstack/api/config.py Normal file
View File

@ -0,0 +1,63 @@
# Copyright (c) 2015 Mirantis, Inc.
# All Rights Reserved.
#
# 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.
"""Configuration for running API.
Custom Configurations must be in Python dictionary format:
foo = {'bar':'baz'}
All configurations are accessible at:
pecan.conf
"""
# Server Specific Configurations
server = {
'port': '8000',
'host': '0.0.0.0'
}
# Pecan Application Configurations
app = {
'root': 'refstack.api.controllers.root.RootController',
'modules': ['refstack.api'],
'static_root': '%(confdir)s/public',
'template_path': '%(confdir)s/${package}/templates',
'debug': False,
'errors': {
'404': '/error/404',
'__force_dict__': True
}
}
logging = {
'loggers': {
'root': {'level': 'INFO', 'handlers': ['console']},
'refstack': {'level': 'DEBUG', 'handlers': ['console']}
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
}
},
'formatters': {
'simple': {
'format': ('%(asctime)s %(levelname)-5.5s [%(name)s]'
'[%(threadName)s] %(message)s')
}
}
}

View File

View File

@ -0,0 +1,28 @@
# Copyright (c) 2015 Mirantis, Inc.
# All Rights Reserved.
#
# 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.
from pecan import expose
from refstack.api.controllers import v1
class RootController(object):
v1 = v1.V1Controller()
@expose('json')
def index(self):
"""root response."""
return {'Root': 'OK'}

View File

@ -0,0 +1,32 @@
# Copyright (c) 2015 Mirantis, Inc.
# All Rights Reserved.
#
# 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.
"""Version 1 of the API.
"""
from pecan import expose
from pecan import rest
class ResultsController(rest.RestController):
@expose('json')
def index(self):
return {'Results': 'OK'}
class V1Controller(object):
"""Version 1 API controller root."""
results = ResultsController()

View File

@ -0,0 +1,72 @@
# Copyright (c) 2015 Mirantis, Inc.
# All Rights Reserved.
#
# 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.
"""Base classes for API tests.
"""
from unittest import TestCase
from pecan import set_config
from pecan.testing import load_test_app
class FunctionalTest(TestCase):
"""
Used for functional tests where you need to test your
literal application and its integration with the framework.
"""
def setUp(self):
self.config = {
'app': {
'root': 'refstack.api.controllers.root.RootController',
'modules': ['refstack.api'],
'static_root': '%(confdir)s/public',
'template_path': '%(confdir)s/${package}/templates',
}
}
self.app = load_test_app(self.config)
def tearDown(self):
set_config({}, overwrite=True)
def get_json(self, url, headers=None, extra_environ=None,
status=None, expect_errors=False, **params):
"""Sends HTTP GET request.
:param url: url path to target service
:param headers: a dictionary of extra headers to send
:param extra_environ: a dictionary of environmental variables that
should be added to the request
:param status: integer or string of the HTTP status code you expect
in response (if not 200 or 3xx). You can also use a
wildcard, like '3*' or '*'
:param expect_errors: boolean value, if this is False, then if
anything is written to environ wsgi.errors it
will be an error. If it is True, then
non-200/3xx responses are also okay
:param params: a query string, or a dictionary that will be encoded
into a query string. You may also include a URL query
string on the url
"""
response = self.app.get(url,
headers=headers,
extra_environ=extra_environ,
status=status,
expect_errors=expect_errors,
params=params)
if not expect_errors:
response = response.json
return response

View File

@ -0,0 +1,29 @@
# Copyright (c) 2015 Mirantis, Inc.
# All Rights Reserved.
#
# 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.
from refstack.tests import api
class TestRefStackApi(api.FunctionalTest):
def test_root_controller(self):
actual_response = self.get_json('/')
expected_response = {'Root': 'OK'}
self.assertEqual(expected_response, actual_response)
def test_results_controller(self):
actual_response = self.get_json('/v1/results/')
expected_response = {'Results': 'OK'}
self.assertEqual(expected_response, actual_response)

View File

@ -1 +0,0 @@
__author__ = 'dlenwell'

View File

@ -1,6 +1,7 @@
SQLAlchemy==0.8.3 SQLAlchemy==0.8.3
alembic==0.5.0 alembic==0.5.0
gunicorn==0.17.4 gunicorn==0.17.4
pecan>=0.8.2
pyOpenSSL==0.13 pyOpenSSL==0.13
pycrypto==2.6 pycrypto==2.6
requests==1.2.3 requests==1.2.3