Skip to content

Commit

Permalink
First version.
Browse files Browse the repository at this point in the history
  • Loading branch information
koenedaele committed Sep 27, 2015
1 parent be92944 commit e929380
Show file tree
Hide file tree
Showing 9 changed files with 410 additions and 0 deletions.
60 changes: 60 additions & 0 deletions development.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
###
# app configuration
# http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/environment.html
###

[app:main]
use = egg:urihandler

pyramid.reload_templates = true
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en
pyramid.includes =
pyramid_debugtoolbar

# By default, the toolbar only appears for clients from IP addresses
# '127.0.0.1' and '::1'.
debugtoolbar.hosts = 127.0.0.1 ::1

###
# wsgi server configuration
###

[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543

###
# logging configuration
# http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/logging.html
###

[loggers]
keys = root, urihandler

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = INFO
handlers = console

[logger_urihandler]
level = DEBUG
handlers =
qualname = urihandler

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
54 changes: 54 additions & 0 deletions production.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
###
# app configuration
# http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/environment.html
###

[app:main]
use = egg:urihandler

pyramid.reload_templates = false
pyramid.debug_authorization = false
pyramid.debug_notfound = false
pyramid.debug_routematch = false
pyramid.default_locale_name = en

###
# wsgi server configuration
###

[server:main]
use = egg:waitress#main
host = 0.0.0.0
port = 6543

###
# logging configuration
# http://docs.pylonsproject.org/projects/pyramid/en/1.5-branch/narr/logging.html
###

[loggers]
keys = root, urihandler

[handlers]
keys = console

[formatters]
keys = generic

[logger_root]
level = WARN
handlers = console

[logger_urihandler]
level = WARN
handlers =
qualname = urihandler

[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic

[formatter_generic]
format = %(asctime)s %(levelname)-5.5s [%(name)s][%(threadName)s] %(message)s
9 changes: 9 additions & 0 deletions sample.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
uris:
- match: '^/actoren/(?P<id>\d+)$'
mount: true
redirect: 'https://actoren.onroerenderfgoed.be/actoren/{id}'
- match: '^/besluiten/(?P<id>\d+)$'
redirect: 'https://besluiten.onroerenderfgoed.be/besluiten/{id}'
- match: '^urn:x-oe:actoren:(?P<id>\d+)$'
mount: false
redirect: 'https://actoren.onroerenderfgoed.be/actoren/{id}'
28 changes: 28 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import pytest

from urihandler.handler import UriHandler

@pytest.fixture(scope="session")
def handlerconfig():
cfg = {
'uris': [
{
'match': '^/foobar/(?P<id>\d+)$',
'mount': True,
'redirect': 'http://localhost:5555/foobar/{id}'
}, {
'match': '^/bar/(?P<name>\w+)$',
'redirect': 'http://localhost:5555/bar/{name}'
} , {
'match': '^urn:x-barbar:(?P<namespace>:\w+):(?P<id>\d+)$',
'mount': False,
'redirect': 'http://localhost:2222/{namespace}/{id}'
}
]
}
return cfg


@pytest.fixture(scope="session")
def urihandler(handlerconfig):
return UriHandler(handlerconfig['uris'])
9 changes: 9 additions & 0 deletions tests/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
uris:
- match: '^/foobar/(?P<id>\d+)$'
mount: True
redirect: 'http://localhost:5555/foobar/{id}'
- match: '^/bar/(?P<name>\w+)$'
redirect: 'http://localhost:5555/bar/{name}'
- match: '^urn:x-barbar:(?P<namespace>:\w+):(?P<id>\d+)$'
mount: False
redirect: 'http://localhost:2222/{namespace}/{id}'
74 changes: 74 additions & 0 deletions tests/test_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import os
import unittest
import pytest

import logging
logging.basicConfig(level=logging.DEBUG)

from pyramid import testing
from pyramid.response import Response

from urihandler import _load_configuration
from urihandler.handler import (
IUriHandler,
UriHandler,
_build_uri_handler,
get_uri_handler
)

class TestHandler:

def test_urihandler_exists(self, urihandler):
assert urihandler

def test_no_match(self, urihandler):
req = testing.DummyRequest()
req.host_url = 'http://test.urihandler.org'
res = urihandler.handle('http://test.urihandler.org/bunnies/koen', req)
assert res is None

def test_mounted_redirect(self, urihandler):
req = testing.DummyRequest()
req.host_url = 'http://test.urihandler.org'
res = urihandler.handle('http://test.urihandler.org/foobar/18', req)
assert res == 'http://localhost:5555/foobar/18'


class MockRegistry:

def __init__(self, settings=None):

if settings is None:
self.settings = {}
else: # pragma NO COVER
self.settings = settings

self.uri_handler = None

def queryUtility(self, iface):
return self.uri_handler

def registerUtility(self, uri_handler, iface):
self.uri_handler = uri_handler


class TestGetAndBuild:

def test_get_uri_handler(self, handlerconfig):
r = MockRegistry()
UH = UriHandler(handlerconfig['uris'])
r.registerUtility(UH, IUriHandler)
UH2 = get_uri_handler(r)
assert UH == UH2

def test_build_uri_handler_already_exists(self, handlerconfig):
r = MockRegistry()
UH = UriHandler(handlerconfig['uris'])
r.registerUtility(UH, IUriHandler)
UH2 = _build_uri_handler(r, handlerconfig)
assert UH == UH2

def test_build_uri_handler(self, handlerconfig):
r = MockRegistry()
UH = _build_uri_handler(r, handlerconfig)
assert isinstance(UH, UriHandler)
74 changes: 74 additions & 0 deletions urihandler/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import logging
log = logging.getLogger(__name__)

from pyramid.config import Configurator

import os
import json
import yaml

from .handler import get_uri_handler, _build_uri_handler

def _parse_settings(settings):
'''
Parse the relevant settings for this application.
:param dict settings:
'''

log.debug(settings)

prefix='urihandler'

defaults = {
'config': os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'sample.yaml'))
}

urihand_settings = defaults.copy()

for short_key_name in ('config', ):
key_name = '%s.%s' % (prefix, short_key_name)
if key_name in settings:
urihand_settings[short_key_name] = \
settings.get(key_name, defaults.get(short_key_name, None))

for short_key in urihand_settings:
long_key = '%s.%s' % (prefix, short_key)
settings[long_key] = urihand_settings[short_key]

return urihand_settings

def _load_configuration(path):
'''
Load the configuration for the UriHandler.
:param str path: Path to the config file in YAML format.
:returns: A :class:`dict` with the config options.
'''
log.debug('Loading uriregistry config from %s.' % path)
f = open(path, 'r')
content = yaml.load(f.read())
log.debug(content)
f.close()
return content


def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
config = Configurator(settings=settings)

urihand_settings = _parse_settings(config.registry.settings)
handlerconfig = _load_configuration(urihand_settings['config'])

_build_uri_handler(config.registry, handlerconfig)

config.add_directive('get_uri_handler', get_uri_handler)
config.add_request_method(get_uri_handler, 'uri_handler', reify=True)

config.add_route('home', '/')
config.add_route('handle', '/handle')
config.add_route('redirect', '/{uri:.*}')

config.scan()
return config.make_wsgi_app()
70 changes: 70 additions & 0 deletions urihandler/handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-

import logging
log = logging.getLogger(__name__)

import re
import copy

from zope.interface import Interface


class IUriHandler(Interface):
pass


class UriHandler:
'''
Central handler that deals with redirecting uri's.
'''

def __init__(self, uris=[]):
self.uris = uris

def handle(self, uri, request):
uris = copy.deepcopy(self.uris)
for u in uris:
if 'mount' not in u or u['mount']:
if u['match'].startswith('^'):
u['match'] = u['match'].replace('^', '^' + request.host_url)
else:
u['match'] = request.host + '.*' + u['match']
log.debug('Matching {0} to {1}.'.format(uri, u['match']))
m = re.match(u['match'], uri)
if m:
redirect = u['redirect'].format(**m.groupdict())
log.debug('Match found. Redirecting to {0}.'.format(redirect,))
return redirect
return None


def _build_uri_handler(registry, handlerconfig):
'''
:param pyramid.registry.Registry registry: Pyramid registry
:param dict handlerconfig: UriHandler config in dict form.
:rtype: :class:`uriregistry.registry.UriHandler`
'''
uri_handler = registry.queryUtility(IUriHandler)
if uri_handler is not None:
return uri_handler

uri_handler = UriHandler(
handlerconfig['uris'],
)

registry.registerUtility(uri_handler, IUriHandler)
return registry.queryUtility(IUriHandler)


def get_uri_handler(registry):
'''
Get the :class:`urihandler.handler.UriHandler` attached to this pyramid
application.
:rtype: :class:`urihandler.handler.UriHandler`
'''
# Argument might be a config or request
regis = getattr(registry, 'registry', None)
if regis is None:
regis = registry
return regis.queryUtility(IUriHandler)
Loading

0 comments on commit e929380

Please sign in to comment.