883 lines
30 KiB
Python
Raw Normal View History

# Copyright (C) 2015 SUSE Linux GmbH
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
import os
import re
import string
2014-03-04 16:32:15 +01:00
import time
import urlparse
import xml.etree.cElementTree as ET
import httpretty
import osc
2014-02-28 11:45:06 +01:00
APIURL = 'http://localhost'
FIXTURES = os.path.join(os.getcwd(), 'tests/fixtures')
DEBUG = True
# The idiotic routing system of httpretty use a hash table. Because
# we have a default() handler, we need a deterministic routing
# mechanism.
_table = {
httpretty.GET: [],
httpretty.POST: [],
httpretty.PUT: [],
httpretty.DELETE: [],
}
def router_handler(route_table, method, request, uri, headers):
"""Route the URLs in a deterministic way."""
uri_parsed = urlparse.urlparse(uri)
for path, fn in route_table:
match = False
if isinstance(path, basestring) and uri_parsed.path == path:
match = True
elif not isinstance(path, basestring) and path.search(uri_parsed.path):
match = True
if match:
return fn(request, uri, headers)
raise Exception('Not found entry for method %s for url %s' % (method, uri))
def router_handler_GET(request, uri, headers):
return router_handler(_table[httpretty.GET], 'GET', request, uri, headers)
def router_handler_POST(request, uri, headers):
return router_handler(_table[httpretty.POST], 'POST', request, uri, headers)
def router_handler_PUT(request, uri, headers):
return router_handler(_table[httpretty.PUT], 'PUT', request, uri, headers)
def router_handler_DELETE(request, uri, headers):
return router_handler(_table[httpretty.DELETE], 'DELETE', request, uri, headers)
def method_decorator(method, path):
def _decorator(fn):
def _fn(*args, **kwargs):
return fn(OBS._self, *args, **kwargs)
_table[method].append((path, _fn))
return _fn
return _decorator
def GET(path):
return method_decorator(httpretty.GET, path)
def POST(path):
return method_decorator(httpretty.POST, path)
def PUT(path):
return method_decorator(httpretty.PUT, path)
def DELETE(path):
return method_decorator(httpretty.DELETE, path)
class OBS(object):
# This class will become a singleton
_self = None
def __new__(cls, *args, **kwargs):
"""Class constructor."""
if not OBS._self:
OBS._self = super(OBS, cls).__new__(cls, *args, **kwargs)
httpretty.reset()
httpretty.enable()
httpretty.register_uri(httpretty.GET, re.compile(r'.*'), body=router_handler_GET)
httpretty.register_uri(httpretty.POST, re.compile(r'.*'), body=router_handler_POST)
httpretty.register_uri(httpretty.PUT, re.compile(r'.*'), body=router_handler_PUT)
httpretty.register_uri(httpretty.DELETE, re.compile(r'.*'), body=router_handler_DELETE)
return OBS._self
def __init__(self, fixtures=FIXTURES):
"""Instance constructor."""
self.fixtures = fixtures
httpretty.enable()
oscrc = os.path.join(fixtures, 'oscrc')
osc.core.conf.get_config(override_conffile=oscrc,
override_no_keyring=True,
override_no_gnome_keyring=True)
os.environ['OSC_CONFIG'] = oscrc
# Internal status of OBS. The mockup will use this data to
# build the responses. We will try to put responses as XML
# templates in the fixture directory.
self.requests = {
'123': {
'request': 'new',
'review': 'accepted',
'who': 'Admin',
'by': 'group',
'id': '123',
'by_who': 'opensuse-review-team',
'package': 'gcc',
},
'321': {
'request': 'review',
'review': 'new',
'who': 'Admin',
'by': 'group',
'id': '321',
'by_who': 'factory-staging',
'package': 'puppet',
},
'333': {
'request': 'review',
'review': 'new',
'who': 'Admin',
'by': 'project',
'id': '333',
'by_who': 'openSUSE:Factory:Staging:B',
'package': 'wine',
},
'501': {
'request': 'review',
'review': 'new',
'who': 'Admin',
'by': 'project',
'id': '501',
'by_who': 'openSUSE:Factory:Staging:C',
'package': 'apparmor',
},
'502': {
'request': 'review',
'review': 'new',
'who': 'Admin',
'by': 'project',
'id': '502',
'by_who': 'openSUSE:Factory:Staging:C',
'package': 'mariadb',
},
2014-06-24 14:11:06 +02:00
'1000': {
'request': 'review',
'review': 'new',
'who': 'Admin',
'by': 'user',
'id': '1000',
'by_who': 'factory-repo-checker',
'package': 'emacs',
},
'1001': {
'request': 'review',
'review': 'new',
'who': 'Admin',
'by': 'user',
'id': '1001',
'by_who': 'factory-repo-checker',
'package': 'python',
},
}
self.staging_project = {
'A': {
'project': 'openSUSE:Factory:Staging:A',
'title': '',
'description': '',
},
'U': {
'project': 'openSUSE:Factory:Staging:U',
'title': 'Unfrozen',
'description': '',
},
'B': {
'project': 'openSUSE:Factory:Staging:B',
'title': 'wine',
'description': 'requests:\n- {id: 333, package: wine}',
},
'C': {
'project': 'openSUSE:Factory:Staging:C',
'title': 'A project ready to be accepted',
2014-06-24 14:28:57 +02:00
'description': ('requests:\n- {id: 501, package: apparmor, author: Admin}\n'
'- {id: 502, package: mariadb, author: Admin}'),
2014-06-24 14:11:06 +02:00
},
'J': {
'project': 'openSUSE:Factory:Staging:J',
'title': 'A project to be checked',
'description': ('requests:\n- {id: 1000, package: emacs, author: Admin}\n'
'- {id: 1001, package: python, author: Admin}'),
},
}
self.links = {
'openSUSE:Factory:Staging:B/wine': {
'prj': 'openSUSE:Factory:Staging:B',
'pkg': 'wine',
'devprj': 'home:Admin',
},
'openSUSE:Factory:Staging:C/apparmor': {
'prj': 'openSUSE:Factory:Staging:C',
'pkg': 'apparmor',
'devprj': 'home:Admin',
},
'openSUSE:Factory:Staging:C/mariadb': {
'prj': 'openSUSE:Factory:Staging:C',
'pkg': 'mariadb',
'devprj': 'home:Admin',
},
2014-06-24 14:11:06 +02:00
'openSUSE:Factory:Staging:J/emacs': {
'prj': 'openSUSE:Factory:Staging:J',
'pkg': 'emacs',
2014-06-25 14:17:52 +02:00
'devprj': 'home:Admin',
2014-06-24 14:11:06 +02:00
},
'openSUSE:Factory:Staging:J/python': {
'prj': 'openSUSE:Factory:Staging:J',
'pkg': 'python',
2014-06-25 14:17:52 +02:00
'devprj': 'home:Admin',
2014-06-24 14:11:06 +02:00
},
2014-03-07 13:58:33 +01:00
}
self.meta = {}
self.package = {
'home:Admin/gcc': {
'rev': '1',
'vrev': '1',
'name': 'gcc',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
},
'home:Admin/wine': {
'rev': '1',
'vrev': '1',
'name': 'wine',
'srcmd5': 'de9a9f5e3bedb01980465f3be3d236cb',
},
'home:Admin/puppet': {
'rev': '1',
'vrev': '1',
'name': 'puppet',
'srcmd5': 'de8a9f5e3bedb01980465f3be3d236cb',
},
'openSUSE:Factory/gcc': {
'rev': '1',
'vrev': '1',
'name': 'gcc',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
},
'openSUSE:Factory/wine': {
'rev': '1',
'vrev': '1',
'name': 'wine',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
},
'openSUSE:Factory:Rings:0-Bootstrap/elem-ring0': {
'rev': '1',
'vrev': '1',
'name': 'elem-ring0',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
},
'openSUSE:Factory/binutils': {
'rev': '1',
'vrev': '1',
'name': 'wine',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
},
'home:Admin/apparmor': {
'rev': '1',
'vrev': '1',
'name': 'apparmor',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
},
'openSUSE:Factory/apparmor': {
'rev': '1',
'vrev': '1',
'name': 'apparmor',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
},
'home:Admin/mariadb': {
'rev': '1',
'vrev': '1',
'name': 'mariadb',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
},
'openSUSE:Factory/mariadb': {
'rev': '1',
'vrev': '1',
'name': 'mariadb',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
},
2014-06-25 14:17:52 +02:00
'home:Admin/emacs': {
'rev': '1',
'vrev': '1',
'name': 'emacs',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
2014-07-04 15:20:28 +02:00
'lsrcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
'verifymd5': 'de7a9f5e3bedb01980465f3be3d236cb',
2014-06-25 14:17:52 +02:00
},
'home:Admin/python': {
'rev': '1',
'vrev': '1',
'name': 'python',
'srcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
2014-07-04 15:20:28 +02:00
'lsrcmd5': 'de7a9f5e3bedb01980465f3be3d236cb',
'verifymd5': 'de7a9f5e3bedb01980465f3be3d236cb',
2014-06-25 14:17:52 +02:00
},
}
2014-02-27 13:39:57 +01:00
self.comments = {
'openSUSE:Factory:Staging:A': [
2014-06-23 14:08:00 +02:00
{
'who': 'Admin',
'when': '2014-06-01 17:56:28 UTC',
'id': '1',
'body': 'Just a comment',
}
],
'openSUSE:Factory:Staging:U': [],
'openSUSE:Factory:Staging:B': [],
'openSUSE:Factory:Staging:C': [
2014-06-23 14:08:00 +02:00
{
'who': 'Admin',
'when': '2014-06-01 17:56:28 UTC',
'id': '2',
'body': ("The list of requests tracked in openSUSE:Factory:Staging:C has changed:\n\n"
" * Request#501 for package apparmor submitted by Admin\n"
" * Request#502 for package mariadb submitted by Admin\n")
}
],
2014-06-24 14:11:06 +02:00
'openSUSE:Factory:Staging:J': [],
}
# To track comments created during test execution, even if
# they have been deleted afterward
self.comment_bodies = []
# Different spec files stored in some openSUSE:Factory
# projects
self.spec_list = {
'openSUSE:Factory/apparmor': [
{
'spec': 'apparmor.spec',
}
],
}
#
# /request/
#
@GET(re.compile(r'/request/\d+'))
def request(self, request, uri, headers):
"""Return a request XML description."""
request_id = re.search(r'(\d+)', uri).group(1)
response = (404, headers, '<result>Not found</result>')
try:
template = string.Template(self._fixture(uri))
response = (200, headers, template.substitute(self.requests[request_id]))
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'REQUEST', uri, response
return response
def _request(self, request_id):
"""Utility function to recover a request from the ID."""
template = string.Template(self._fixture(urlparse.urljoin(APIURL, '/request/' + request_id)))
return template.substitute(self.requests[request_id])
@POST(re.compile(r'/request/\d+'))
def review_request(self, request, uri, headers):
request_id = re.search(r'(\d+)', uri).group(1)
qs = urlparse.parse_qs(urlparse.urlparse(uri).query)
response = (404, headers, '<result>Not found</result>')
# Adding review
if qs.get('cmd', None) == ['addreview']:
self.requests[request_id]['request'] = 'review'
self.requests[request_id]['review'] = 'new'
# Changing review
if qs.get('cmd', None) == ['changereviewstate']:
self.requests[request_id]['request'] = 'new'
self.requests[request_id]['review'] = qs['newstate'][0]
# Project review
if 'by_project' in qs:
self.requests[request_id]['by'] = 'project'
self.requests[request_id]['by_who'] = qs['by_project'][0]
# Group review
if 'by_group' in qs:
self.requests[request_id]['by'] = 'group'
self.requests[request_id]['by_who'] = qs[u'by_group'][0]
try:
response = (200, headers, self._request(request_id))
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'REVIEW REQUEST', uri, response
return response
@GET('/request')
def request_search(self, request, uri, headers):
"""Request search function."""
qs = urlparse.parse_qs(urlparse.urlparse(uri).query)
states = qs['states'][0].split(',')
response = (404, headers, '<result>Not found</result>')
requests = [rq for rq in self.requests.values() if rq['request'] in states]
if 'package' in qs:
requests = [rq for rq in requests if qs['package'][0] in rq['package']]
try:
_requests = '\n'.join(self._request(rq['id']) for rq in requests)
template = string.Template(self._fixture(uri, filename='result.xml'))
result = template.substitute(
{
'nrequests': len(requests),
'requests': _requests,
})
response = (200, headers, result)
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'SEARCH REQUEST', uri, response
return response
#
# /source/
#
2014-06-24 14:11:06 +02:00
@GET(re.compile(r'/source/openSUSE:Factory:Staging:[A|B|C|J]/_project'))
def source_staging_project_project(self, request, uri, headers):
"""Return the _project information for a staging project."""
# Load the proper fixture and adjust mtime according the
# current time.
response = (404, headers, '<result>Not found</result>')
try:
template = string.Template(self._fixture(uri))
if 'Staging:A' in uri:
project = template.substitute({'mtime': int(time.time()) - 3600 * 24 * 356})
2014-03-07 13:58:33 +01:00
else:
project = template.substitute({'mtime': int(time.time()) - 100})
response = (200, headers, project)
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'STAGING PROJECT _PROJECT', uri, response
return response
2014-06-24 14:11:06 +02:00
@GET(re.compile(r'/source/openSUSE:Factory:Staging:[A|B|C|U|J](/\w+)?/_meta'))
def source_staging_project_meta(self, request, uri, headers):
"""Return the _meta information for a staging project."""
key = re.search(r'openSUSE:Factory:Staging:(\w(?:/\w+)?)/_meta', uri).group(1)
response = (404, headers, '<result>Not found</result>')
try:
if key not in self.meta:
template = string.Template(self._fixture(uri))
self.meta[key] = template.substitute(self.staging_project[key])
meta = self.meta[key]
response = (200, headers, meta)
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'STAGING PROJECT [PACKAGE] _META', uri, response
return response
2014-06-24 14:11:06 +02:00
@PUT(re.compile(r'/source/openSUSE:Factory:Staging:[A|B|C|U|J](/\w+)?/_meta'))
def put_source_staging_project_meta(self, request, uri, headers):
"""Set the _meta information for a staging project."""
key = re.search(r'openSUSE:Factory:Staging:(\w(?:/\w+)?)/_meta', uri).group(1)
self.meta[key] = request.body
meta = self.meta[key]
response = (200, headers, meta)
if DEBUG:
print 'PUT STAGING PROJECT [PACKAGE] _META', uri, response
return response
@GET(re.compile(r'/source/openSUSE:Factory:Staging:B/wine'))
def source_stating_project_wine(self, request, uri, headers):
"""Return wine package information. Is a link."""
package = re.search(r'/source/([\w:]+/\w+)', uri).group(1)
response = (404, headers, '<result>Not found</result>')
try:
template = string.Template(self._fixture(uri))
response = (200, headers, template.substitute(self.links[package]))
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'SOURCE WINE', uri, response
return response
@DELETE(re.compile('/source/openSUSE:Factory:Staging:[B|C]/\w+'))
def delete_package(self, request, uri, headers):
"""Delete a source package from a Staging project."""
package = re.search(r'/source/([\w:]+/\w+)', uri).group(1)
response = (404, headers, '<result>Not found</result>')
try:
del self.links[package]
response = (200, headers, '<result>Ok</result>')
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'DELETE', uri, response
return response
@GET(re.compile(r'/source/openSUSE:Factory/apparmor$'))
def source_project_apparmor(self, request, uri, headers):
"""Return apparmor spec list."""
package = re.search(r'/source/([\w:]+/\w+)', uri).group(1)
response = (404, headers, '<result>Not found</result>')
try:
template = string.Template(self._fixture(uri, filename='apparmor.xml'))
entry_template = string.Template(self._fixture(uri, filename='_entry.xml'))
entries = ''.join(entry_template.substitute(entry) for entry in self.spec_list[package])
response = (200, headers, template.substitute({'entries': entries}))
# The next call len() will be 2
if len(self.spec_list[package]) == 1:
self.spec_list[package].append({'spec': 'apparmor-doc.spec'})
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'SOURCE APPARMOR', uri, response
return response
2014-07-04 15:20:28 +02:00
@GET(re.compile(r'/source/home:Admin/\w+'))
def source_project(self, request, uri, headers):
"""Return information of a source package."""
2014-07-04 15:20:28 +02:00
qs = urlparse.parse_qs(urlparse.urlparse(uri).query)
index = re.search(r'/source/([\w:]+/\w+)', uri).group(1)
project, package = index.split('/')
response = (404, headers, '<result>Not found</result>')
2014-07-04 15:20:28 +02:00
suffix = '_expanded' if 'expanded' in qs else '_info' if 'info' in qs else ''
path = os.path.join('source', project, package + suffix)
try:
2014-07-04 15:20:28 +02:00
template = string.Template(self._fixture(path=path))
response = (200, headers, template.substitute(self.package[index]))
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'SOURCE HOME:ADMIN', package, uri, response
return response
@POST(re.compile(r'/source/openSUSE:Factory:Rings:1-MinimalX/\w+'))
def show_wine_link(self, request, uri, headers):
# TODO: only useful answer if cmd=showlinked
return (200, headers, '<collection/>')
@GET('/source/openSUSE:Factory:Staging:A/wine')
def source_link(self, request, uri, headers):
project_package = re.search(r'/source/([\w:]+/\w+)', uri).group(1)
response = (404, headers, '<result>Not found</result>')
try:
template = string.Template(self._fixture(uri))
response = (200, headers, template.substitute(self.links[project_package]))
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'SOURCE HOME:ADMIN WINE', uri, response
return response
@PUT(re.compile(r'/source/openSUSE:Factory:Staging:[AB]/\w+/_link'))
def put_source_link(self, request, uri, headers):
"""Create wine link in staging project A."""
project_package = re.search(r'/source/([\w:]+/\w+)/_link', uri).group(1)
project, package = re.search(r'([\w:]+)/(\w+)', project_package).groups()
response = (404, headers, '<result>Not found</result>')
try:
_link = ET.fromstring(request.body)
self.links[project_package] = {
'prj': project,
'pkg': package,
'devprj': _link.get('project')
}
response = (200, headers, '<result>Ok</result>')
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'PUT SOURCE WINE _LINK', uri, response
return response
#
# /build/
#
2014-07-04 15:20:28 +02:00
# @GET(re.compile(r'build/home:Admin/_result'))
# def build_lastsuccess(self, request, uri, headers):
# package = re.search(r'/source/([\w:]+/\w+)', uri).group(1)
# response = (404, headers, '<result>Not found</result>')
# try:
# template = string.Template(self._fixture(uri))
# response = (200, headers, template.substitute(self.package[package]))
# except Exception as e:
# if DEBUG:
# print uri, e
# if DEBUG:
# print 'BUILD _RESULT LASTBUILDSUCCESS', package, uri, response
# return response
#
# /search/
#
@GET('/search/project/id')
def search_project_id(self, request, uri, headers):
"""Return a search result /search/project/id."""
assert urlparse.urlparse(uri).query == "match=starts-with(@name,'openSUSE:Factory:Staging:')"
response = (404, headers, '<result>Not found</result>')
try:
template = string.Template(self._fixture(uri, filename='result.xml'))
projects = '\n'.join(
'<project name="%s"/>' % staging['project'] for staging in self.staging_project.values()
)
result = template.substitute(
{
'nprojects': len(self.staging_project),
'projects': projects,
})
response = (200, headers, result)
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'SEARCH PROJECT ID', uri, response
return response
@GET('/search/request')
def search_request(self, request, uri, headers):
"""Return a search result for /search/request."""
query = urlparse.urlparse(uri).query
2014-06-24 14:11:06 +02:00
assert query in (
2014-09-22 17:56:35 +02:00
"match=state/@name='review'+and+review[@by_group='factory-staging'+and+@state='new']+and+(target[@project='openSUSE:Factory']+or+target[@project='openSUSE:Factory:NonFree'])",
"match=state/@name='review'+and+review[@by_user='factory-repo-checker'+and+@state='new']+and+(target[@project='openSUSE:Factory']+or+target[@project='openSUSE:Factory:NonFree'])"
2014-06-24 14:11:06 +02:00
)
response = (404, headers, '<result>Not found</result>')
2014-06-24 14:11:06 +02:00
by, by_who = re.search(r"@by_(user|group)='([-\w]+)'", query).groups()
state = re.search(r"@state='(\w+)'", query).group(1)
requests = [rq for rq in self.requests.values()
if rq['request'] == 'review'
2014-06-24 14:11:06 +02:00
and rq['review'] == state
and rq['by'] == by
and rq['by_who'] == by_who]
try:
_requests = '\n'.join(self._request(rq['id']) for rq in requests)
template = string.Template(self._fixture(uri, filename='result.xml'))
result = template.substitute(
{
'nrequests': len(requests),
'requests': _requests,
})
response = (200, headers, result)
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'SEARCH REQUEST', uri, response
return response
@GET('/search/request/id')
def search_request_id(self, request, uri, headers):
"""Return a search result for /search/request/id."""
query = urlparse.urlparse(uri).query
project = re.search(r"@by_project='([^']+)'", query).group(1)
response = (404, headers, '<result>Not found</result>')
requests = [rq for rq in self.requests.values()
if rq['request'] == 'review'
and rq['review'] == 'new'
and rq['by'] == 'project'
and rq['by_who'] == project]
try:
_requests = '\n'.join('<request id="%s"/>' % rq['id'] for rq in requests)
template = string.Template(self._fixture(uri, filename='result.xml'))
result = template.substitute(
{
'nrequests': len(requests),
'requests': _requests,
})
response = (200, headers, result)
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'SEARCH REQUEST', uri, response
return response
#
# /comments/
#
@GET(re.compile(r'/comments/project/.*'))
def get_comment(self, request, uri, headers):
"""Get comments for a project."""
prj = re.search(r'comments/project/([^/]*)', uri).group(1)
comments = self.comments[prj]
if not comments:
return (200, headers, '<comments project="%s"/>' % prj)
else:
ret_str = '<comments project="%s">' % prj
for c in comments:
ret_str += '<comment who="%s" when="%s" id="%s">' % (c['who'], c['when'], c['id'])
ret_str += c['body'].replace('<', '&lt;').replace('>', '&gt;')
ret_str += '</comment>'
ret_str += '</comments>'
return (200, headers, ret_str)
@POST(re.compile(r'/comments/project/.*'))
def post_comment(self, request, uri, headers):
"""Add comment to a project."""
prj = re.search(r'comments/project/([^/]*)', uri).group(1)
2014-06-23 14:08:00 +02:00
comment = {
'who': 'Admin',
'when': time.strftime('%Y-%m-%d %H:%M:%S UTC', time.gmtime()),
'id': str(sum(len(c) for c in self.comments.values()) + 1),
'body': request.body
}
self.comments[prj].append(comment)
self.comment_bodies.append(request.body)
response = (200, headers, '<result>Ok</result>')
return response
@DELETE(re.compile(r'/comment/\d+'))
def delete_comment(self, request, uri, headers):
"""Delete a comments."""
comment_id = re.search(r'comment/(\d+)', uri).group(1)
for prj in self.comments:
self.comments[prj] = [c for c in self.comments[prj] if c['id'] != comment_id]
return (200, headers, '<result>Ok</result>')
#
# /project/staging_projects
#
@GET(re.compile(r'/project/staging_projects/openSUSE:Factory.*'))
def staging_projects(self, request, uri, headers):
"""Return a JSON fixture."""
response = (404, headers, '<result>Not found</result>')
try:
path = urlparse.urlparse(uri).path + '.json'
fixture = self._fixture(path=path)
response = (200, headers, fixture)
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'REQUEST', uri, response
return response
#
# Static fixtures
#
@GET(re.compile(r'.*'))
def default(self, request, uri, headers):
"""Default handler. Search in the fixture directory."""
response = (404, headers, '<result>Not found</result>')
try:
response = (200, headers, self._fixture(uri))
except Exception as e:
if DEBUG:
print uri, e
if DEBUG:
print 'DEFAULT', uri, response
return response
def _fixture(self, uri=None, path=None, filename=None):
"""Read a file as a fixture."""
if not path:
path = urlparse.urlparse(uri).path
path = path[1:] if path.startswith('/') else path
if filename:
fixture_path = os.path.join(self.fixtures, path, filename)
else:
fixture_path = os.path.join(self.fixtures, path)
with open(fixture_path) as f:
return f.read()