First implementation of the Graffiti service

This is just to get something into github

Change-Id: I7d5d63e1d67ab89ed31831bba0fee34929413592
This commit is contained in:
Jeffrey J. Walls 2014-03-10 19:57:29 +00:00
parent fed799bd04
commit f68589b832
46 changed files with 476 additions and 0 deletions

1
service/MANIFEST.in Normal file
View File

@ -0,0 +1 @@
recursive-include public *

52
service/config.py Normal file
View File

@ -0,0 +1,52 @@
# Server Specific Configurations
server = {
'port': '21075',
'host': '0.0.0.0'
}
# Pecan Application Configurations
app = {
'root': 'graffiti.controllers.root.RootController',
'modules': ['graffiti'],
'static_root': '%(confdir)s/public',
'template_path': '%(confdir)s/graffiti/templates',
'debug': True,
'errors': {
404: '/error/404',
'__force_dict__': True
}
}
logging = {
'loggers': {
'root': {'level': 'DEBUG', 'handlers': ['console']},
'graffiti': {'level': 'DEBUG', 'handlers': ['console']},
'wsme.api': {'level': 'DEBUG', 'handlers': ['console']},
'py.warnings': {'handlers': ['console']},
'__force_dict__': True
},
'handlers': {
'console': {
'level': 'DEBUG',
'class': 'logging.StreamHandler',
'formatter': 'simple'
}
},
'formatters': {
'simple': {
'format': ('%(asctime)s %(levelname)-5.5s [%(name)s]'
'[%(threadName)s] %(message)s')
}
}
}
wsme = {
'debug': True
}
# Custom Configurations must be in Python dictionary format::
#
# foo = {'bar':'baz'}
#
# All configurations are accessible at::
# pecan.conf

View File

Binary file not shown.

26
service/graffiti/app.py Normal file
View File

@ -0,0 +1,26 @@
from pecan import make_app
from graffiti import model
from graffiti.service import prepare_service
from graffiti.hooks import CorsHook
from oslo.config import cfg
CONF = cfg.CONF
def setup_app(config):
model.init_model()
app_conf = dict(config.app)
prepare_service()
app_hooks = [CorsHook()]
return make_app(
app_conf.pop('root'),
logging=getattr(config, 'logging', {}),
hooks=app_hooks,
**app_conf
)

BIN
service/graffiti/app.pyc Normal file

Binary file not shown.

View File

Binary file not shown.

View File

@ -0,0 +1,22 @@
from pecan import expose
from webob.exc import status_map
from graffiti.controllers.versions import V1Controller
class RootController(object):
v1 = V1Controller()
@expose(generic=True, template='index.html')
def index(self):
return dict()
@expose('error.html')
def error(self, status):
try:
status = int(status)
except ValueError: # pragma: no cover
status = 500
message = getattr(status_map.get(status), 'explanation', '')
return dict(status=status, message=message)

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,41 @@
from pecan.rest import RestController
from wsme.api import Response
from wsmeext.pecan import wsexpose
from graffiti.model.v1.resource import Resource
resources = []
class ResourceController(RestController):
def __init__(self):
super(ResourceController, self).__init__()
self.status = 200
@wsexpose()
def options():
pass
@wsexpose(Resource, unicode)
def get_one(self, id):
global resources
for res in resources:
if res.id.lower() == id.lower():
return res
res = Response(Resource(), status_code=404, error="You Suck")
return res
@wsexpose([Resource])
def get_all(self):
global resources
return resources
@wsexpose(Resource, Resource)
def post(self, resource):
global resources
resources.append(resource)

Binary file not shown.

View File

@ -0,0 +1,5 @@
from graffiti.controllers.v1.resource import ResourceController
class V1Controller(object):
resource = ResourceController()

Binary file not shown.

16
service/graffiti/hooks.py Normal file
View File

@ -0,0 +1,16 @@
import json
from pecan.hooks import PecanHook
class CorsHook(PecanHook):
def after(self, state):
state.response.headers['Access-Control-Allow-Origin'] = '*'
state.response.headers['Access-Control-Allow-Methods'] = 'GET, PUT, POST, DELETE, OPTIONS'
state.response.headers['Access-Control-Allow-Headers'] = 'origin, authorization, accept, content-type'
if not state.response.headers['Content-Length']:
state.response.headers['Content-Length'] = str(len(state.response.body))
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)

BIN
service/graffiti/hooks.pyc Normal file

Binary file not shown.

View File

@ -0,0 +1,15 @@
from pecan import conf # noqa
def init_model():
"""
This is a stub method which is called at application startup time.
If you need to bind to a parse database configuration, set up tables or
ORM classes, or perform any database initialization, this is the
recommended place to do it.
For more information working with databases, and some common recipes,
see http://pecan.readthedocs.org/en/latest/databases.html
"""
pass

Binary file not shown.

View File

Binary file not shown.

View File

@ -0,0 +1,15 @@
import wsme
from wsme import types
from graffiti.model.v1.property import Property
class Capability(types.Base):
properties = wsme.wsattr([Property], mandatory=True)
capability_type = wsme.wsattr(types.text, mandatory=True)
capability_type_namespace = wsme.wsattr(types.text, mandatory=True)
_wsme_attr_order = ('properties', 'capability_type',
'capability_type_namespace')
def __init__(self, **kwargs):
super(Capability, self).__init__(**kwargs)

Binary file not shown.

View File

@ -0,0 +1,11 @@
import wsme
from wsme import types
class Property(types.Base):
name = wsme.wsattr(types.text, mandatory=True)
value = wsme.wsattr(types.text, mandatory=True)
_wsme_attr_order = ('name', 'value')
def __init__(self, **kwargs):
super(Property, self).__init__(**kwargs)

Binary file not shown.

View File

@ -0,0 +1,10 @@
import wsme
from wsme import types
class Provider(types.Base):
id = wsme.wsattr(types.text, mandatory=True)
_wsme_attr_order = ('id',)
def __init__(self, **kwargs):
super(Provider, self).__init__(**kwargs)

Binary file not shown.

View File

@ -0,0 +1,13 @@
import wsme
from wsme import types
class Requirement(types.Base):
criterion = wsme.wsattr(types.text, mandatory=True)
capability_type = wsme.wsattr(types.text, mandatory=True)
capability_type_namespace = wsme.wsattr(types.text, mandatory=True)
_wsme_attr_order = ('criterion', 'capability_type',
'capability_type_namespace')
def __init__(self, **kwargs):
super(Requirement, self).__init__(**kwargs)

Binary file not shown.

View File

@ -0,0 +1,25 @@
import wsme
from wsme import types
from graffiti.model.v1.capability import Capability
from graffiti.model.v1.property import Property
from graffiti.model.v1.provider import Provider
from graffiti.model.v1.requirement import Requirement
class Resource(types.Base):
id = wsme.wsattr(types.text, mandatory=True)
type = wsme.wsattr(types.text, mandatory=True)
name = wsme.wsattr(types.text, mandatory=True)
description = wsme.wsattr(types.text, mandatory=False)
provider = wsme.wsattr(Provider, mandatory=True)
properties = wsme.wsattr([Property], mandatory=False)
capabilities = wsme.wsattr([Capability], mandatory=True)
requirements = wsme.wsattr([Requirement], mandatory=True)
_wsme_attr_order = ('id', 'name', 'description', 'type',
'provider', 'properties', 'capabilities',
'requirements')
def __init__(self, **kwargs):
super(Resource, self).__init__(**kwargs)

Binary file not shown.

View File

@ -0,0 +1,9 @@
import sys
from oslo.config import cfg
def prepare_service(argv=None):
if argv is None:
argv = sys.argv
cfg.CONF(argv[3:], project='graffiti')

Binary file not shown.

View File

@ -0,0 +1,12 @@
<%inherit file="layout.html" />
## provide definitions for blocks we want to redefine
<%def name="title()">
Server Error ${status}
</%def>
## now define the body of the template
<header>
<h1>Server Error ${status}</h1>
</header>
<p>${message}</p>

View File

@ -0,0 +1,34 @@
<%inherit file="layout.html" />
## provide definitions for blocks we want to redefine
<%def name="title()">
Welcome to Pecan!
</%def>
## now define the body of the template
<header>
<h1><img src="/images/logo.png" /></h1>
</header>
<div id="content">
<p>This is a sample Pecan project.</p>
<p>
Instructions for getting started can be found online at <a
href="http://pecanpy.org" target="window">pecanpy.org</a>
</p>
<p>
...or you can search the documentation here:
</p>
<form method="POST" action="/">
<fieldset>
<input name="q" />
<input type="submit" value="Search" />
<fieldset>
<small>Enter search terms or a module, class or function name.</small>
</form>
</div>

View File

@ -0,0 +1,22 @@
<html>
<head>
<title>${self.title()}</title>
${self.style()}
${self.javascript()}
</head>
<body>
${self.body()}
</body>
</html>
<%def name="title()">
Default Title
</%def>
<%def name="style()">
<link rel="stylesheet" type="text/css" media="screen" href="/css/style.css" />
</%def>
<%def name="javascript()">
<script language="text/javascript" src="/javascript/shared.js"></script>
</%def>

View File

@ -0,0 +1,22 @@
import os
from unittest import TestCase
from pecan import set_config
from pecan.testing import load_test_app
__all__ = ['FunctionalTest']
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.app = load_test_app(os.path.join(
os.path.dirname(__file__),
'config.py'
))
def tearDown(self):
set_config({}, overwrite=True)

View File

@ -0,0 +1,25 @@
# Server Specific Configurations
server = {
'port': '8080',
'host': '0.0.0.0'
}
# Pecan Application Configurations
app = {
'root': 'graffiti.controllers.root.RootController',
'modules': ['graffiti'],
'static_root': '%(confdir)s/../../public',
'template_path': '%(confdir)s/../templates',
'debug': True,
'errors': {
'404': '/error/404',
'__force_dict__': True
}
}
# Custom Configurations must be in Python dictionary format::
#
# foo = {'bar':'baz'}
#
# All configurations are accessible at::
# pecan.conf

View File

@ -0,0 +1,22 @@
from unittest import TestCase
from webtest import TestApp
from graffiti.tests import FunctionalTest
class TestRootController(FunctionalTest):
def test_get(self):
response = self.app.get('/')
assert response.status_int == 200
def test_search(self):
response = self.app.post('/', params={'q': 'RestController'})
assert response.status_int == 302
assert response.headers['Location'] == (
'http://pecan.readthedocs.org/en/latest/search.html'
'?q=RestController'
)
def test_get_not_found(self):
response = self.app.get('/a/bogus/url', expect_errors=True)
assert response.status_int == 404

View File

@ -0,0 +1,7 @@
from unittest import TestCase
class TestUnits(TestCase):
def test_units(self):
assert 5 * 5 == 25

Binary file not shown.

View File

@ -0,0 +1,43 @@
body {
background: #311F00;
color: white;
font-family: 'Helvetica Neue', 'Helvetica', 'Verdana', sans-serif;
padding: 1em 2em;
}
a {
color: #FAFF78;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
div#content {
width: 800px;
margin: 0 auto;
}
form {
margin: 0;
padding: 0;
border: 0;
}
fieldset {
border: 0;
}
input.error {
background: #FAFF78;
}
header {
text-align: center;
}
h1, h2, h3, h4, h5, h6 {
font-family: 'Futura-CondensedExtraBold', 'Futura', 'Helvetica', sans-serif;
text-transform: uppercase;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

6
service/setup.cfg Normal file
View File

@ -0,0 +1,6 @@
[nosetests]
match=^test
where=graffiti
nocapture=1
cover-package=graffiti
cover-erase=1

22
service/setup.py Normal file
View File

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
setup(
name='graffiti',
version='0.1',
description='',
author='',
author_email='',
install_requires=[
"pecan",
],
test_suite='graffiti',
zip_safe=False,
include_package_data=True,
packages=find_packages(exclude=['ez_setup'])
)