Commit 89864450 authored by Jacek Furmankiewicz's avatar Jacek Furmankiewicz

major change to enable custom routing to REST services from a single parent Resource

parent 39d43ed7
......@@ -2,6 +2,25 @@
Common classes
'''
from zope.interface import Interface, Attribute
#########################################################
#
# INTERFACES
#
#########################################################
class IRestServiceContainer(Interface):
"""An interface for all REST services that can be added within a root CorePost resource"""
services = Attribute("All the REST services contained in this resource")
#########################################################
#
# CLASSES
#
#########################################################
class Response:
"""
Custom response object, can be returned instead of raw string response
......
This diff is collapsed.
......@@ -3,7 +3,7 @@ Argument extraction tests
@author: jacekf
'''
from corepost.web import CorePost, validate, route
from corepost.web import RestServiceContainer, validate, route
from corepost.enums import Http
from formencode import Schema, validators
......@@ -11,7 +11,7 @@ class TestSchema(Schema):
allow_extra_fields = True
childId = validators.Regex(regex="^jacekf|test$")
class ArgumentApp(CorePost):
class ArgumentApp():
@route("/int/<int:intarg>/float/<float:floatarg>/string/<stringarg>",Http.GET)
def test(self,request,intarg,floatarg,stringarg,**kwargs):
......@@ -29,5 +29,5 @@ class ArgumentApp(CorePost):
return "%s - %s - %s" % (rootId,childId,kwargs)
def run_app_arguments():
app = ArgumentApp()
app = RestServiceContainer((ArgumentApp(),))
app.run(8082)
\ No newline at end of file
......@@ -3,21 +3,21 @@ Server tests
@author: jacekf
'''
from corepost.web import CorePost, route
from corepost.web import RestServiceContainer, route
from corepost.enums import Http
from corepost.filters import IRequestFilter, IResponseFilter
import zope.interface
from zope.interface import implements
class AddCustomHeaderFilter():
"""Implements just a request filter"""
zope.interface.implements(IRequestFilter)
implements(IRequestFilter)
def filterRequest(self,request):
request.received_headers["Custom-Header"] = "Custom Header Value"
class Change404to503Filter():
"""Implements just a response filter that changes 404 to 503 statuses"""
zope.interface.implements(IResponseFilter)
implements(IResponseFilter)
def filterResponse(self,request,response):
if response.code == 404:
......@@ -25,7 +25,7 @@ class Change404to503Filter():
class WrapAroundFilter():
"""Implements both types of filters in one class"""
zope.interface.implements(IRequestFilter,IResponseFilter)
implements(IRequestFilter,IResponseFilter)
def filterRequest(self,request):
request.received_headers["X-Wrap-Input"] = "Input"
......@@ -33,14 +33,15 @@ class WrapAroundFilter():
def filterResponse(self,request,response):
response.headers["X-Wrap-Output"] = "Output"
class FilterApp(CorePost):
class FilterService():
path = "/"
@route("/",Http.GET)
def root(self,request,**kwargs):
return request.received_headers
def run_filter_app():
app = FilterApp(filters=(Change404to503Filter(),AddCustomHeaderFilter(),WrapAroundFilter(),))
app = RestServiceContainer(services=(FilterService(),),filters=(Change404to503Filter(),AddCustomHeaderFilter(),WrapAroundFilter(),))
app.run(8083)
if __name__ == "__main__":
......
......@@ -3,17 +3,15 @@ Server tests
@author: jacekf
'''
from corepost.web import CorePost, route
from corepost.web import RestServiceContainer, route
from corepost.enums import Http, MediaType, HttpHeader
from twisted.internet import defer
from xml.etree import ElementTree
from UserDict import UserDict
import json, yaml
class HomeApp(CorePost):
class HomeApp():
def __init__(self,*args,**kwargs):
CorePost.__init__(self, *args, **kwargs)
self.issue1 = "issue 1"
@route("/",Http.GET)
......@@ -117,7 +115,7 @@ class HomeApp(CorePost):
return self.issue1
def run_app_home():
app = HomeApp()
app = RestServiceContainer((HomeApp(),))
app.run()
if __name__ == "__main__":
......
'''
A CorePost module1 that can be merged into the main CorePost Resource
A RestServiceContainer module1 that can be merged into the main RestServiceContainer Resource
'''
from corepost.web import CorePost, route
from corepost.web import RestServiceContainer, route
from corepost.enums import Http
from twisted.web.resource import Resource
from twisted.internet import reactor
from twisted.web.server import Site
class HomeApp(CorePost):
class HomeApp():
@route("/")
def home_root(self,request,**kwargs):
return "HOME %s" % kwargs
class Module1(CorePost):
class Module1():
path = "/module1"
@route("/",Http.GET)
def module1_get(self,request,**kwargs):
......@@ -24,7 +22,8 @@ class Module1(CorePost):
def module1e_sub(self,request,**kwargs):
return request.path
class Module2(CorePost):
class Module2():
path = "/module2"
@route("/",Http.GET)
def module2_get(self,request,**kwargs):
......@@ -35,12 +34,8 @@ class Module2(CorePost):
return request.path
def run_app_multi():
app = Resource()
app.putChild('', HomeApp())
app.putChild('module1',Module1())
app.putChild('module2',Module2())
factory = Site(app)
reactor.listenTCP(8081, factory) #@UndefinedVariable
reactor.run() #@UndefinedVariable
app = RestServiceContainer((HomeApp(),Module1(),Module2()))
app.run(8081)
if __name__ == "__main__":
run_app_multi()
\ No newline at end of file
'''
Server tests
@author: jacekf
'''
from corepost import Response
from corepost.web import RestServiceContainer
class DB():
"""Fake in-memory DB for testing"""
customers = {}
class Customer():
"""Represents customer entity"""
def __init__(self,customerId,firstName,lastName):
(self.customerId,self.firstName,self.lastName) = (customerId,firstName,lastName)
self.addresses = {}
class CustomerAddress():
"""Represents customer address entity"""
def __init__(self,customer,streetNumber,streetName,stateCode,countryCode):
(self.customer,self.streetNumber,self.streetName.self.stateCode,self.countryCode) = (customer,streetNumber,streetName,stateCode,countryCode)
class CustomerRestService():
path = "/customer"
def getAll(self,request):
return DB.customers
def get(self,request,customerId):
return DB.customers[customerId] if customerId in DB.customers else Response(404, "Customer %s not found" % customerId)
def post(self,request,customerId,firstName,lastName):
if customerId in DB.customers:
return Response(409,"Customer %s already exists" % customerId)
else:
DB.customers[customerId] = Customer(customerId, firstName, lastName)
return Response(201)
def put(self,request,customerId,firstName,lastName):
if customerId in DB.customers:
DB.customers[customerId].firstName = firstName
DB.customers[customerId].lastName = lastName
return Response(200)
else:
return Response(404, "Customer %s not found" % customerId)
def delete(self,request,customerId):
if customerId in DB.customers:
del(DB.customers[customerId])
return Response(200)
else:
return Response(404, "Customer %s not found" % customerId)
def deleteAll(self,request):
DB.customers.clear()
return Response(200)
class CustomerAddressRestService():
path = "/customer/<customerId>/address"
def getAll(self,request,customerId):
return DB.customers
def get(self,request,customerId):
return DB.customers[customerId] if customerId in DB.customers else Response(404, "Customer %s not found" % customerId)
def post(self,request,customerId,firstName,lastName):
if customerId in DB.customers:
return Response(409,"Customer %s already exists" % customerId)
else:
DB.customers[customerId] = Customer(customerId, firstName, lastName)
return Response(201)
def put(self,request,customerId,firstName,lastName):
if customerId in DB.customers:
DB.customers[customerId].firstName = firstName
DB.customers[customerId].lastName = lastName
return Response(200)
else:
return Response(404, "Customer %s not found" % customerId)
def delete(self,request,customerId):
if customerId in DB.customers:
del(DB.customers[customerId])
return Response(200)
else:
return Response(404, "Customer %s not found" % customerId)
def deleteAll(self,request):
DB.customers.clear()
return Response(200)
def run_rest_app():
app = RestServiceContainer(restServices=(CustomerRestService(),))
app.run(8085)
if __name__ == "__main__":
run_rest_app()
\ No newline at end of file
......@@ -22,3 +22,11 @@ def convertToJson(obj):
return json.dumps(obj)
except Exception as ex:
raise RuntimeError(str(ex))
def checkExpectedInterfaces(objects,expectedInterface):
"""Verifies that all the objects implement the expected interface"""
for obj in objects:
if not expectedInterface.providedBy(obj):
raise RuntimeError("Object %s does not implement %s interface" % (obj,expectedInterface))
\ No newline at end of file
......@@ -3,32 +3,35 @@ Main server classes
@author: jacekf
'''
from collections import defaultdict
from corepost import Response
from corepost.enums import Http, HttpHeader
from corepost.utils import getMandatoryArgumentNames, convertToJson
from corepost.routing import UrlRouter, CachedUrl, RequestRouter
from corepost import Response, IRestServiceContainer
from corepost.enums import Http
from corepost.routing import UrlRouter, RequestRouter
from enums import MediaType
from formencode import FancyValidator, Invalid
from twisted.internet import reactor, defer
from twisted.web.http import parse_qs
from twisted.internet import reactor
from twisted.internet.defer import Deferred
from twisted.web.resource import Resource
from twisted.web.server import Site, NOT_DONE_YET
import re, copy, exceptions, json, yaml
from xml.etree import ElementTree
from xml.etree.ElementTree import Element
from twisted.internet.defer import Deferred
from zope.interface import implements
#########################################################
#
# CLASSES
#
#########################################################
class CorePost(Resource):
class RestServiceContainer(Resource):
'''
Main resource responsible for routing REST requests to the implementing methods
'''
isLeaf = True
implements(IRestServiceContainer)
def __init__(self,schema=None,filters=()):
def __init__(self,services=(),schema=None,filters=()):
'''
Constructor
'''
self.services = services
self.__router = RequestRouter(self,schema,filters)
Resource.__init__(self)
......@@ -83,7 +86,6 @@ class CorePost(Resource):
reactor.listenTCP(port, factory) #@UndefinedVariable
reactor.run() #@UndefinedVariable
##################################################################################################
#
# DECORATORS
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment