You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by je...@apache.org on 2015/02/16 12:44:32 UTC
[01/37] allura git commit: [#5726] Rss feed now updates correctly for
new topics.
Repository: allura
Updated Branches:
refs/heads/ib/4542 c17783c25 -> b967bc5cc (forced update)
[#5726] Rss feed now updates correctly for new topics.
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/ef5610b9
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/ef5610b9
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/ef5610b9
Branch: refs/heads/ib/4542
Commit: ef5610b98e87ecaed02fa7e3088f33ccfa14c918
Parents: 852360f
Author: Heith Seewald <hs...@slashdotmedia.com>
Authored: Thu Jan 29 17:23:56 2015 -0500
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Feb 2 18:23:51 2015 +0000
----------------------------------------------------------------------
.../forgediscussion/controllers/root.py | 3 +-
.../tests/functional/test_forum.py | 64 ++++++++++++++++++++
2 files changed, 66 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/ef5610b9/ForgeDiscussion/forgediscussion/controllers/root.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/controllers/root.py b/ForgeDiscussion/forgediscussion/controllers/root.py
index cf1116e..9174fba 100644
--- a/ForgeDiscussion/forgediscussion/controllers/root.py
+++ b/ForgeDiscussion/forgediscussion/controllers/root.py
@@ -131,7 +131,8 @@ class RootController(BaseController, DispatchIndex, FeedController):
require_access(discussion, 'post')
thd = discussion.get_discussion_thread(dict(
headers=dict(Subject=subject)))[0]
- thd.post(subject, text)
+ p = thd.post(subject, text)
+ thd.post_to_feed(p)
flash('Message posted')
redirect(thd.url())
http://git-wip-us.apache.org/repos/asf/allura/blob/ef5610b9/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
index 34b71c9..3f45b46 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
@@ -27,6 +27,7 @@ from email.mime.multipart import MIMEMultipart
import pkg_resources
from pylons import tmpl_context as c
from nose.tools import assert_equal, assert_in
+import feedparser
from allura import model as M
from allura.tasks import mail_tasks
@@ -632,6 +633,69 @@ class TestForum(TestController):
r = self.app.get('/discussion/testforum/childforum/')
self.check_announcement_table(r, 'AAAA')
+ def test_post_to_feed(self):
+ # Create a new topic
+ r = self.app.get('/discussion/create_topic/')
+ f = r.html.find(
+ 'form', {'action': '/p/test/discussion/save_new_topic'})
+ params = dict()
+ inputs = f.findAll('input')
+ for field in inputs:
+ if field.has_key('name'):
+ params[field['name']] = field.has_key(
+ 'value') and field['value'] or ''
+ params[f.find('textarea')['name']] = 'XYZ'
+ params[f.find('select')['name']] = 'testforum'
+ params[f.find('input', {'style': 'width: 90%'})['name']] = 'AAAA'
+ thread = self.app.post('/discussion/save_new_topic', params=params).follow()
+ url = thread.request.url
+
+ # Check that the newly created topic is the most recent in the rss feed
+ f = self.app.get('/discussion/feed.rss').body
+ f = feedparser.parse(f)
+ newest_entry = f['entries'][0]['summary_detail']['value'].split("</p>")[0].split("<p>")[-1]
+ assert newest_entry == 'XYZ'
+
+ # Reply to the newly created thread.
+ thread = self.app.get(url)
+ t = thread.html.find(
+ 'div', {'class': 'row reply_post_form'}).find('form')
+ rep_url = t.get('action')
+ params = dict()
+ inputs = t.findAll('input')
+ for field in inputs:
+ if field.has_key('name'):
+ params[field['name']] = field.has_key(
+ 'value') and field['value'] or ''
+ params[t.find('textarea')['name']] = 'bbb'
+ self.app.post(str(rep_url), params=params)
+
+ # Check that reply matches the newest in the rss feed
+ f = self.app.get('/discussion/feed.rss').body
+ f = feedparser.parse(f)
+ newest_reply = f['entries'][0]['summary_detail']['value'].split("</p>")[0].split("<p>")[-1]
+ assert newest_reply == 'bbb'
+
+ def test_post_to_feed(self):
+ r = self.app.get('/discussion/create_topic/')
+ f = r.html.find(
+ 'form', {'action': '/p/test/discussion/save_new_topic'})
+ params = dict()
+ inputs = f.findAll('input')
+ for field in inputs:
+ if field.has_key('name'):
+ params[field['name']] = field.has_key(
+ 'value') and field['value'] or ''
+ params[f.find('textarea')['name']] = 'XYZ'
+ params[f.find('select')['name']] = 'testforum'
+ params[f.find('input', {'style': 'width: 90%'})['name']] = 'AAAA'
+ self.app.post('/discussion/save_new_topic', params=params)
+
+ f = self.app.get('/discussion/feed.rss').body
+ f = feedparser.parse(f)
+ newest_entry = f['entries'][0]['summary_detail']['value'].split("</p>")[0].split("<p>")[-1]
+ assert newest_entry == 'XYZ'
+
def test_thread_sticky(self):
r = self.app.get('/discussion/create_topic/')
f = r.html.find(
[37/37] allura git commit: [#4542] ticket:728 Add more test +
refactoring
Posted by je...@apache.org.
[#4542] ticket:728 Add more test + refactoring
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/b967bc5c
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/b967bc5c
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/b967bc5c
Branch: refs/heads/ib/4542
Commit: b967bc5cc4de9d98cd94fba8dfe86986559b08d9
Parents: a433fa9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 16 11:03:17 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 11:03:17 2015 +0000
----------------------------------------------------------------------
Allura/allura/model/repo_refresh.py | 50 +++++++++++++++++-------------
Allura/allura/tests/test_webhooks.py | 30 ++++++++++++++++++
Allura/allura/tests/unit/test_repo.py | 42 ++++++++++++++++++++++++-
Allura/allura/webhooks.py | 4 +--
4 files changed, 101 insertions(+), 25 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/b967bc5c/Allura/allura/model/repo_refresh.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repo_refresh.py b/Allura/allura/model/repo_refresh.py
index 20bd365..d8ef050 100644
--- a/Allura/allura/model/repo_refresh.py
+++ b/Allura/allura/model/repo_refresh.py
@@ -134,12 +134,6 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False):
log.info('Compute last commit info %d: %s', (i + 1), ci._id)
if not all_commits and not new_clone:
- commits_by_branches = {}
- commits_by_tags = {}
- # svn has no branches, so we need __default__ as a fallback to collect
- # all commits into
- current_branches = ['__default__']
- current_tags = []
for commit in commit_ids:
new = repo.commit(commit)
user = User.by_email_address(new.committed.email)
@@ -153,26 +147,13 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False):
related_nodes=[repo.app_config.project],
tags=['commit', repo.tool.lower()])
- branches, tags = repo.symbolics_for_commit(new)
- if branches:
- current_branches = branches
- if tags:
- current_tags = tags
- for b in current_branches:
- if b not in commits_by_branches.keys():
- commits_by_branches[b] = []
- commits_by_branches[b].append(commit)
- for t in current_tags:
- if t not in commits_by_tags.keys():
- commits_by_tags[t] = []
- commits_by_tags[t].append(commit)
-
from allura.webhooks import RepoPushWebhookSender
+ by_branches, by_tags = _group_commits(repo, commit_ids)
params = []
- for b, commits in commits_by_branches.iteritems():
+ for b, commits in by_branches.iteritems():
ref = u'refs/heads/{}'.format(b) if b != '__default__' else None
params.append(dict(commit_ids=commits, ref=ref))
- for t, commits in commits_by_tags.iteritems():
+ for t, commits in by_tags.iteritems():
ref = u'refs/tags/{}'.format(t)
params.append(dict(commit_ids=commits, ref=ref))
if params:
@@ -646,3 +627,28 @@ def _update_tree_cache(tree_ids, cache):
cached_ids = set(cache.instance_ids(Tree))
new_ids = current_ids - cached_ids
cache.batch_load(Tree, {'_id': {'$in': list(new_ids)}})
+
+
+def _group_commits(repo, commit_ids):
+ by_branches = {}
+ by_tags = {}
+ # svn has no branches, so we need __default__ as a fallback to collect
+ # all commits into
+ current_branches = ['__default__']
+ current_tags = []
+ for commit in commit_ids:
+ ci = repo.commit(commit)
+ branches, tags = repo.symbolics_for_commit(ci)
+ if branches:
+ current_branches = branches
+ if tags:
+ current_tags = tags
+ for b in current_branches:
+ if b not in by_branches.keys():
+ by_branches[b] = []
+ by_branches[b].append(commit)
+ for t in current_tags:
+ if t not in by_tags.keys():
+ by_tags[t] = []
+ by_tags[t].append(commit)
+ return by_branches, by_tags
http://git-wip-us.apache.org/repos/asf/allura/blob/b967bc5c/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 629b322..c0f9140 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -485,6 +485,18 @@ class TestRepoPushWebhookSender(TestWebhookBase):
self.wh._id,
sender.get_payload.return_value)
+ @patch('allura.webhooks.send_webhook', autospec=True)
+ def test_send_with_list(self, send_webhook):
+ sender = RepoPushWebhookSender()
+ sender.get_payload = Mock(side_effect=[1, 2])
+ self.wh.enforce_limit = Mock(return_value=True)
+ with h.push_config(c, app=self.git):
+ sender.send([dict(arg1=1, arg2=2), dict(arg1=3, arg2=4)])
+ assert_equal(send_webhook.post.call_count, 2)
+ assert_equal(send_webhook.post.call_args_list,
+ [call(self.wh._id, 1), call(self.wh._id, 2)])
+ assert_equal(self.wh.enforce_limit.call_count, 1)
+
@patch('allura.webhooks.log', autospec=True)
@patch('allura.webhooks.send_webhook', autospec=True)
def test_send_limit_reached(self, send_webhook, log):
@@ -549,6 +561,24 @@ class TestRepoPushWebhookSender(TestWebhookBase):
add_webhooks('two', 3)
assert_equal(sender.enforce_limit(self.git), False)
+ def test_before(self):
+ sender = RepoPushWebhookSender()
+ with patch.object(self.git.repo, 'commit', autospec=True) as _ci:
+ assert_equal(sender._before(self.git.repo, ['3', '2', '1']), '')
+ _ci.return_value.parent_ids = ['0']
+ assert_equal(sender._before(self.git.repo, ['3', '2', '1']), '0')
+
+ def test_after(self):
+ sender = RepoPushWebhookSender()
+ assert_equal(sender._after([]), '')
+ assert_equal(sender._after(['3', '2', '1']), '3')
+
+ def test_convert_id(self):
+ sender = RepoPushWebhookSender()
+ assert_equal(sender._convert_id(''), '')
+ assert_equal(sender._convert_id('a433fa9'), 'a433fa9')
+ assert_equal(sender._convert_id('a433fa9:13'), 'r13')
+
class TestModels(TestWebhookBase):
http://git-wip-us.apache.org/repos/asf/allura/blob/b967bc5c/Allura/allura/tests/unit/test_repo.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/unit/test_repo.py b/Allura/allura/tests/unit/test_repo.py
index f2554fb..7411db5 100644
--- a/Allura/allura/tests/unit/test_repo.py
+++ b/Allura/allura/tests/unit/test_repo.py
@@ -19,13 +19,18 @@ import datetime
import unittest
from mock import patch, Mock, MagicMock
from nose.tools import assert_equal
+from datadiff import tools as dd
from pylons import tmpl_context as c
from allura import model as M
from allura.controllers.repository import topo_sort
from allura.model.repository import zipdir, prefix_paths_union
-from allura.model.repo_refresh import CommitRunDoc, CommitRunBuilder
+from allura.model.repo_refresh import (
+ CommitRunDoc,
+ CommitRunBuilder,
+ _group_commits,
+)
from alluratest.controller import setup_unit_test
@@ -315,3 +320,38 @@ class TestPrefixPathsUnion(unittest.TestCase):
a = set(['a1', 'a2', 'a3'])
b = set(['b1', 'a2/foo', 'b3/foo'])
self.assertItemsEqual(prefix_paths_union(a, b), ['a2'])
+
+
+class TestGroupCommits(object):
+
+ def setUp(self):
+ self.repo = Mock()
+ self.repo.symbolics_for_commit.return_value = ([], [])
+
+ def test_no_branches(self):
+ b, t = _group_commits(self.repo, ['3', '2', '1'])
+ dd.assert_equal(b, {'__default__': ['3', '2', '1']})
+ dd.assert_equal(t, {})
+
+ def test_branches_and_tags(self):
+ self.repo.symbolics_for_commit.side_effect = [
+ (['master'], ['v1.1']),
+ ([], []),
+ ([], []),
+ ]
+ b, t = _group_commits(self.repo, ['3', '2', '1'])
+ dd.assert_equal(b, {'master': ['3', '2', '1']})
+ dd.assert_equal(t, {'v1.1': ['3', '2', '1']})
+
+ def test_multiple_branches(self):
+ self.repo.symbolics_for_commit.side_effect = [
+ (['master'], ['v1.1']),
+ ([], ['v1.0']),
+ (['test1', 'test2'], []),
+ ]
+ b, t = _group_commits(self.repo, ['3', '2', '1'])
+ dd.assert_equal(b, {'master': ['3', '2'],
+ 'test1': ['1'],
+ 'test2': ['1']})
+ dd.assert_equal(t, {'v1.1': ['3'],
+ 'v1.0': ['2', '1']})
http://git-wip-us.apache.org/repos/asf/allura/blob/b967bc5c/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 03a909d..ba55a89 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -337,7 +337,7 @@ class RepoPushWebhookSender(WebhookSender):
return self._convert_id(parents[-1])
return u''
- def _after(self, repo, commit_ids):
+ def _after(self, commit_ids):
if len(commit_ids) > 0:
return self._convert_id(commit_ids[0])
return u''
@@ -353,7 +353,7 @@ class RepoPushWebhookSender(WebhookSender):
for ci in commits:
ci['id'] = self._convert_id(ci['id'])
before = self._before(app.repo, commit_ids)
- after = self._after(app.repo, commit_ids)
+ after = self._after(commit_ids)
payload = {
'size': len(commits),
'commits': commits,
[18/37] allura git commit: [#4542] ticket:714 Delete webhooks
Posted by je...@apache.org.
[#4542] ticket:714 Delete webhooks
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/ad60782f
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/ad60782f
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/ad60782f
Branch: refs/heads/ib/4542
Commit: ad60782fef61daad65b93dcd7e00c1079468b4d8
Parents: 4e8acf6
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jan 28 14:56:01 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:16:48 2015 +0000
----------------------------------------------------------------------
.../ext/admin/templates/webhooks_list.html | 35 +++++++++++++++++---
Allura/allura/webhooks.py | 19 +++++++++--
2 files changed, 47 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/ad60782f/Allura/allura/ext/admin/templates/webhooks_list.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/webhooks_list.html b/Allura/allura/ext/admin/templates/webhooks_list.html
index d591173..f54b7fb 100644
--- a/Allura/allura/ext/admin/templates/webhooks_list.html
+++ b/Allura/allura/ext/admin/templates/webhooks_list.html
@@ -27,17 +27,20 @@
<p><a href="{{c.app.url}}webhooks/{{ hook.type }}">Create</a></p>
{% if configured_hooks[hook.type] %}
<table>
- {% for h in configured_hooks[hook.type] %}
+ {% for wh in configured_hooks[hook.type] %}
<tr>
<td>
- <a href="{{ h.url() }}">{{ h.hook_url }}</a>
+ <a href="{{ wh.url() }}">{{ wh.hook_url }}</a>
</td>
<td>
- <a href="{{ h.app_config.url() }}">{{ h.app_config.options.mount_label }}</a>
+ <a href="{{ wh.app_config.url() }}">{{ wh.app_config.options.mount_label }}</a>
</td>
- <td>{{ h.secret or '' }}</td>
+ <td>{{ wh.secret or '' }}</td>
<td>
- <a href="#" title="Delete">
+ <a href="{{c.app.url}}webhooks/{{hook.type}}/delete"
+ class="delete-link"
+ data-id="{{h.really_unicode(wh._id)}}"
+ title="Delete">
<b data-icon="{{g.icons['delete'].char}}" class="ico {{g.icons['delete'].css}}" title="Delete"></b>
</a>
</td>
@@ -47,3 +50,25 @@
{% endif %}
{% endfor %}
{% endblock %}
+
+{% block extra_js %}
+<script type="text/javascript">
+$(function() {
+ $('.delete-link').click(function(e) {
+ e.preventDefault();
+ var id = $(this).attr('data-id');
+ var csrf = $.cookie('_session_id');
+ var data = {'webhook': id, '_session_id': csrf};
+ var url = $(this).attr('href');
+ var $tr = $(this).parents('tr')
+ $.post(url, data, function(data) {
+ if (data['status'] == 'ok') {
+ $tr.remove();
+ } else {
+ console.log(data);
+ }
+ });
+ });
+});
+</script>
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/ad60782f/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 8fb62d5..c4c6ca6 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -21,7 +21,7 @@ import hmac
import hashlib
import requests
-from tg import expose, validate, redirect
+from tg import expose, validate, redirect, flash
from tg.decorators import with_trailing_slash, without_trailing_slash
from pylons import tmpl_context as c
from formencode import validators as fev, schema, Invalid
@@ -87,7 +87,7 @@ class WebhookEditForm(WebhookCreateForm):
class WebhookControllerMeta(type):
def __call__(cls, sender, *args, **kw):
- """Decorate the `create` post handler with a validator that references
+ """Decorate post handlers with a validator that references
the appropriate webhook sender for this controller.
"""
if hasattr(cls, 'create'):
@@ -136,6 +136,7 @@ class WebhookController(BaseController):
session(wh).flush(wh)
M.AuditLog.log('add webhook %s %s %s',
wh.type, wh.hook_url, wh.app_config.url())
+ flash('Created successfully', 'ok')
redirect(c.project.url() + 'admin/webhooks/')
@expose()
@@ -154,8 +155,22 @@ class WebhookController(BaseController):
M.AuditLog.log('edit webhook %s\n%s => %s\n%s => %s\n%s',
webhook.type, old_url, url, old_app, app.url(),
'secret changed' if old_secret != secret else '')
+ flash('Edited successfully', 'ok')
redirect(c.project.url() + 'admin/webhooks/')
+ @expose('json:')
+ @require_post()
+ def delete(self, webhook):
+ form = self.edit_form(self.sender)
+ try:
+ wh = form.fields['webhook'].to_python(webhook)
+ except Invalid:
+ raise exc.HTTPNotFound()
+ wh.delete()
+ M.AuditLog.log('delete webhook %s %s %s',
+ wh.type, wh.hook_url, wh.app_config.url())
+ return {'status': 'ok'}
+
@without_trailing_slash
@expose('jinja:allura:templates/webhooks/create_form.html')
def _default(self, webhook, **kw):
[35/37] allura git commit: [#4542] ticket:728 Handle case when every
commit has no branch (i.e. svn)
Posted by je...@apache.org.
[#4542] ticket:728 Handle case when every commit has no branch (i.e. svn)
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/5e5a19d8
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/5e5a19d8
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/5e5a19d8
Branch: refs/heads/ib/4542
Commit: 5e5a19d8405c23a4b05fb9ef1144f325809d776b
Parents: a60ccc9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 13 16:53:34 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:42 2015 +0000
----------------------------------------------------------------------
Allura/allura/model/repo_refresh.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/5e5a19d8/Allura/allura/model/repo_refresh.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repo_refresh.py b/Allura/allura/model/repo_refresh.py
index 1dc17ba..20bd365 100644
--- a/Allura/allura/model/repo_refresh.py
+++ b/Allura/allura/model/repo_refresh.py
@@ -136,7 +136,9 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False):
if not all_commits and not new_clone:
commits_by_branches = {}
commits_by_tags = {}
- current_branches = []
+ # svn has no branches, so we need __default__ as a fallback to collect
+ # all commits into
+ current_branches = ['__default__']
current_tags = []
for commit in commit_ids:
new = repo.commit(commit)
@@ -168,7 +170,7 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False):
from allura.webhooks import RepoPushWebhookSender
params = []
for b, commits in commits_by_branches.iteritems():
- ref = u'refs/heads/{}'.format(b)
+ ref = u'refs/heads/{}'.format(b) if b != '__default__' else None
params.append(dict(commit_ids=commits, ref=ref))
for t, commits in commits_by_tags.iteritems():
ref = u'refs/tags/{}'.format(t)
[05/37] allura git commit: [#7821] ticket:720 More accurate audit
logs when changing user's status
Posted by je...@apache.org.
[#7821] ticket:720 More accurate audit logs when changing user's status
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/09365c95
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/09365c95
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/09365c95
Branch: refs/heads/ib/4542
Commit: 09365c951ef223be5594163995df12942e7a4fd0
Parents: 1daebd1
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 5 11:54:08 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 6 20:42:52 2015 +0000
----------------------------------------------------------------------
Allura/allura/controllers/site_admin.py | 4 +-
Allura/allura/lib/plugin.py | 44 +++++++++++---------
.../allura/tests/functional/test_site_admin.py | 28 +++++++++----
3 files changed, 47 insertions(+), 29 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/09365c95/Allura/allura/controllers/site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/site_admin.py b/Allura/allura/controllers/site_admin.py
index f150370..a85680b 100644
--- a/Allura/allura/controllers/site_admin.py
+++ b/Allura/allura/controllers/site_admin.py
@@ -524,7 +524,7 @@ class AdminUserDetailsController(object):
if not user or user.is_anonymous():
raise HTTPNotFound()
if status == 'enable' and (user.disabled or user.pending):
- AuthenticationProvider.get(request).activate_user(user)
+ AuthenticationProvider.get(request).activate_user(user, audit=False)
AuthenticationProvider.get(request).enable_user(user)
flash('User enabled')
elif status == 'disable' and not user.disabled:
@@ -532,7 +532,7 @@ class AdminUserDetailsController(object):
flash('User disabled')
elif status == 'pending':
AuthenticationProvider.get(request).deactivate_user(user)
- AuthenticationProvider.get(request).enable_user(user)
+ AuthenticationProvider.get(request).enable_user(user, audit=False)
flash('Set user status to pending')
redirect(request.referer)
http://git-wip-us.apache.org/repos/asf/allura/blob/09365c95/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index 5cf1905..f3f90a8 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -185,19 +185,19 @@ class AuthenticationProvider(object):
'''
raise NotImplementedError, 'validate_password'
- def disable_user(self, user):
+ def disable_user(self, user, **kw):
'''Disable user account'''
raise NotImplementedError, 'disable_user'
- def enable_user(self, user):
+ def enable_user(self, user, **kw):
'''Enable user account'''
raise NotImplementedError, 'enable_user'
- def activate_user(self, user):
+ def activate_user(self, user, **kw):
'''Activate user after registration'''
raise NotImplementedError, 'activate_user'
- def deactivate_user(self, user):
+ def deactivate_user(self, user, **kw):
'''Deactivate user (== registation not confirmed)'''
raise NotImplementedError, 'deactivate_user'
@@ -355,25 +355,29 @@ class LocalAuthenticationProvider(AuthenticationProvider):
raise exc.HTTPUnauthorized()
return user
- def disable_user(self, user):
+ def disable_user(self, user, **kw):
user.disabled = True
session(user).flush(user)
- h.auditlog_user(u'Account disabled', user=user)
+ if kw.get('audit', True):
+ h.auditlog_user(u'Account disabled', user=user)
- def enable_user(self, user):
+ def enable_user(self, user, **kw):
user.disabled = False
session(user).flush(user)
- h.auditlog_user(u'Account enabled', user=user)
+ if kw.get('audit', True):
+ h.auditlog_user(u'Account enabled', user=user)
- def activate_user(self, user):
+ def activate_user(self, user, **kw):
user.pending = False
session(user).flush(user)
- h.auditlog_user('Account activated', user=user)
+ if kw.get('audit', True):
+ h.auditlog_user('Account activated', user=user)
- def deactivate_user(self, user):
+ def deactivate_user(self, user, **kw):
user.pending = True
session(user).flush(user)
- h.auditlog_user('Account deactivated', user=user)
+ if kw.get('audit', True):
+ h.auditlog_user('Account changed to pending', user=user)
def validate_password(self, user, password):
return self._validate_password(user, password)
@@ -624,17 +628,17 @@ class LdapAuthenticationProvider(AuthenticationProvider):
def update_notifications(self, user):
return LocalAuthenticationProvider(None).update_notifications(user)
- def disable_user(self, user):
- return LocalAuthenticationProvider(None).disable_user(user)
+ def disable_user(self, user, **kw):
+ return LocalAuthenticationProvider(None).disable_user(user, **kw)
- def enable_user(self, user):
- return LocalAuthenticationProvider(None).enable_user(user)
+ def enable_user(self, user, **kw):
+ return LocalAuthenticationProvider(None).enable_user(user, **kw)
- def activate_user(self, user):
- return LocalAuthenticationProvider(None).activate_user(user)
+ def activate_user(self, user, **kw):
+ return LocalAuthenticationProvider(None).activate_user(user, **kw)
- def deactivate_user(self, user):
- return LocalAuthenticationProvider(None).deactivate_user(user)
+ def deactivate_user(self, user, **kw):
+ return LocalAuthenticationProvider(None).deactivate_user(user, **kw)
def get_last_password_updated(self, user):
return LocalAuthenticationProvider(None).get_last_password_updated(user)
http://git-wip-us.apache.org/repos/asf/allura/blob/09365c95/Allura/allura/tests/functional/test_site_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_site_admin.py b/Allura/allura/tests/functional/test_site_admin.py
index 17c23f3..e89b9fd 100644
--- a/Allura/allura/tests/functional/test_site_admin.py
+++ b/Allura/allura/tests/functional/test_site_admin.py
@@ -356,7 +356,9 @@ class TestUserDetails(TestController):
assert_equal(form['username'].value, 'test-user-3')
assert_equal(form['status'].value, 'enable')
form['status'].value = 'disable'
- r = form.submit()
+ with td.audits('Account disabled', user=True):
+ r = form.submit()
+ assert_equal(M.AuditLog.query.find().count(), 1)
assert_in(u'User disabled', self.webflash(r))
assert_equal(M.User.by_username('test-user-3').disabled, True)
assert_equal(M.User.by_username('test-user-3').pending, False)
@@ -373,7 +375,9 @@ class TestUserDetails(TestController):
assert_equal(form['username'].value, 'test-user-3')
assert_equal(form['status'].value, 'pending')
form['status'].value = 'disable'
- r = form.submit()
+ with td.audits('Account disabled', user=True):
+ r = form.submit()
+ assert_equal(M.AuditLog.query.find().count(), 1)
assert_in(u'User disabled', self.webflash(r))
assert_equal(M.User.by_username('test-user-3').disabled, True)
assert_equal(M.User.by_username('test-user-3').pending, True)
@@ -390,7 +394,9 @@ class TestUserDetails(TestController):
assert_equal(form['username'].value, 'test-user-3')
assert_equal(form['status'].value, 'disable')
form['status'].value = 'enable'
- r = form.submit()
+ with td.audits('Account enabled', user=True):
+ r = form.submit()
+ assert_equal(M.AuditLog.query.find().count(), 1)
assert_in(u'User enabled', self.webflash(r))
assert_equal(M.User.by_username('test-user-3').disabled, False)
assert_equal(M.User.by_username('test-user-3').pending, False)
@@ -407,7 +413,9 @@ class TestUserDetails(TestController):
assert_equal(form['username'].value, 'test-user-3')
assert_equal(form['status'].value, 'pending')
form['status'].value = 'enable'
- r = form.submit()
+ with td.audits('Account enabled', user=True):
+ r = form.submit()
+ assert_equal(M.AuditLog.query.find().count(), 1)
assert_in(u'User enabled', self.webflash(r))
assert_equal(M.User.by_username('test-user-3').disabled, False)
assert_equal(M.User.by_username('test-user-3').pending, False)
@@ -424,7 +432,9 @@ class TestUserDetails(TestController):
assert_equal(form['username'].value, 'test-user-3')
assert_equal(form['status'].value, 'disable')
form['status'].value = 'enable'
- r = form.submit()
+ with td.audits('Account enabled', user=True):
+ r = form.submit()
+ assert_equal(M.AuditLog.query.find().count(), 1)
assert_in(u'User enabled', self.webflash(r))
assert_equal(M.User.by_username('test-user-3').disabled, False)
assert_equal(M.User.by_username('test-user-3').pending, False)
@@ -441,7 +451,9 @@ class TestUserDetails(TestController):
assert_equal(form['username'].value, 'test-user-3')
assert_equal(form['status'].value, 'disable')
form['status'].value = 'pending'
- r = form.submit()
+ with td.audits('Account changed to pending', user=True):
+ r = form.submit()
+ assert_equal(M.AuditLog.query.find().count(), 1)
assert_in(u'Set user status to pending', self.webflash(r))
assert_equal(M.User.by_username('test-user-3').disabled, False)
assert_equal(M.User.by_username('test-user-3').pending, True)
@@ -458,7 +470,9 @@ class TestUserDetails(TestController):
assert_equal(form['username'].value, 'test-user-3')
assert_equal(form['status'].value, 'enable')
form['status'].value = 'pending'
- r = form.submit()
+ with td.audits('Account changed to pending', user=True):
+ r = form.submit()
+ assert_equal(M.AuditLog.query.find().count(), 1)
assert_in(u'Set user status to pending', self.webflash(r))
assert_equal(M.User.by_username('test-user-3').disabled, False)
assert_equal(M.User.by_username('test-user-3').pending, True)
[13/37] allura git commit: [#7823] ticket:724 Add tests for new
EmailAddress.canonical behavior
Posted by je...@apache.org.
[#7823] ticket:724 Add tests for new EmailAddress.canonical behavior
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/1c2d973c
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/1c2d973c
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/1c2d973c
Branch: refs/heads/ib/4542
Commit: 1c2d973c7961195101699270a6dff0e3af644337
Parents: f155a68
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 12 10:28:46 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 13 20:48:35 2015 +0000
----------------------------------------------------------------------
Allura/allura/tests/model/test_auth.py | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/1c2d973c/Allura/allura/tests/model/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/model/test_auth.py b/Allura/allura/tests/model/test_auth.py
index 3356ce1..12db982 100644
--- a/Allura/allura/tests/model/test_auth.py
+++ b/Allura/allura/tests/model/test_auth.py
@@ -74,6 +74,7 @@ def test_email_address():
@with_setup(setUp)
def test_email_address_lookup_helpers():
addr = M.EmailAddress.create('TEST@DOMAIN.NET')
+ nobody = M.EmailAddress.create('nobody@example.com')
ThreadLocalORMSession.flush_all()
assert_equal(addr.email, 'TEST@domain.net')
@@ -81,14 +82,30 @@ def test_email_address_lookup_helpers():
assert_equal(M.EmailAddress.get(email='TEST@domain.net'), addr)
assert_equal(M.EmailAddress.get(email='test@domain.net'), None)
assert_equal(M.EmailAddress.get(email=None), None)
+ assert_equal(M.EmailAddress.get(email='nobody@example.com'), nobody)
+ # invalid email returns None, but not nobody@example.com as before
+ assert_equal(M.EmailAddress.get(email='invalid'), None)
assert_equal(M.EmailAddress.find(dict(email='TEST@DOMAIN.NET')).all(), [addr])
assert_equal(M.EmailAddress.find(dict(email='TEST@domain.net')).all(), [addr])
assert_equal(M.EmailAddress.find(dict(email='test@domain.net')).all(), [])
assert_equal(M.EmailAddress.find(dict(email=None)).all(), [])
+ assert_equal(M.EmailAddress.find(dict(email='nobody@example.com')).all(), [nobody])
+ # invalid email returns empty query, but not nobody@example.com as before
+ assert_equal(M.EmailAddress.find(dict(email='invalid')).all(), [])
@with_setup(setUp)
+def test_email_address_canonical():
+ assert_equal(M.EmailAddress.canonical('nobody@EXAMPLE.COM'),
+ 'nobody@example.com')
+ assert_equal(M.EmailAddress.canonical('nobody@example.com'),
+ 'nobody@example.com')
+ assert_equal(M.EmailAddress.canonical('I Am Nobody <no...@example.com>'),
+ 'nobody@example.com')
+ assert_equal(M.EmailAddress.canonical('invalid'), None)
+
+@with_setup(setUp)
def test_email_address_send_verification_link():
addr = M.EmailAddress(email='test_admin@domain.net',
claimed_by_user_id=c.user._id)
@@ -178,7 +195,6 @@ def test_user_by_email_address(log):
claimed_by_user_id=u1._id)
addr2 = M.EmailAddress(email='abc123@abc.me', confirmed=True,
claimed_by_user_id=u2._id)
-
# both users are disabled
u1.disabled, u2.disabled = True, True
ThreadLocalORMSession.flush_all()
@@ -197,6 +213,14 @@ def test_user_by_email_address(log):
assert_in(M.User.by_email_address('abc123@abc.me'), [u1, u2])
assert_equal(log.warn.call_count, 1)
+ # invalid email returns None, but not user which claimed
+ # nobody@example.com as before
+ nobody = M.EmailAddress(email='nobody@example.com', confirmed=True,
+ claimed_by_user_id=u1._id)
+ ThreadLocalORMSession.flush_all()
+ assert_equal(M.User.by_email_address('nobody@example.com'), u1)
+ assert_equal(M.User.by_email_address('invalid'), None)
+
@with_setup(setUp)
def test_project_role():
[07/37] allura git commit: [#7824] ticket:721 Cache neighborhood
record
Posted by je...@apache.org.
[#7824] ticket:721 Cache neighborhood record
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/724ba595
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/724ba595
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/724ba595
Branch: refs/heads/ib/4542
Commit: 724ba5951657b96967209a69d7ee9d324c6699b2
Parents: 09365c9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 5 09:59:15 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 9 07:47:57 2015 +0000
----------------------------------------------------------------------
Allura/allura/controllers/root.py | 3 +-
Allura/allura/lib/app_globals.py | 34 +++++++++++++++
Allura/allura/tests/test_globals.py | 74 +++++++++++++++++++++++++++++++-
Allura/development.ini | 4 ++
4 files changed, 113 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/724ba595/Allura/allura/controllers/root.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/root.py b/Allura/allura/controllers/root.py
index e588d45..a0418d0 100644
--- a/Allura/allura/controllers/root.py
+++ b/Allura/allura/controllers/root.py
@@ -24,6 +24,7 @@ from tg import expose, request, config, session
from tg.decorators import with_trailing_slash
from tg.flash import TGFlash
from pylons import tmpl_context as c
+from pylons import app_globals as g
from paste.deploy.converters import asbool
from allura.app import SitemapEntry
@@ -75,7 +76,7 @@ class RootController(WsgiDispatchController):
def __init__(self):
n_url_prefix = '/%s/' % request.path.split('/')[1]
- n = M.Neighborhood.query.get(url_prefix=n_url_prefix)
+ n = g.neighborhood_cache.get(n_url_prefix)
if n and not n.url_prefix.startswith('//'):
n.bind_controller(self)
self.browse = ProjectBrowseController()
http://git-wip-us.apache.org/repos/asf/allura/blob/724ba595/Allura/allura/lib/app_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index d196cc8..f3cb551 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -130,6 +130,36 @@ class ForgeMarkdown(markdown.Markdown):
return html
+class NeighborhoodCache(object):
+ """Cached Neighborhood objects by url_prefix.
+ For faster RootController.__init__ lookup
+ """
+
+ def __init__(self, duration):
+ self.duration = duration
+ self._data = {}
+
+ def _lookup(self, url_prefix):
+ n = M.Neighborhood.query.get(url_prefix=url_prefix)
+ self._data[url_prefix] = {
+ 'object': n,
+ 'ts': datetime.datetime.utcnow(),
+ }
+ return n
+
+ def _expired(self, n):
+ delta = datetime.datetime.utcnow() - n['ts']
+ if delta >= datetime.timedelta(seconds=self.duration):
+ return True
+ return False
+
+ def get(self, url_prefix):
+ n = self._data.get(url_prefix)
+ if n and not self._expired(n):
+ return n['object']
+ return self._lookup(url_prefix)
+
+
class Globals(object):
"""Container for objects available throughout the life of the application.
@@ -256,6 +286,10 @@ class Globals(object):
macros=_cache_eps('allura.macros'),
)
+ # Neighborhood cache
+ duration = asint(config.get('neighborhood.cache.duration', 0))
+ self.neighborhood_cache = NeighborhoodCache(duration)
+
# Zarkov logger
self._zarkov = None
http://git-wip-us.apache.org/repos/asf/allura/blob/724ba595/Allura/allura/tests/test_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_globals.py b/Allura/allura/tests/test_globals.py
index dd4e7e6..53c8af1 100644
--- a/Allura/allura/tests/test_globals.py
+++ b/Allura/allura/tests/test_globals.py
@@ -23,6 +23,7 @@ import os
import allura
import unittest
import hashlib
+import datetime as dt
from mock import patch, Mock
from bson import ObjectId
@@ -41,7 +42,7 @@ from alluratest.controller import (
from allura import model as M
from allura.lib import helpers as h
-from allura.lib.app_globals import ForgeMarkdown
+from allura.lib.app_globals import ForgeMarkdown, NeighborhoodCache
from allura.tests import decorators as td
from forgewiki import model as WM
@@ -774,3 +775,74 @@ class TestHandlePaging(unittest.TestCase):
self.assertEqual(g.handle_paging(None, 2, 30), (25, 2, 50))
# handle paging must not mess up user preferences
self.assertEqual(c.user.get_pref('results_per_page'), 25)
+
+
+class TestNeighborhoodCache(object):
+
+ @patch('allura.lib.app_globals.M', autospec=True)
+ @patch('allura.lib.app_globals.datetime', autospec=True)
+ def test_lookup(self, dt_mock, M):
+ dt_mock.datetime.utcnow.side_effect = [
+ dt.datetime(2015, 02, 05, 11, 32),
+ dt.datetime(2015, 02, 05, 11, 34),
+ ]
+ ret = M.Neighborhood.query.get.return_value
+ cache = NeighborhoodCache(30)
+ assert_equal(cache._data, {})
+
+ n = cache._lookup('/p/')
+ M.Neighborhood.query.get.assert_called_once_with(url_prefix='/p/')
+ assert_equal(n, ret)
+ assert_equal(cache._data, {'/p/': {
+ 'object': ret,
+ 'ts': dt.datetime(2015, 02, 05, 11, 32),
+ }})
+
+ # hits mongo every time
+ n = cache._lookup('/p/')
+ assert_equal(M.Neighborhood.query.get.call_count, 2)
+ assert_equal(n, ret)
+ assert_equal(cache._data, {'/p/': {
+ 'object': ret,
+ 'ts': dt.datetime(2015, 02, 05, 11, 34),
+ }})
+
+ @patch('allura.lib.app_globals.M', autospec=True)
+ @patch('allura.lib.app_globals.datetime', autospec=True)
+ def test_get(self, dt_mock, M):
+ dt_mock.datetime.utcnow.side_effect = [
+ dt.datetime(2015, 02, 05, 11, 32),
+ dt.datetime(2015, 02, 05, 11, 34),
+ ]
+ ret = M.Neighborhood.query.get.return_value
+ cache = NeighborhoodCache(30)
+ cache._expired = Mock(return_value=False)
+
+ n = cache.get('/p/')
+ M.Neighborhood.query.get.assert_called_once_with(url_prefix='/p/')
+ assert_equal(n, ret)
+
+ # don't hit mongo second time
+ n = cache.get('/p/')
+ assert_equal(M.Neighborhood.query.get.call_count, 1)
+ assert_equal(n, ret)
+
+ # and hits if cache is expired
+ cache._expired.return_value = True
+ n = cache.get('/p/')
+ assert_equal(M.Neighborhood.query.get.call_count, 2)
+ assert_equal(n, ret)
+
+ @patch('allura.lib.app_globals.datetime', autospec=True)
+ def test_expired(self, dt_mock):
+ dt_mock.timedelta = dt.timedelta # restore original
+ _now = dt.datetime(2015, 02, 05, 11, 53)
+ dt_mock.datetime.utcnow.return_value = _now
+
+ cache = NeighborhoodCache(0)
+ assert_equal(cache._expired({'ts': _now}), True)
+ assert_equal(cache._expired({'ts': _now - dt.timedelta(seconds=1)}), True)
+
+ cache = NeighborhoodCache(30)
+ assert_equal(cache._expired({'ts': _now - dt.timedelta(seconds=29)}), False)
+ assert_equal(cache._expired({'ts': _now - dt.timedelta(seconds=30)}), True)
http://git-wip-us.apache.org/repos/asf/allura/blob/724ba595/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index e5be8b2..c6425a8 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -54,6 +54,10 @@ base_url = http://localhost:8080
#lang = ru
cache_dir = %(here)s/data
+# Cache Neighborhood objects for N seconds (speeds up requests).
+# Set to 0 to disable (the default).
+# neighborhood.cache.duration = 0
+
; Docs at http://beaker.readthedocs.org/en/latest/configuration.html#session-options
; and http://beaker.readthedocs.org/en/latest/modules/session.html#beaker.session.CookieSession
beaker.session.key = allura
[29/37] allura git commit: [#4542] ticket:726 Add edit link and
truncate long urls
Posted by je...@apache.org.
[#4542] ticket:726 Add edit link and truncate long urls
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/10cbeeac
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/10cbeeac
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/10cbeeac
Branch: refs/heads/ib/4542
Commit: 10cbeeacb88eb8501f9dbcf1a4cb6771a8ab731b
Parents: 8cbd330
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Feb 11 15:54:33 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:40 2015 +0000
----------------------------------------------------------------------
Allura/allura/templates/app_admin_webhooks_list.html | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/10cbeeac/Allura/allura/templates/app_admin_webhooks_list.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/app_admin_webhooks_list.html b/Allura/allura/templates/app_admin_webhooks_list.html
index 0fe02e9..a4efb97 100644
--- a/Allura/allura/templates/app_admin_webhooks_list.html
+++ b/Allura/allura/templates/app_admin_webhooks_list.html
@@ -29,10 +29,9 @@
<table>
{% for wh in configured_hooks[hook.type] %}
<tr>
- <td>
- <a href="{{ wh.url() }}">{{ wh.hook_url }}</a>
- </td>
+ <td title="{{wh.hook_url}}">{{ wh.hook_url|truncate(40, True) }}</td>
<td>{{ wh.secret or '' }}</td>
+ <td><a href="{{wh.url()}}">Edit</a></td>
<td>
<a href="{{admin_url}}/{{hook.type}}/delete"
class="delete-link"
[30/37] allura git commit: [#4542] ticket:728 Use admin_url in
Webhook.url()
Posted by je...@apache.org.
[#4542] ticket:728 Use admin_url in Webhook.url()
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/a22f6f62
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/a22f6f62
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/a22f6f62
Branch: refs/heads/ib/4542
Commit: a22f6f62282bb3052746870541e79ff1da37772c
Parents: 289eed1
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 13 13:14:01 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:41 2015 +0000
----------------------------------------------------------------------
Allura/allura/app.py | 2 +-
Allura/allura/model/webhook.py | 7 +++----
2 files changed, 4 insertions(+), 5 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/a22f6f62/Allura/allura/app.py
----------------------------------------------------------------------
diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 388b8a2..5a6db3e 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -287,7 +287,7 @@ class Application(object):
@LazyProperty
def admin_url(self):
return '{}{}/{}/'.format(
- c.project.url(), 'admin',
+ self.project.url(), 'admin',
self.config.options.mount_point)
@property
http://git-wip-us.apache.org/repos/asf/allura/blob/a22f6f62/Allura/allura/model/webhook.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
index 4c39309..563f871 100644
--- a/Allura/allura/model/webhook.py
+++ b/Allura/allura/model/webhook.py
@@ -36,10 +36,9 @@ class Webhook(Artifact):
last_sent = FieldProperty(dt.datetime, if_missing=None)
def url(self):
- return '{}{}/{}/webhooks/{}/{}'.format(
- self.app_config.project.url(), 'admin',
- self.app_config.options.mount_point,
- self.type, self._id)
+ app = self.app_config.load()
+ app = app(self.app_config.project, self.app_config)
+ return '{}webhooks/{}/{}'.format(app.admin_url, self.type, self._id)
def enforce_limit(self):
'''Returns False if limit is reached, otherwise True'''
[19/37] allura git commit: [#4542] ticket:714 Edit webhooks &
validation improvements
Posted by je...@apache.org.
[#4542] ticket:714 Edit webhooks & validation improvements
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/4e8acf6b
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/4e8acf6b
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/4e8acf6b
Branch: refs/heads/ib/4542
Commit: 4e8acf6b7bba66b7538565c02d1bdbf251f5e015
Parents: 7d8c47b
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jan 28 13:40:26 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:16:48 2015 +0000
----------------------------------------------------------------------
Allura/allura/ext/admin/admin_main.py | 2 +-
.../ext/admin/templates/webhooks_list.html | 2 +-
.../allura/templates/webhooks/create_form.html | 11 +-
Allura/allura/webhooks.py | 115 ++++++++++++++++---
4 files changed, 106 insertions(+), 24 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/4e8acf6b/Allura/allura/ext/admin/admin_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index 52b80f9..787bb8c 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -201,7 +201,7 @@ class WebhooksLookup(BaseController):
webhooks = [ep.load() for ep in webhooks]
return webhooks
- @with_trailing_slash
+ @without_trailing_slash
@expose('jinja:allura.ext.admin:templates/webhooks_list.html')
def index(self):
webhooks = self._webhooks
http://git-wip-us.apache.org/repos/asf/allura/blob/4e8acf6b/Allura/allura/ext/admin/templates/webhooks_list.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/webhooks_list.html b/Allura/allura/ext/admin/templates/webhooks_list.html
index 39df656..d591173 100644
--- a/Allura/allura/ext/admin/templates/webhooks_list.html
+++ b/Allura/allura/ext/admin/templates/webhooks_list.html
@@ -24,7 +24,7 @@
{% block content %}
{% for hook in webhooks %}
<h1>{{ hook.type }}</h1>
- <p><a href="{{ hook.type }}">Create</a></p>
+ <p><a href="{{c.app.url}}webhooks/{{ hook.type }}">Create</a></p>
{% if configured_hooks[hook.type] %}
<table>
{% for h in configured_hooks[hook.type] %}
http://git-wip-us.apache.org/repos/asf/allura/blob/4e8acf6b/Allura/allura/templates/webhooks/create_form.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/webhooks/create_form.html b/Allura/allura/templates/webhooks/create_form.html
index f3eb713..c5ccdfb 100644
--- a/Allura/allura/templates/webhooks/create_form.html
+++ b/Allura/allura/templates/webhooks/create_form.html
@@ -18,9 +18,9 @@
-#}
{% extends g.theme.master %}
-{% block title %}{{c.project.name}} / Create {{webhook.type}} webhook{% endblock %}
+{% block title %}{{c.project.name}} / {{action|capitalize}} {{sender.type}} webhook{% endblock %}
-{% block header %}Create {{webhook.type}} webhook{% endblock %}
+{% block header %}{{action|capitalize}} {{sender.type}} webhook{% endblock %}
{% block extra_css %}
<style type="text/css">
@@ -64,7 +64,7 @@
{%- endmacro %}
{% block content %}
-<form action="create" method="post" enctype="multipart/form-data">
+<form action="{{action}}" method="post" enctype="multipart/form-data">
<div>
<label for="url">url</label>
<input name="url" value="{{ c.form_values['url'] }}">
@@ -89,7 +89,10 @@
{% block additional_fields %}{% endblock %}
- <input type="submit" value="Create">
+ <input type="submit" value="{{action|capitalize}}">
+ {% if c.form_values['webhook'] %}
+ <input type="hidden" name="webhook" value="{{c.form_values['webhook']}}">
+ {% endif %}
{{lib.csrf_token()}}
</form>
{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/4e8acf6b/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 9bb99e3..8fb62d5 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -21,15 +21,16 @@ import hmac
import hashlib
import requests
-from bson import ObjectId
from tg import expose, validate, redirect
-from tg.decorators import with_trailing_slash
+from tg.decorators import with_trailing_slash, without_trailing_slash
from pylons import tmpl_context as c
-from formencode import validators as fev, schema
+from formencode import validators as fev, schema, Invalid
from ming.odm import session
+from webob import exc
from allura.controllers import BaseController
from allura.lib import helpers as h
+from allura.lib import validators as av
from allura.lib.decorators import require_post, task
from allura.lib.utils import DateJSONEncoder
from allura import model as M
@@ -38,61 +39,139 @@ from allura import model as M
log = logging.getLogger(__name__)
+class MingOneOf(av.Ming):
+ def __init__(self, ids, **kw):
+ self.ids = ids
+ super(MingOneOf, self).__init__(**kw)
+
+ def _to_python(self, value, state):
+ result = super(MingOneOf, self)._to_python(value, state)
+ if result and result._id in self.ids:
+ return result
+ raise Invalid(
+ u'Object must be one of: {}, not {}'.format(self.ids, value),
+ value, state)
+
+
+class WebhookValidator(av.Ming):
+ def __init__(self, sender, ac_ids, **kw):
+ self.ac_ids = ac_ids
+ self.sender = sender
+ super(WebhookValidator, self).__init__(cls=M.Webhook, **kw)
+
+ def _to_python(self, value, state):
+ wh = super(WebhookValidator, self)._to_python(value, state)
+ if wh and wh.type == self.sender.type and wh.app_config_id in self.ac_ids:
+ return wh
+ raise Invalid(u'Invalid webhook', value, state)
+
+
class WebhookCreateForm(schema.Schema):
- def __init__(self, hook):
+ def __init__(self, sender):
super(WebhookCreateForm, self).__init__()
self.triggered_by = [ac for ac in c.project.app_configs
- if ac.tool_name.lower() in hook.triggered_by]
- self.add_field('app', fev.OneOf(
- [unicode(ac._id) for ac in self.triggered_by]))
+ if ac.tool_name.lower() in sender.triggered_by]
+ self.add_field('app', MingOneOf(
+ cls=M.AppConfig, ids=[ac._id for ac in self.triggered_by]))
url = fev.URL(not_empty=True)
secret = fev.UnicodeString()
+class WebhookEditForm(WebhookCreateForm):
+ def __init__(self, sender):
+ super(WebhookEditForm, self).__init__(sender)
+ self.add_field('webhook', WebhookValidator(
+ sender=sender, ac_ids=[ac._id for ac in self.triggered_by]))
+
+
class WebhookControllerMeta(type):
- def __call__(cls, hook, *args, **kw):
+ def __call__(cls, sender, *args, **kw):
"""Decorate the `create` post handler with a validator that references
the appropriate webhook sender for this controller.
"""
if hasattr(cls, 'create'):
cls.create = validate(
- cls.create_form(hook),
+ cls.create_form(sender),
error_handler=cls.index.__func__,
)(cls.create)
- return type.__call__(cls, hook, *args, **kw)
+ if hasattr(cls, 'edit'):
+ cls.edit = validate(
+ cls.edit_form(sender),
+ error_handler=cls._default.__func__,
+ )(cls.edit)
+ return type.__call__(cls, sender, *args, **kw)
class WebhookController(BaseController):
__metaclass__ = WebhookControllerMeta
create_form = WebhookCreateForm
+ edit_form = WebhookEditForm
- def __init__(self, hook):
+ def __init__(self, sender):
super(WebhookController, self).__init__()
- self.webhook = hook
+ self.sender = sender
+
+ def gen_secret(self):
+ return h.cryptographic_nonce(20)
@with_trailing_slash
@expose('jinja:allura:templates/webhooks/create_form.html')
def index(self, **kw):
- return {'webhook': self.webhook,
- 'form': self.create_form(self.webhook)}
+ return {'sender': self.sender,
+ 'action': 'create',
+ 'form': self.create_form(self.sender)}
@expose()
@require_post()
- def create(self, url, app, secret=None):
+ def create(self, url, app, secret):
if not secret:
- secret = h.cryptographic_nonce(20)
+ secret = self.gen_secret()
# TODO: catch DuplicateKeyError
wh = M.Webhook(
hook_url=url,
secret=secret,
- app_config_id=ObjectId(app),
- type=self.webhook.type)
+ app_config_id=app._id,
+ type=self.sender.type)
session(wh).flush(wh)
M.AuditLog.log('add webhook %s %s %s',
wh.type, wh.hook_url, wh.app_config.url())
redirect(c.project.url() + 'admin/webhooks/')
+ @expose()
+ @require_post()
+ def edit(self, webhook, url, app, secret):
+ if not secret:
+ secret = self.gen_secret()
+ old_url = webhook.hook_url
+ old_app = webhook.app_config.url()
+ old_secret = webhook.secret
+ webhook.hook_url = url
+ webhook.app_config_id = app._id
+ webhook.secret = secret
+ # TODO: duplicate
+ session(webhook).flush(webhook)
+ M.AuditLog.log('edit webhook %s\n%s => %s\n%s => %s\n%s',
+ webhook.type, old_url, url, old_app, app.url(),
+ 'secret changed' if old_secret != secret else '')
+ redirect(c.project.url() + 'admin/webhooks/')
+
+ @without_trailing_slash
+ @expose('jinja:allura:templates/webhooks/create_form.html')
+ def _default(self, webhook, **kw):
+ form = self.edit_form(self.sender)
+ try:
+ wh = form.fields['webhook'].to_python(webhook)
+ except Invalid:
+ raise exc.HTTPNotFound()
+ c.form_values = {'url': kw.get('url') or wh.hook_url,
+ 'app': kw.get('app') or unicode(wh.app_config_id),
+ 'secret': kw.get('secret') or wh.secret,
+ 'webhook': unicode(wh._id)}
+ return {'sender': self.sender,
+ 'action': 'edit',
+ 'form': form}
+
@task()
def send_webhook(webhook_id, payload):
[11/37] allura git commit: CHANGES updated for ASF release 1.2.1
Posted by je...@apache.org.
CHANGES updated for ASF release 1.2.1
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/17d42762
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/17d42762
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/17d42762
Branch: refs/heads/ib/4542
Commit: 17d427623a30fd9c6986ea137f08a197f3925946
Parents: 1f019a1
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 13 12:58:29 2015 +0200
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Feb 13 12:58:29 2015 +0200
----------------------------------------------------------------------
CHANGES | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/17d42762/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 6136268..76a4292 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,25 @@
+Version 1.2.1 (February 2015)
+
+Bug Fixes & Minor Improvements
+
+ * [#5726] RSS feed for discussion stopped 12/13/2012? [ss2637]
+ * [#6248] long lines in markdown lists get truncated on the right [ss4073]
+ * [#7772] Type text is splitted in more lines if separated by spaces in bulk edit
+ * [#7813] Handle uppercase in email address all the time
+ * [#7815] KeyError: 'name'
+ * [#7808] Check for wiki presence before importing it
+ * [#7831] Logout issue
+ Administration:
+ * [#7816] Show/manage user's pending status
+ * [#7821] More accurate audit logs when changing user's status
+ Performance:
+ * [#7824] Cache neighborhood record
+ For developers:
+ * [#7516] Timing may case test_set_password_sets_last_updated to fail
+ * [#7795] test_version_race fails occassionally
+ * [#7819] New email address lookup helpers fail on None
+
+
Version 1.2.0 (December 2014)
Upgrade Instructions
[06/37] allura git commit: [#7824] remove redirect if proj nbhd name
differs from nbhd name
Posted by je...@apache.org.
[#7824] remove redirect if proj nbhd name differs from nbhd name
This could potentially cause problems when neighborhood.cache.duration is set.
I believe this redirect harkens back to some early days of Allura where
project shortnames were unique across the system, not per-neighborhood. So
if a project was accessed via the wrong neighborhood, this would redirect it
to the canonical neighborhood. But now that's not even possible.
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/70df2e8f
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/70df2e8f
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/70df2e8f
Branch: refs/heads/ib/4542
Commit: 70df2e8fe6c121b0ba03a68c34defd3b6aaa0169
Parents: 724ba59
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Feb 6 18:46:28 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 9 07:47:57 2015 +0000
----------------------------------------------------------------------
Allura/allura/controllers/project.py | 2 --
1 file changed, 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/70df2e8f/Allura/allura/controllers/project.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index 8f2b2ef..201ee54 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -129,8 +129,6 @@ class NeighborhoodController(object):
c.project = project
if project is None or (project.deleted and not has_access(c.project, 'update')()):
raise exc.HTTPNotFound, pname
- if project.neighborhood.name != self.neighborhood_name:
- redirect(project.url())
return ProjectController(), remainder
@expose('jinja:allura:templates/neighborhood_project_list.html')
[21/37] allura git commit: [#4542] ticket:714 Add tests for new
webhook functionality
Posted by je...@apache.org.
[#4542] ticket:714 Add tests for new webhook functionality
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/e7ace573
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/e7ace573
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/e7ace573
Branch: refs/heads/ib/4542
Commit: e7ace573ae8995955fb37de7ec62cd87e9d7bc2e
Parents: ba555ec
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Jan 30 15:01:49 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:38 2015 +0000
----------------------------------------------------------------------
Allura/allura/tests/test_utils.py | 9 +
Allura/allura/tests/test_webhooks.py | 469 +++++++++++++++++++
Allura/allura/webhooks.py | 2 +-
.../forgegit/tests/model/test_repository.py | 25 +
.../forgesvn/tests/model/test_repository.py | 25 +
5 files changed, 529 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/Allura/allura/tests/test_utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_utils.py b/Allura/allura/tests/test_utils.py
index e5f9c43..06579c5 100644
--- a/Allura/allura/tests/test_utils.py
+++ b/Allura/allura/tests/test_utils.py
@@ -17,8 +17,10 @@
# specific language governing permissions and limitations
# under the License.
+import json
import time
import unittest
+import datetime as dt
from os import path
from webob import Request
@@ -295,3 +297,10 @@ def test_empty_cursor():
assert_raises(ValueError, cursor.one)
assert_raises(StopIteration, cursor.next)
assert_raises(StopIteration, cursor._next_impl)
+
+
+def test_DateJSONEncoder():
+ data = {'message': u'Hi!',
+ 'date': dt.datetime(2015, 01, 30, 13, 13, 13)}
+ result = json.dumps(data, cls=utils.DateJSONEncoder)
+ assert_equal(result, '{"date": "2015-01-30T13:13:13Z", "message": "Hi!"}')
http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
new file mode 100644
index 0000000..fa0305f
--- /dev/null
+++ b/Allura/allura/tests/test_webhooks.py
@@ -0,0 +1,469 @@
+import json
+import hmac
+import hashlib
+
+from mock import Mock, patch
+from nose.tools import (
+ assert_raises,
+ assert_equal,
+ assert_not_in,
+ assert_in,
+)
+from formencode import Invalid
+from ming.odm import session
+from pylons import tmpl_context as c
+
+from allura import model as M
+from allura.lib import helpers as h
+from allura.lib.utils import DateJSONEncoder
+from allura.webhooks import (
+ MingOneOf,
+ WebhookValidator,
+ WebhookController,
+ send_webhook,
+ RepoPushWebhookSender,
+)
+from allura.tests import decorators as td
+from alluratest.controller import setup_basic_test, TestController
+
+
+# important to be distinct from 'test' and 'test2' which ForgeGit and
+# ForgeImporter use, so that the tests can run in parallel and not clobber each
+# other
+test_project_with_repo = 'adobe-1'
+with_git = td.with_tool(test_project_with_repo, 'git', 'src', 'Git')
+with_git2 = td.with_tool(test_project_with_repo, 'git', 'src2', 'Git2')
+
+
+class TestWebhookBase(object):
+
+ def setUp(self):
+ setup_basic_test()
+ self.setup_with_tools()
+ self.project = M.Project.query.get(shortname=test_project_with_repo)
+ self.git = self.project.app_instance('src')
+ self.wh = M.Webhook(
+ type='repo-push',
+ app_config_id=self.git.config._id,
+ hook_url='http://httpbin.org/post',
+ secret='secret')
+ session(self.wh).flush(self.wh)
+
+ @with_git
+ def setup_with_tools(self):
+ pass
+
+
+class TestValidators(TestWebhookBase):
+
+ def test_ming_one_of(self):
+ ids = [ac._id for ac in M.AppConfig.query.find().all()[:2]]
+ v = MingOneOf(cls=M.AppConfig, ids=ids, not_empty=True)
+ with assert_raises(Invalid) as cm:
+ v.to_python(None)
+ assert_equal(cm.exception.msg, u'Please enter a value')
+ with assert_raises(Invalid) as cm:
+ v.to_python('invalid id')
+ assert_equal(cm.exception.msg,
+ u'Object must be one of: %s, not invalid id' % ids)
+ assert_equal(v.to_python(ids[0]), M.AppConfig.query.get(_id=ids[0]))
+ assert_equal(v.to_python(ids[1]), M.AppConfig.query.get(_id=ids[1]))
+ assert_equal(v.to_python(unicode(ids[0])),
+ M.AppConfig.query.get(_id=ids[0]))
+ assert_equal(v.to_python(unicode(ids[1])),
+ M.AppConfig.query.get(_id=ids[1]))
+
+ def test_webhook_validator(self):
+ sender = Mock(type='repo-push')
+ ids = [ac._id for ac in M.AppConfig.query.find().all()[:3]]
+ ids, invalid_id = ids[:2], ids[2]
+ v = WebhookValidator(sender=sender, ac_ids=ids, not_empty=True)
+ with assert_raises(Invalid) as cm:
+ v.to_python(None)
+ assert_equal(cm.exception.msg, u'Please enter a value')
+ with assert_raises(Invalid) as cm:
+ v.to_python('invalid id')
+ assert_equal(cm.exception.msg, u'Invalid webhook')
+
+ wh = M.Webhook(type='invalid type',
+ app_config_id=invalid_id,
+ hook_url='http://httpbin.org/post',
+ secret='secret')
+ session(wh).flush(wh)
+ with assert_raises(Invalid) as cm:
+ v.to_python(wh._id)
+ assert_equal(cm.exception.msg, u'Invalid webhook')
+
+ wh.type = 'repo-push'
+ session(wh).flush(wh)
+ with assert_raises(Invalid) as cm:
+ v.to_python(wh._id)
+ assert_equal(cm.exception.msg, u'Invalid webhook')
+
+ wh.app_config_id = ids[0]
+ session(wh).flush(wh)
+ assert_equal(v.to_python(wh._id), wh)
+ assert_equal(v.to_python(unicode(wh._id)), wh)
+
+
+class TestWebhookController(TestController):
+
+ def setUp(self):
+ super(TestWebhookController, self).setUp()
+ self.setup_with_tools()
+ self.patches = self.monkey_patch()
+ for p in self.patches:
+ p.start()
+ self.project = M.Project.query.get(shortname=test_project_with_repo)
+ self.git = self.project.app_instance('src')
+ self.git2 = self.project.app_instance('src2')
+ self.url = str(self.project.url() + 'admin/webhooks')
+
+ def tearDown(self):
+ super(TestWebhookController, self).tearDown()
+ for p in self.patches:
+ p.stop()
+
+ @with_git
+ @with_git2
+ def setup_with_tools(self):
+ pass
+
+ def monkey_patch(self):
+ gen_secret = patch.object(
+ WebhookController,
+ 'gen_secret',
+ return_value='super-secret',
+ autospec=True)
+ return [gen_secret]
+
+ def create_webhook(self, data):
+ r = self.app.post(self.url + '/repo-push/create', data)
+ wf = json.loads(self.webflash(r))
+ assert_equal(wf['status'], 'ok')
+ assert_equal(wf['message'], 'Created successfully')
+ return r
+
+ def find_error(self, r, field, msg, form_type='create'):
+ form = r.html.find('form', attrs={'action': form_type})
+ if field == '_the_form':
+ error = form.findPrevious('div', attrs={'class': 'error'})
+ else:
+ widget = 'select' if field == 'app' else 'input'
+ error = form.find(widget, attrs={'name': field})
+ error = error.findNext('div', attrs={'class': 'error'})
+ if error:
+ assert_in(h.escape(msg), error.getText())
+ else:
+ assert False, 'Validation error not found'
+
+ def test_access(self):
+ self.app.get(self.url + '/repo-push/')
+ self.app.get(self.url + '/repo-push/',
+ extra_environ={'username': 'test-user'},
+ status=403)
+ r = self.app.get(self.url + '/repo-push/',
+ extra_environ={'username': '*anonymous'},
+ status=302)
+ assert_equal(r.location,
+ 'http://localhost/auth/'
+ '?return_to=%2Fadobe%2Fadobe-1%2Fadmin%2Fwebhooks%2Frepo-push%2F')
+
+ def test_invalid_hook_type(self):
+ self.app.get(self.url + '/invalid-hook-type/', status=404)
+
+ def test_create(self):
+ assert_equal(M.Webhook.query.find().count(), 0)
+ r = self.app.get(self.url)
+ assert_in('<h1>repo-push</h1>', r)
+ assert_not_in('http://httpbin.org/post', r)
+ data = {'url': u'http://httpbin.org/post',
+ 'app': unicode(self.git.config._id),
+ 'secret': ''}
+ msg = 'add webhook repo-push {} {}'.format(
+ data['url'], self.git.config.url())
+ with td.audits(msg):
+ r = self.create_webhook(data).follow().follow(status=200)
+ assert_in('http://httpbin.org/post', r)
+
+ hooks = M.Webhook.query.find().all()
+ assert_equal(len(hooks), 1)
+ assert_equal(hooks[0].type, 'repo-push')
+ assert_equal(hooks[0].hook_url, 'http://httpbin.org/post')
+ assert_equal(hooks[0].app_config_id, self.git.config._id)
+ assert_equal(hooks[0].secret, 'super-secret')
+
+ # Try to create duplicate
+ with td.out_audits(msg):
+ r = self.app.post(self.url + '/repo-push/create', data)
+ self.find_error(r, '_the_form',
+ '"repo-push" webhook already exists for Git http://httpbin.org/post')
+ assert_equal(M.Webhook.query.find().count(), 1)
+
+ def test_create_validation(self):
+ assert_equal(M.Webhook.query.find().count(), 0)
+ r = self.app.post(
+ self.url + '/repo-push/create', {}, status=404)
+
+ data = {'url': '', 'app': '', 'secret': ''}
+ r = self.app.post(self.url + '/repo-push/create', data)
+ self.find_error(r, 'url', 'Please enter a value')
+ self.find_error(r, 'app', 'Please enter a value')
+
+ data = {'url': 'qwer', 'app': '123', 'secret': 'qwe'}
+ r = self.app.post(self.url + '/repo-push/create', data)
+ self.find_error(r, 'url',
+ 'You must provide a full domain name (like qwer.com)')
+ self.find_error(r, 'app', 'Object must be one of: ')
+ self.find_error(r, 'app', '%s' % self.git.config._id)
+ self.find_error(r, 'app', '%s' % self.git2.config._id)
+
+ def test_edit(self):
+ data1 = {'url': u'http://httpbin.org/post',
+ 'app': unicode(self.git.config._id),
+ 'secret': u'secret'}
+ data2 = {'url': u'http://example.com/hook',
+ 'app': unicode(self.git2.config._id),
+ 'secret': u'secret2'}
+ self.create_webhook(data1).follow().follow(status=200)
+ self.create_webhook(data2).follow().follow(status=200)
+ assert_equal(M.Webhook.query.find().count(), 2)
+ wh1 = M.Webhook.query.get(hook_url=data1['url'])
+ r = self.app.get(self.url + '/repo-push/%s' % wh1._id)
+ form = r.forms[0]
+ assert_equal(form['url'].value, data1['url'])
+ assert_equal(form['app'].value, data1['app'])
+ assert_equal(form['secret'].value, data1['secret'])
+ assert_equal(form['webhook'].value, unicode(wh1._id))
+ form['url'] = 'http://host.org/hook'
+ form['app'] = unicode(self.git2.config._id)
+ form['secret'] = 'new secret'
+ msg = 'edit webhook repo-push\n{} => {}\n{} => {}\n{}'.format(
+ data1['url'], form['url'].value,
+ self.git.config.url(), self.git2.config.url(),
+ 'secret changed')
+ with td.audits(msg):
+ r = form.submit()
+ wf = json.loads(self.webflash(r))
+ assert_equal(wf['status'], 'ok')
+ assert_equal(wf['message'], 'Edited successfully')
+ assert_equal(M.Webhook.query.find().count(), 2)
+ wh1 = M.Webhook.query.get(_id=wh1._id)
+ assert_equal(wh1.hook_url, 'http://host.org/hook')
+ assert_equal(wh1.app_config_id, self.git2.config._id)
+ assert_equal(wh1.secret, 'new secret')
+ assert_equal(wh1.type, 'repo-push')
+
+ # Duplicates
+ r = self.app.get(self.url + '/repo-push/%s' % wh1._id)
+ form = r.forms[0]
+ form['url'] = data2['url']
+ form['app'] = data2['app']
+ r = form.submit()
+ self.find_error(r, '_the_form',
+ u'"repo-push" webhook already exists for Git2 http://example.com/hook',
+ form_type='edit')
+
+ def test_edit_validation(self):
+ invalid = M.Webhook(
+ type='invalid type',
+ app_config_id=None,
+ hook_url='http://httpbin.org/post',
+ secret='secret')
+ session(invalid).flush(invalid)
+ self.app.get(self.url + '/repo-push/%s' % invalid._id, status=404)
+
+ data = {'url': u'http://httpbin.org/post',
+ 'app': unicode(self.git.config._id),
+ 'secret': u'secret'}
+ self.create_webhook(data).follow().follow(status=200)
+ wh = M.Webhook.query.get(hook_url=data['url'], type='repo-push')
+
+ # invalid id in hidden field, just in case
+ r = self.app.get(self.url + '/repo-push/%s' % wh._id)
+ data = {k: v[0].value for (k, v) in r.forms[0].fields.items()}
+ data['webhook'] = unicode(invalid._id)
+ self.app.post(self.url + '/repo-push/edit', data, status=404)
+
+ # empty values
+ data = {'url': '', 'app': '', 'secret': '', 'webhook': str(wh._id)}
+ r = self.app.post(self.url + '/repo-push/edit', data)
+ self.find_error(r, 'url', 'Please enter a value', 'edit')
+ self.find_error(r, 'app', 'Please enter a value', 'edit')
+
+ data = {'url': 'qwe', 'app': '123', 'secret': 'qwe',
+ 'webhook': str(wh._id)}
+ r = self.app.post(self.url + '/repo-push/edit', data)
+ self.find_error(r, 'url',
+ 'You must provide a full domain name (like qwe.com)', 'edit')
+ self.find_error(r, 'app', 'Object must be one of:', 'edit')
+ self.find_error(r, 'app', '%s' % self.git.config._id, 'edit')
+ self.find_error(r, 'app', '%s' % self.git2.config._id, 'edit')
+
+ def test_delete(self):
+ data = {'url': u'http://httpbin.org/post',
+ 'app': unicode(self.git.config._id),
+ 'secret': u'secret'}
+ self.create_webhook(data).follow().follow(status=200)
+ assert_equal(M.Webhook.query.find().count(), 1)
+ wh = M.Webhook.query.get(hook_url=data['url'])
+ data = {'webhook': unicode(wh._id)}
+ msg = 'delete webhook repo-push {} {}'.format(
+ wh.hook_url, self.git.config.url())
+ with td.audits(msg):
+ r = self.app.post(self.url + '/repo-push/delete', data)
+ assert_equal(r.json, {'status': 'ok'})
+ assert_equal(M.Webhook.query.find().count(), 0)
+
+ def test_delete_validation(self):
+ invalid = M.Webhook(
+ type='invalid type',
+ app_config_id=None,
+ hook_url='http://httpbin.org/post',
+ secret='secret')
+ session(invalid).flush(invalid)
+ assert_equal(M.Webhook.query.find().count(), 1)
+
+ data = {'webhook': ''}
+ self.app.post(self.url + '/repo-push/delete', data, status=404)
+
+ data = {'webhook': unicode(invalid._id)}
+ self.app.post(self.url + '/repo-push/delete', data, status=404)
+ assert_equal(M.Webhook.query.find().count(), 1)
+
+ def test_list_webhooks(self):
+ data1 = {'url': u'http://httpbin.org/post',
+ 'app': unicode(self.git.config._id),
+ 'secret': 'secret'}
+ data2 = {'url': u'http://another-host.org/',
+ 'app': unicode(self.git2.config._id),
+ 'secret': 'secret2'}
+ self.create_webhook(data1).follow().follow(status=200)
+ self.create_webhook(data2).follow().follow(status=200)
+ wh1 = M.Webhook.query.get(hook_url=data1['url'])
+ wh2 = M.Webhook.query.get(hook_url=data2['url'])
+
+ r = self.app.get(self.url)
+ assert_in('<h1>repo-push</h1>', r)
+ rows = r.html.find('table').findAll('tr')
+ assert_equal(len(rows), 2)
+ rows = sorted([self._format_row(row) for row in rows])
+ expected_rows = sorted([
+ [{'href': self.url + '/repo-push/' + str(wh1._id),
+ 'text': wh1.hook_url},
+ {'href': self.git.url,
+ 'text': self.git.config.options.mount_label},
+ {'text': wh1.secret},
+ {'href': self.url + '/repo-push/delete',
+ 'data-id': str(wh1._id)}],
+ [{'href': self.url + '/repo-push/' + str(wh2._id),
+ 'text': wh2.hook_url},
+ {'href': self.git2.url,
+ 'text': self.git2.config.options.mount_label},
+ {'text': wh2.secret},
+ {'href': self.url + '/repo-push/delete',
+ 'data-id': str(wh2._id)}],
+ ])
+ assert_equal(rows, expected_rows)
+
+ def _format_row(self, row):
+ def link(td):
+ a = td.find('a')
+ return {'href': a.get('href'), 'text': a.getText()}
+ def text(td):
+ return {'text': td.getText()}
+ def delete_btn(td):
+ a = td.find('a')
+ return {'href': a.get('href'), 'data-id': a.get('data-id')}
+ tds = row.findAll('td')
+ return [link(tds[0]), link(tds[1]), text(tds[2]), delete_btn(tds[3])]
+
+
+class TestTasks(TestWebhookBase):
+
+ @patch('allura.webhooks.requests', autospec=True)
+ @patch('allura.webhooks.log', autospec=True)
+ def test_send_webhook(self, log, requests):
+ requests.post.return_value = Mock(status_code=200)
+ payload = {'some': ['data']}
+ json_payload = json.dumps(payload, cls=DateJSONEncoder)
+ send_webhook(self.wh._id, payload)
+ signature = hmac.new(
+ self.wh.secret.encode('utf-8'),
+ json_payload.encode('utf-8'),
+ hashlib.sha1)
+ signature = 'sha1=' + signature.hexdigest()
+ headers = {'content-type': 'application/json',
+ 'User-Agent': 'Allura Webhook (https://allura.apache.org/)',
+ 'X-Allura-Signature': signature}
+ requests.post.assert_called_once_with(
+ self.wh.hook_url,
+ data=json_payload,
+ headers=headers,
+ timeout=30)
+ log.info.assert_called_once_with(
+ 'Webhook successfully sent: %s %s %s',
+ self.wh.type, self.wh.hook_url, self.wh.app_config.url())
+
+ @patch('allura.webhooks.requests', autospec=True)
+ @patch('allura.webhooks.log', autospec=True)
+ def test_send_webhook_error(self, log, requests):
+ requests.post.return_value = Mock(status_code=500)
+ send_webhook(self.wh._id, {})
+ assert_equal(requests.post.call_count, 1)
+ assert_equal(log.info.call_count, 0)
+ log.error.assert_called_once_with(
+ 'Webhook send error: %s %s %s %s %s',
+ self.wh.type, self.wh.hook_url,
+ self.wh.app_config.url(),
+ requests.post.return_value.status_code,
+ requests.post.return_value.reason)
+
+class TestRepoPushWebhookSender(TestWebhookBase):
+
+ @patch('allura.webhooks.send_webhook', autospec=True)
+ def test_send(self, send_webhook):
+ sender = RepoPushWebhookSender()
+ sender.get_payload = Mock()
+ with h.push_config(c, app=self.git):
+ sender.send(arg1=1, arg2=2)
+ send_webhook.post.assert_called_once_with(
+ self.wh._id,
+ sender.get_payload.return_value)
+
+ @patch('allura.webhooks.send_webhook', autospec=True)
+ def test_send_no_configured_webhooks(self, send_webhook):
+ self.wh.delete()
+ session(self.wh).flush(self.wh)
+ sender = RepoPushWebhookSender()
+ with h.push_config(c, app=self.git):
+ sender.send(arg1=1, arg2=2)
+ assert_equal(send_webhook.post.call_count, 0)
+
+ def test_get_payload(self):
+ sender = RepoPushWebhookSender()
+ _ci = list(range(1, 4))
+ _se = [Mock(info=str(x)) for x in _ci]
+ with patch.object(self.git.repo, 'commit', autospec=True, side_effect=_se):
+ with h.push_config(c, app=self.git):
+ result = sender.get_payload(commit_ids=_ci)
+ expected_result = {
+ 'url': 'http://localhost/adobe/adobe-1/src/',
+ 'count': 3,
+ 'revisions': ['1', '2', '3'],
+ }
+ assert_equal(result, expected_result)
+
+
+class TestModels(TestWebhookBase):
+
+ def test_webhook_find(self):
+ p = M.Project.query.get(shortname='test')
+ assert_equal(M.Webhook.find('smth', p), [])
+ assert_equal(M.Webhook.find('repo-push', p), [])
+ assert_equal(M.Webhook.find('smth', self.project), [])
+ assert_equal(M.Webhook.find('repo-push', self.project), [self.wh])
+
+ def test_webhook_url(self):
+ assert_equal(self.wh.url(),
+ '/adobe/adobe-1/admin/webhooks/repo-push/{}'.format(self.wh._id))
http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 8495786..2393acd 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -236,7 +236,7 @@ def send_webhook(webhook_id, payload):
# TODO: catch
# TODO: configurable timeout
r = requests.post(url, data=json_payload, headers=headers, timeout=30)
- if r.status_code >= 200 and r.status_code <= 300:
+ if r.status_code >= 200 and r.status_code < 300:
log.info('Webhook successfully sent: %s %s %s',
webhook.type, webhook.hook_url, webhook.app_config.url())
else:
http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/ForgeGit/forgegit/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index 4d7a0c0..5e86ab0 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -39,6 +39,7 @@ from allura.tests import decorators as td
from allura.tests.model.test_repo import RepoImplTestBase
from allura import model as M
from allura.model.repo_refresh import send_notifications
+from allura.webhooks import RepoPushWebhookSender
from forgegit import model as GM
from forgegit.tests import with_git
from forgewiki import model as WM
@@ -519,6 +520,30 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
self.repo.clone_url('https', 'user'),
'https://user@foo.com/')
+ def test_webhook_payload(self):
+ sender = RepoPushWebhookSender()
+ cids = list(self.repo.all_commit_ids())[:2]
+ payload = sender.get_payload(commit_ids=cids)
+ expected_payload = {
+ 'url': 'http://localhost/p/test/src-git/',
+ 'count': 2,
+ 'revisions': [
+ {'author': u'Cory Johns',
+ 'author_email': u'cjohns@slashdotmedia.com',
+ 'author_url': None,
+ 'date': datetime.datetime(2013, 3, 28, 18, 54, 16),
+ 'id': u'5c47243c8e424136fd5cdd18cd94d34c66d1955c',
+ 'shortlink': u'[5c4724]',
+ 'summary': u'Not repo root'},
+ {'author': u'Rick Copeland',
+ 'author_email': u'rcopeland@geek.net',
+ 'author_url': None,
+ 'date': datetime.datetime(2010, 10, 7, 18, 44, 11),
+ 'id': u'1e146e67985dcd71c74de79613719bef7bddca4a',
+ 'shortlink': u'[1e146e]',
+ 'summary': u'Change README'}]}
+ assert_equal(payload, expected_payload)
+
class TestGitImplementation(unittest.TestCase):
http://git-wip-us.apache.org/repos/asf/allura/blob/e7ace573/ForgeSVN/forgesvn/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeSVN/forgesvn/tests/model/test_repository.py b/ForgeSVN/forgesvn/tests/model/test_repository.py
index 70c34ac..5267614 100644
--- a/ForgeSVN/forgesvn/tests/model/test_repository.py
+++ b/ForgeSVN/forgesvn/tests/model/test_repository.py
@@ -38,6 +38,7 @@ from alluratest.controller import setup_basic_test, setup_global_objects
from allura import model as M
from allura.model.repo_refresh import send_notifications
from allura.lib import helpers as h
+from allura.webhooks import RepoPushWebhookSender
from allura.tests.model.test_repo import RepoImplTestBase
from forgesvn import model as SM
@@ -569,6 +570,30 @@ class TestSVNRepo(unittest.TestCase, RepoImplTestBase):
ThreadLocalORMSession.flush_all()
assert repo2.is_empty()
+ def test_webhook_payload(self):
+ sender = RepoPushWebhookSender()
+ cids = list(self.repo.all_commit_ids())[:2]
+ payload = sender.get_payload(commit_ids=cids)
+ expected_payload = {
+ 'url': 'http://localhost/p/test/src/',
+ 'count': 2,
+ 'revisions': [
+ {'author': u'coldmind',
+ 'author_email': u'',
+ 'author_url': None,
+ 'date': datetime(2013, 11, 8, 13, 38, 11, 152000),
+ 'id': u'{}:6'.format(self.repo._id),
+ 'shortlink': '[r6]',
+ 'summary': ''},
+ {'author': u'rick446',
+ 'author_email': u'',
+ 'author_url': None,
+ 'date': datetime(2010, 11, 18, 20, 14, 21, 515000),
+ 'id': u'{}:5'.format(self.repo._id),
+ 'shortlink': '[r5]',
+ 'summary': u'Copied a => b'}]}
+ assert_equal(payload, expected_payload)
+
class TestSVNRev(unittest.TestCase):
[17/37] allura git commit: [#4542] ticket:714 Handle DuplicateKeyError
Posted by je...@apache.org.
[#4542] ticket:714 Handle DuplicateKeyError
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/ba555ec4
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/ba555ec4
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/ba555ec4
Branch: refs/heads/ib/4542
Commit: ba555ec4743d46dad2bd1c70121d6e437ff202dd
Parents: ad60782
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Jan 29 14:34:19 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:16:48 2015 +0000
----------------------------------------------------------------------
.../allura/templates/webhooks/create_form.html | 1 +
Allura/allura/webhooks.py | 76 ++++++++++++++------
2 files changed, 55 insertions(+), 22 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/ba555ec4/Allura/allura/templates/webhooks/create_form.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/webhooks/create_form.html b/Allura/allura/templates/webhooks/create_form.html
index c5ccdfb..6653985 100644
--- a/Allura/allura/templates/webhooks/create_form.html
+++ b/Allura/allura/templates/webhooks/create_form.html
@@ -64,6 +64,7 @@
{%- endmacro %}
{% block content %}
+{{ error('_the_form') }}
<form action="{{action}}" method="post" enctype="multipart/form-data">
<div>
<label for="url">url</label>
http://git-wip-us.apache.org/repos/asf/allura/blob/ba555ec4/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index c4c6ca6..8495786 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -21,12 +21,14 @@ import hmac
import hashlib
import requests
+from bson import ObjectId
from tg import expose, validate, redirect, flash
from tg.decorators import with_trailing_slash, without_trailing_slash
from pylons import tmpl_context as c
from formencode import validators as fev, schema, Invalid
from ming.odm import session
from webob import exc
+from pymongo.errors import DuplicateKeyError
from allura.controllers import BaseController
from allura.lib import helpers as h
@@ -53,14 +55,23 @@ class MingOneOf(av.Ming):
value, state)
-class WebhookValidator(av.Ming):
+class WebhookValidator(fev.FancyValidator):
def __init__(self, sender, ac_ids, **kw):
self.ac_ids = ac_ids
self.sender = sender
- super(WebhookValidator, self).__init__(cls=M.Webhook, **kw)
+ super(WebhookValidator, self).__init__(**kw)
def _to_python(self, value, state):
- wh = super(WebhookValidator, self)._to_python(value, state)
+ wh = None
+ if isinstance(value, M.Webhook):
+ wh = value
+ elif isinstance(value, ObjectId):
+ wh = M.Webhook.query.get(_id=value)
+ else:
+ try:
+ wh = M.Webhook.query.get(_id=ObjectId(value))
+ except:
+ pass
if wh and wh.type == self.sender.type and wh.app_config_id in self.ac_ids:
return wh
raise Invalid(u'Invalid webhook', value, state)
@@ -72,7 +83,9 @@ class WebhookCreateForm(schema.Schema):
self.triggered_by = [ac for ac in c.project.app_configs
if ac.tool_name.lower() in sender.triggered_by]
self.add_field('app', MingOneOf(
- cls=M.AppConfig, ids=[ac._id for ac in self.triggered_by]))
+ cls=M.AppConfig,
+ ids=[ac._id for ac in self.triggered_by],
+ not_empty=True))
url = fev.URL(not_empty=True)
secret = fev.UnicodeString()
@@ -82,7 +95,9 @@ class WebhookEditForm(WebhookCreateForm):
def __init__(self, sender):
super(WebhookEditForm, self).__init__(sender)
self.add_field('webhook', WebhookValidator(
- sender=sender, ac_ids=[ac._id for ac in self.triggered_by]))
+ sender=sender,
+ ac_ids=[ac._id for ac in self.triggered_by],
+ not_empty=True))
class WebhookControllerMeta(type):
@@ -115,9 +130,38 @@ class WebhookController(BaseController):
def gen_secret(self):
return h.cryptographic_nonce(20)
+ def update_webhook(self, wh, url, ac, secret=None):
+ if not secret:
+ secret = self.gen_secret()
+ wh.hook_url = url
+ wh.app_config_id = ac._id
+ wh.secret = secret
+ try:
+ session(wh).flush(wh)
+ except DuplicateKeyError:
+ session(wh).expunge(wh)
+ msg = u'_the_form: "{}" webhook already exists for {} {}'.format(
+ wh.type, ac.options.mount_label, url)
+ raise Invalid(msg, None, None)
+
+ def form_app_id(self, app):
+ if app and isinstance(app, M.AppConfig):
+ _app = unicode(app._id)
+ elif app:
+ _app = unicode(app)
+ else:
+ _app = None
+ return _app
+
@with_trailing_slash
@expose('jinja:allura:templates/webhooks/create_form.html')
def index(self, **kw):
+ if not c.form_values and kw:
+ # Executes if update_webhook raises an error
+ _app = self.form_app_id(kw.get('app'))
+ c.form_values = {'url': kw.get('url'),
+ 'app': _app,
+ 'secret': kw.get('secret')}
return {'sender': self.sender,
'action': 'create',
'form': self.create_form(self.sender)}
@@ -125,15 +169,8 @@ class WebhookController(BaseController):
@expose()
@require_post()
def create(self, url, app, secret):
- if not secret:
- secret = self.gen_secret()
- # TODO: catch DuplicateKeyError
- wh = M.Webhook(
- hook_url=url,
- secret=secret,
- app_config_id=app._id,
- type=self.sender.type)
- session(wh).flush(wh)
+ wh = M.Webhook(type=self.sender.type)
+ self.update_webhook(wh, url, app, secret)
M.AuditLog.log('add webhook %s %s %s',
wh.type, wh.hook_url, wh.app_config.url())
flash('Created successfully', 'ok')
@@ -142,16 +179,10 @@ class WebhookController(BaseController):
@expose()
@require_post()
def edit(self, webhook, url, app, secret):
- if not secret:
- secret = self.gen_secret()
old_url = webhook.hook_url
old_app = webhook.app_config.url()
old_secret = webhook.secret
- webhook.hook_url = url
- webhook.app_config_id = app._id
- webhook.secret = secret
- # TODO: duplicate
- session(webhook).flush(webhook)
+ self.update_webhook(webhook, url, app, secret)
M.AuditLog.log('edit webhook %s\n%s => %s\n%s => %s\n%s',
webhook.type, old_url, url, old_app, app.url(),
'secret changed' if old_secret != secret else '')
@@ -179,8 +210,9 @@ class WebhookController(BaseController):
wh = form.fields['webhook'].to_python(webhook)
except Invalid:
raise exc.HTTPNotFound()
+ _app = self.form_app_id(kw.get('app')) or unicode(wh.app_config._id)
c.form_values = {'url': kw.get('url') or wh.hook_url,
- 'app': kw.get('app') or unicode(wh.app_config_id),
+ 'app': _app,
'secret': kw.get('secret') or wh.secret,
'webhook': unicode(wh._id)}
return {'sender': self.sender,
[28/37] allura git commit: [#4542] ticket:726 Move 'Webhooks' from
sidebar to app admin
Posted by je...@apache.org.
[#4542] ticket:726 Move 'Webhooks' from sidebar to app admin
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/8cbd3308
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/8cbd3308
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/8cbd3308
Branch: refs/heads/ib/4542
Commit: 8cbd33080b76954ea7a0cc55898189d79f3f1f99
Parents: 13a8a60
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Feb 11 14:46:49 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:40 2015 +0000
----------------------------------------------------------------------
Allura/allura/app.py | 56 +++++++++-
Allura/allura/ext/admin/admin_main.py | 28 -----
.../ext/admin/templates/webhooks_list.html | 74 -------------
Allura/allura/lib/app_globals.py | 1 +
Allura/allura/lib/repository.py | 3 -
Allura/allura/model/webhook.py | 15 +--
.../templates/app_admin_webhooks_list.html | 71 ++++++++++++
.../allura/templates/webhooks/create_form.html | 11 --
Allura/allura/webhooks.py | 108 +++++++------------
ForgeBlog/forgeblog/main.py | 3 -
ForgeShortUrl/forgeshorturl/main.py | 3 -
ForgeTracker/forgetracker/tracker_main.py | 4 +-
ForgeWiki/forgewiki/wiki_main.py | 3 -
13 files changed, 169 insertions(+), 211 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/Allura/allura/app.py
----------------------------------------------------------------------
diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index fff483d..388b8a2 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -30,6 +30,7 @@ from paste.deploy.converters import asbool, asint
from bson import ObjectId
from bson.errors import InvalidId
from formencode import validators as V
+from webob import exc
from ming.orm import session
from ming.utils import LazyProperty
@@ -40,6 +41,7 @@ from allura import model
from allura.controllers import BaseController
from allura.lib.decorators import require_post, memoize
from allura.lib.utils import permanent_redirect, ConfigProxy
+from allura import model as M
log = logging.getLogger(__name__)
@@ -282,6 +284,12 @@ class Application(object):
"""
return self.config.url(project=self.project)
+ @LazyProperty
+ def admin_url(self):
+ return '{}{}/{}/'.format(
+ c.project.url(), 'admin',
+ self.config.options.mount_point)
+
@property
def email_address(self):
"""Return email address for this Application.
@@ -524,16 +532,28 @@ class Application(object):
"""
return ""
+ @LazyProperty
+ def _webhooks(self):
+ """A list of webhooks that can be triggered by this app.
+
+ :return: a list of :class:`WebhookSender <allura.webhooks.WebhookSender>`
+ """
+ tool_name = self.config.tool_name.lower()
+ webhooks = [w for w in g.entry_points['webhooks'].itervalues()
+ if tool_name in w.triggered_by]
+ return webhooks
+
def admin_menu(self, force_options=False):
"""Return the admin menu for this Application.
- Default implementation will return a menu with up to 3 links:
+ Default implementation will return a menu with up to 4 links:
- 'Permissions', if the current user has admin access to the
project in which this Application is installed
- 'Options', if this Application has custom options, or
``force_options`` is True
- 'Label', for editing this Application's label
+ - 'Webhooks', if this Application can trigger any webhooks
Subclasses should override this method to provide additional admin
menu items.
@@ -554,6 +574,8 @@ class Application(object):
SitemapEntry('Options', admin_url + 'options', className='admin_modal'))
links.append(
SitemapEntry('Label', admin_url + 'edit_label', className='admin_modal'))
+ if len(self._webhooks) > 0:
+ links.append(SitemapEntry('Webhooks', admin_url + 'webhooks'))
return links
def handle_message(self, topic, message):
@@ -672,7 +694,9 @@ class DefaultAdminController(BaseController):
"""Instantiate this controller for an :class:`app <Application>`.
"""
+ super(DefaultAdminController, self).__init__()
self.app = app
+ self.webhooks = WebhooksLookup(app)
@expose()
def index(self, **kw):
@@ -858,3 +882,33 @@ class DefaultAdminController(BaseController):
if (ace.permission == perm) and (ace.access == model.ACE.DENY):
self.app.config.acl.append(ace)
redirect(request.referer)
+
+
+class WebhooksLookup(BaseController):
+
+ def __init__(self, app):
+ super(WebhooksLookup, self).__init__()
+ self.app = app
+
+ @without_trailing_slash
+ @expose('jinja:allura:templates/app_admin_webhooks_list.html')
+ def index(self):
+ webhooks = self.app._webhooks
+ if len(webhooks) == 0:
+ raise exc.HTTPNotFound()
+ configured_hooks = {}
+ for hook in webhooks:
+ configured_hooks[hook.type] = M.Webhook.query.find({
+ 'type': hook.type,
+ 'app_config_id': self.app.config._id}
+ ).all()
+ return {'webhooks': webhooks,
+ 'configured_hooks': configured_hooks,
+ 'admin_url': self.app.admin_url + 'webhooks'}
+
+ @expose()
+ def _lookup(self, name, *remainder):
+ for hook in self.app._webhooks:
+ if hook.type == name and hook.controller:
+ return hook.controller(hook, self.app), remainder
+ raise exc.HTTPNotFound, name
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/Allura/allura/ext/admin/admin_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index 787bb8c..b812b0d 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -148,7 +148,6 @@ class AdminApp(Application):
SitemapEntry('Categorization', admin_url + 'trove')
]
links.append(SitemapEntry('Tools', admin_url + 'tools'))
- links.append(SitemapEntry('Webhooks', admin_url + 'webhooks'))
if asbool(config.get('bulk_export_enabled', True)):
links.append(SitemapEntry('Export', admin_url + 'export'))
if c.project.is_root and has_access(c.project, 'admin')():
@@ -193,32 +192,6 @@ class AdminExtensionLookup(object):
raise exc.HTTPNotFound, name
-class WebhooksLookup(BaseController):
-
- @LazyProperty
- def _webhooks(self):
- webhooks = h.iter_entry_points('allura.webhooks')
- webhooks = [ep.load() for ep in webhooks]
- return webhooks
-
- @without_trailing_slash
- @expose('jinja:allura.ext.admin:templates/webhooks_list.html')
- def index(self):
- webhooks = self._webhooks
- configured_hooks = {}
- for hook in webhooks:
- configured_hooks[hook.type] = M.Webhook.find(hook.type, c.project)
- return {'webhooks': webhooks,
- 'configured_hooks': configured_hooks}
-
- @expose()
- def _lookup(self, name, *remainder):
- for hook in self._webhooks:
- if hook.type == name and hook.controller:
- return hook.controller(hook), remainder
- raise exc.HTTPNotFound, name
-
-
class ProjectAdminController(BaseController):
def _check_security(self):
require_access(c.project, 'admin')
@@ -228,7 +201,6 @@ class ProjectAdminController(BaseController):
self.groups = GroupsController()
self.audit = AuditController()
self.ext = AdminExtensionLookup()
- self.webhooks = WebhooksLookup()
@with_trailing_slash
@expose('jinja:allura.ext.admin:templates/project_admin.html')
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/Allura/allura/ext/admin/templates/webhooks_list.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/webhooks_list.html b/Allura/allura/ext/admin/templates/webhooks_list.html
deleted file mode 100644
index f54b7fb..0000000
--- a/Allura/allura/ext/admin/templates/webhooks_list.html
+++ /dev/null
@@ -1,74 +0,0 @@
-{#-
- Licensed to the Apache Software Foundation (ASF) under one
- or more contributor license agreements. See the NOTICE file
- distributed with this work for additional information
- regarding copyright ownership. The ASF licenses this file
- to you 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.
--#}
-{% extends g.theme.master %}
-
-{% block title %}{{c.project.name}} / Webhooks{% endblock %}
-{% block header %}Webhooks{% endblock %}
-
-{% block content %}
- {% for hook in webhooks %}
- <h1>{{ hook.type }}</h1>
- <p><a href="{{c.app.url}}webhooks/{{ hook.type }}">Create</a></p>
- {% if configured_hooks[hook.type] %}
- <table>
- {% for wh in configured_hooks[hook.type] %}
- <tr>
- <td>
- <a href="{{ wh.url() }}">{{ wh.hook_url }}</a>
- </td>
- <td>
- <a href="{{ wh.app_config.url() }}">{{ wh.app_config.options.mount_label }}</a>
- </td>
- <td>{{ wh.secret or '' }}</td>
- <td>
- <a href="{{c.app.url}}webhooks/{{hook.type}}/delete"
- class="delete-link"
- data-id="{{h.really_unicode(wh._id)}}"
- title="Delete">
- <b data-icon="{{g.icons['delete'].char}}" class="ico {{g.icons['delete'].css}}" title="Delete"></b>
- </a>
- </td>
- </tr>
- {% endfor %}
- </table>
- {% endif %}
- {% endfor %}
-{% endblock %}
-
-{% block extra_js %}
-<script type="text/javascript">
-$(function() {
- $('.delete-link').click(function(e) {
- e.preventDefault();
- var id = $(this).attr('data-id');
- var csrf = $.cookie('_session_id');
- var data = {'webhook': id, '_session_id': csrf};
- var url = $(this).attr('href');
- var $tr = $(this).parents('tr')
- $.post(url, data, function(data) {
- if (data['status'] == 'ok') {
- $tr.remove();
- } else {
- console.log(data);
- }
- });
- });
-});
-</script>
-{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/Allura/allura/lib/app_globals.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/app_globals.py b/Allura/allura/lib/app_globals.py
index f3cb551..fc49749 100644
--- a/Allura/allura/lib/app_globals.py
+++ b/Allura/allura/lib/app_globals.py
@@ -284,6 +284,7 @@ class Globals(object):
# macro eps are used solely for ensuring that external macros are
# imported (after load, the ep itself is not used)
macros=_cache_eps('allura.macros'),
+ webhooks=_cache_eps('allura.webhooks'),
)
# Neighborhood cache
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/Allura/allura/lib/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/repository.py b/Allura/allura/lib/repository.py
index e0b139e..9a4966f 100644
--- a/Allura/allura/lib/repository.py
+++ b/Allura/allura/lib/repository.py
@@ -220,9 +220,6 @@ class RepositoryApp(Application):
class RepoAdminController(DefaultAdminController):
- def __init__(self, app):
- self.app = app
-
@LazyProperty
def repo(self):
return self.app.repo
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/Allura/allura/model/webhook.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
index 09cc7fa..4c39309 100644
--- a/Allura/allura/model/webhook.py
+++ b/Allura/allura/model/webhook.py
@@ -36,17 +36,10 @@ class Webhook(Artifact):
last_sent = FieldProperty(dt.datetime, if_missing=None)
def url(self):
- return '{}{}/{}/{}'.format(
- self.app_config.project.url(),
- 'admin/webhooks',
- self.type,
- self._id)
-
- @classmethod
- def find(cls, type, project):
- ac_ids = [ac._id for ac in project.app_configs]
- hooks = cls.query.find(dict(type=type, app_config_id={'$in': ac_ids}))
- return hooks.all()
+ return '{}{}/{}/webhooks/{}/{}'.format(
+ self.app_config.project.url(), 'admin',
+ self.app_config.options.mount_point,
+ self.type, self._id)
def enforce_limit(self):
'''Returns False if limit is reached, otherwise True'''
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/Allura/allura/templates/app_admin_webhooks_list.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/app_admin_webhooks_list.html b/Allura/allura/templates/app_admin_webhooks_list.html
new file mode 100644
index 0000000..0fe02e9
--- /dev/null
+++ b/Allura/allura/templates/app_admin_webhooks_list.html
@@ -0,0 +1,71 @@
+{#-
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-#}
+{% extends g.theme.master %}
+
+{% block title %}{{c.project.name}} / Webhooks{% endblock %}
+{% block header %}Webhooks{% endblock %}
+
+{% block content %}
+ {% for hook in webhooks %}
+ <h1>{{ hook.type }}</h1>
+ <p><a href="{{admin_url}}/{{hook.type}}">Create</a></p>
+ {% if configured_hooks[hook.type] %}
+ <table>
+ {% for wh in configured_hooks[hook.type] %}
+ <tr>
+ <td>
+ <a href="{{ wh.url() }}">{{ wh.hook_url }}</a>
+ </td>
+ <td>{{ wh.secret or '' }}</td>
+ <td>
+ <a href="{{admin_url}}/{{hook.type}}/delete"
+ class="delete-link"
+ data-id="{{h.really_unicode(wh._id)}}"
+ title="Delete">
+ <b data-icon="{{g.icons['delete'].char}}" class="ico {{g.icons['delete'].css}}" title="Delete"></b>
+ </a>
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+ {% endif %}
+ {% endfor %}
+{% endblock %}
+
+{% block extra_js %}
+<script type="text/javascript">
+$(function() {
+ $('.delete-link').click(function(e) {
+ e.preventDefault();
+ var id = $(this).attr('data-id');
+ var csrf = $.cookie('_session_id');
+ var data = {'webhook': id, '_session_id': csrf};
+ var url = $(this).attr('href');
+ var $tr = $(this).parents('tr')
+ $.post(url, data, function(data) {
+ if (data['status'] == 'ok') {
+ $tr.remove();
+ } else {
+ console.log(data);
+ }
+ });
+ });
+});
+</script>
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/Allura/allura/templates/webhooks/create_form.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/webhooks/create_form.html b/Allura/allura/templates/webhooks/create_form.html
index 6653985..95bfdf6 100644
--- a/Allura/allura/templates/webhooks/create_form.html
+++ b/Allura/allura/templates/webhooks/create_form.html
@@ -72,17 +72,6 @@
{{ error('url') }}
</div>
<div>
- <label for="app">app</label>
- <select name="app">
- {% for ac in form.triggered_by %}
- <option value="{{ac._id}}"{% if h.really_unicode(ac._id) == c.form_values['app'] %} selected{% endif %}>
- {{ ac.options.mount_label }}
- </option>
- {% endfor %}
- </select>
- {{ error('app') }}
- </div>
- <div>
<label for="secret">secret (leave empty to autogenerate)</label>
<input name="secret" value="{{ c.form_values['secret'] }}">
{{ error('secret') }}
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 17d7ebf..233c3d5 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -45,23 +45,9 @@ from allura import model as M
log = logging.getLogger(__name__)
-class MingOneOf(av.Ming):
- def __init__(self, ids, **kw):
- self.ids = ids
- super(MingOneOf, self).__init__(**kw)
-
- def _to_python(self, value, state):
- result = super(MingOneOf, self)._to_python(value, state)
- if result and result._id in self.ids:
- return result
- raise Invalid(
- u'Object must be one of: {}, not {}'.format(self.ids, value),
- value, state)
-
-
class WebhookValidator(fev.FancyValidator):
- def __init__(self, sender, ac_ids, **kw):
- self.ac_ids = ac_ids
+ def __init__(self, sender, app, **kw):
+ self.app = app
self.sender = sender
super(WebhookValidator, self).__init__(**kw)
@@ -76,50 +62,39 @@ class WebhookValidator(fev.FancyValidator):
wh = M.Webhook.query.get(_id=ObjectId(value))
except:
pass
- if wh and wh.type == self.sender.type and wh.app_config_id in self.ac_ids:
+ if wh and wh.type == self.sender.type and wh.app_config_id == self.app.config._id:
return wh
raise Invalid(u'Invalid webhook', value, state)
class WebhookCreateForm(schema.Schema):
- def __init__(self, sender):
- super(WebhookCreateForm, self).__init__()
- self.triggered_by = [ac for ac in c.project.app_configs
- if ac.tool_name.lower() in sender.triggered_by]
- self.add_field('app', MingOneOf(
- cls=M.AppConfig,
- ids=[ac._id for ac in self.triggered_by],
- not_empty=True))
-
url = fev.URL(not_empty=True)
secret = fev.UnicodeString()
class WebhookEditForm(WebhookCreateForm):
- def __init__(self, sender):
- super(WebhookEditForm, self).__init__(sender)
+ def __init__(self, sender, app):
+ super(WebhookEditForm, self).__init__()
self.add_field('webhook', WebhookValidator(
- sender=sender,
- ac_ids=[ac._id for ac in self.triggered_by],
- not_empty=True))
+ sender=sender, app=app, not_empty=True))
class WebhookControllerMeta(type):
- def __call__(cls, sender, *args, **kw):
+ def __call__(cls, sender, app, *args, **kw):
"""Decorate post handlers with a validator that references
the appropriate webhook sender for this controller.
"""
if hasattr(cls, 'create'):
cls.create = validate(
- cls.create_form(sender),
+ cls.create_form(),
error_handler=cls.index.__func__,
)(cls.create)
if hasattr(cls, 'edit'):
cls.edit = validate(
- cls.edit_form(sender),
+ cls.edit_form(sender, app),
error_handler=cls._default.__func__,
)(cls.edit)
- return type.__call__(cls, sender, *args, **kw)
+ return type.__call__(cls, sender, app, *args, **kw)
class WebhookController(BaseController):
@@ -127,80 +102,71 @@ class WebhookController(BaseController):
create_form = WebhookCreateForm
edit_form = WebhookEditForm
- def __init__(self, sender):
+ def __init__(self, sender, app):
super(WebhookController, self).__init__()
self.sender = sender()
+ self.app = app
def gen_secret(self):
return h.cryptographic_nonce(20)
- def update_webhook(self, wh, url, ac, secret=None):
+ def update_webhook(self, wh, url, secret=None):
if not secret:
secret = self.gen_secret()
wh.hook_url = url
- wh.app_config_id = ac._id
wh.secret = secret
try:
session(wh).flush(wh)
except DuplicateKeyError:
session(wh).expunge(wh)
msg = u'_the_form: "{}" webhook already exists for {} {}'.format(
- wh.type, ac.options.mount_label, url)
+ wh.type, self.app.config.options.mount_label, url)
raise Invalid(msg, None, None)
- def form_app_id(self, app):
- if app and isinstance(app, M.AppConfig):
- _app = unicode(app._id)
- elif app:
- _app = unicode(app)
- else:
- _app = None
- return _app
-
@with_trailing_slash
@expose('jinja:allura:templates/webhooks/create_form.html')
def index(self, **kw):
if not c.form_values and kw:
# Executes if update_webhook raises an error
- _app = self.form_app_id(kw.get('app'))
c.form_values = {'url': kw.get('url'),
- 'app': _app,
'secret': kw.get('secret')}
return {'sender': self.sender,
'action': 'create',
- 'form': self.create_form(self.sender)}
+ 'form': self.create_form()}
@expose()
@require_post()
- def create(self, url, app, secret):
- if self.sender.enforce_limit(app):
- wh = M.Webhook(type=self.sender.type)
- self.update_webhook(wh, url, app, secret)
+ def create(self, url, secret):
+ if self.sender.enforce_limit(self.app):
+ webhook = M.Webhook(
+ type=self.sender.type,
+ app_config_id=self.app.config._id)
+ self.update_webhook(webhook, url, secret)
M.AuditLog.log('add webhook %s %s %s',
- wh.type, wh.hook_url, wh.app_config.url())
+ webhook.type, webhook.hook_url,
+ webhook.app_config.url())
flash('Created successfully', 'ok')
else:
- flash('You have exceeded the maximum number of projects '
+ flash('You have exceeded the maximum number of webhooks '
'you are allowed to create for this project/app', 'error')
- redirect(c.project.url() + 'admin/webhooks/')
+ redirect(self.app.admin_url + 'webhooks')
@expose()
@require_post()
- def edit(self, webhook, url, app, secret):
+ def edit(self, webhook, url, secret):
old_url = webhook.hook_url
- old_app = webhook.app_config.url()
old_secret = webhook.secret
- self.update_webhook(webhook, url, app, secret)
- M.AuditLog.log('edit webhook %s\n%s => %s\n%s => %s\n%s',
- webhook.type, old_url, url, old_app, app.url(),
- 'secret changed' if old_secret != secret else '')
+ self.update_webhook(webhook, url, secret)
+ M.AuditLog.log('edit webhook %s\n%s => %s\n%s',
+ webhook.type, old_url, url,
+ 'secret changed' if old_secret != secret else '')
flash('Edited successfully', 'ok')
- redirect(c.project.url() + 'admin/webhooks/')
+ redirect(self.app.admin_url + 'webhooks')
@expose('json:')
@require_post()
def delete(self, webhook):
- form = self.edit_form(self.sender)
+ form = self.edit_form(self.sender, self.app)
try:
wh = form.fields['webhook'].to_python(webhook)
except Invalid:
@@ -213,14 +179,12 @@ class WebhookController(BaseController):
@without_trailing_slash
@expose('jinja:allura:templates/webhooks/create_form.html')
def _default(self, webhook, **kw):
- form = self.edit_form(self.sender)
+ form = self.edit_form(self.sender, self.app)
try:
wh = form.fields['webhook'].to_python(webhook)
except Invalid:
raise exc.HTTPNotFound()
- _app = self.form_app_id(kw.get('app')) or unicode(wh.app_config._id)
c.form_values = {'url': kw.get('url') or wh.hook_url,
- 'app': _app,
'secret': kw.get('secret') or wh.secret,
'webhook': unicode(wh._id)}
return {'sender': self.sender,
@@ -336,7 +300,7 @@ class WebhookSender(object):
else:
log.warn('Webhook fires too often: %s. Skipping', webhook)
- def enforce_limit(self, app_config):
+ def enforce_limit(self, app):
'''
Checks if limit of webhooks created for given project/app is reached.
Returns False if limit is reached, True otherwise.
@@ -344,10 +308,10 @@ class WebhookSender(object):
_type = self.type.replace('-', '_')
limits = json.loads(config.get('webhook.%s.max_hooks' % _type, '{}'))
count = M.Webhook.query.find(dict(
- app_config_id=app_config._id,
+ app_config_id=app.config._id,
type=self.type,
)).count()
- return count < limits.get(app_config.tool_name.lower(), 3)
+ return count < limits.get(app.config.tool_name.lower(), 3)
class RepoPushWebhookSender(WebhookSender):
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/ForgeBlog/forgeblog/main.py
----------------------------------------------------------------------
diff --git a/ForgeBlog/forgeblog/main.py b/ForgeBlog/forgeblog/main.py
index 1e3f604..93cd01b 100644
--- a/ForgeBlog/forgeblog/main.py
+++ b/ForgeBlog/forgeblog/main.py
@@ -395,9 +395,6 @@ class PostController(BaseController, FeedController):
class BlogAdminController(DefaultAdminController):
- def __init__(self, app):
- self.app = app
-
@without_trailing_slash
@expose('jinja:forgeblog:templates/blog/admin_options.html')
def options(self):
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/ForgeShortUrl/forgeshorturl/main.py
----------------------------------------------------------------------
diff --git a/ForgeShortUrl/forgeshorturl/main.py b/ForgeShortUrl/forgeshorturl/main.py
index f4ab4ed..b259e92 100644
--- a/ForgeShortUrl/forgeshorturl/main.py
+++ b/ForgeShortUrl/forgeshorturl/main.py
@@ -198,9 +198,6 @@ class ShortURLAdminController(DefaultAdminController):
)
)
- def __init__(self, app):
- self.app = app
-
@expose()
def index(self, **kw):
redirect(c.project.url() + 'admin/tools')
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index b38bf1d..a7c4e61 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1528,8 +1528,8 @@ NONALNUM_RE = re.compile(r'\W+')
class TrackerAdminController(DefaultAdminController):
def __init__(self, app):
- self.app = app
- self.bins = BinController(app=app)
+ super(TrackerAdminController, self).__init__(app)
+ self.bins = BinController(app=self.app)
# if self.app.globals and self.app.globals.milestone_names is None:
# self.app.globals.milestone_names = ''
http://git-wip-us.apache.org/repos/asf/allura/blob/8cbd3308/ForgeWiki/forgewiki/wiki_main.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index 3341377..ecbddef 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -801,9 +801,6 @@ class PageRestController(BaseController):
class WikiAdminController(DefaultAdminController):
- def __init__(self, app):
- self.app = app
-
def _check_security(self):
require_access(self.app, 'configure')
[15/37] allura git commit: [#4542] ticket:714 Sign webhook payload
Posted by je...@apache.org.
[#4542] ticket:714 Sign webhook payload
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/0f529d6c
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/0f529d6c
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/0f529d6c
Branch: refs/heads/ib/4542
Commit: 0f529d6c95393561b2c801a5e6d8bc555e5a4ab5
Parents: 0eb35a9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jan 28 11:30:33 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:16:47 2015 +0000
----------------------------------------------------------------------
.../allura/ext/admin/templates/webhooks_list.html | 1 +
Allura/allura/model/webhook.py | 1 +
Allura/allura/templates/webhooks/create_form.html | 5 +++++
Allura/allura/webhooks.py | 17 +++++++++++++++--
4 files changed, 22 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/0f529d6c/Allura/allura/ext/admin/templates/webhooks_list.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/webhooks_list.html b/Allura/allura/ext/admin/templates/webhooks_list.html
index 9bb2476..39df656 100644
--- a/Allura/allura/ext/admin/templates/webhooks_list.html
+++ b/Allura/allura/ext/admin/templates/webhooks_list.html
@@ -35,6 +35,7 @@
<td>
<a href="{{ h.app_config.url() }}">{{ h.app_config.options.mount_label }}</a>
</td>
+ <td>{{ h.secret or '' }}</td>
<td>
<a href="#" title="Delete">
<b data-icon="{{g.icons['delete'].char}}" class="ico {{g.icons['delete'].css}}" title="Delete"></b>
http://git-wip-us.apache.org/repos/asf/allura/blob/0f529d6c/Allura/allura/model/webhook.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
index 73b9540..05eb436 100644
--- a/Allura/allura/model/webhook.py
+++ b/Allura/allura/model/webhook.py
@@ -27,6 +27,7 @@ class Webhook(Artifact):
type = FieldProperty(str)
hook_url = FieldProperty(str)
+ secret = FieldProperty(str)
def url(self):
return '{}{}/{}/{}'.format(
http://git-wip-us.apache.org/repos/asf/allura/blob/0f529d6c/Allura/allura/templates/webhooks/create_form.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/webhooks/create_form.html b/Allura/allura/templates/webhooks/create_form.html
index a44bb5b..f3eb713 100644
--- a/Allura/allura/templates/webhooks/create_form.html
+++ b/Allura/allura/templates/webhooks/create_form.html
@@ -81,6 +81,11 @@
</select>
{{ error('app') }}
</div>
+ <div>
+ <label for="secret">secret (leave empty to autogenerate)</label>
+ <input name="secret" value="{{ c.form_values['secret'] }}">
+ {{ error('secret') }}
+ </div>
{% block additional_fields %}{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/0f529d6c/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index c5dc37e..5c792be 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -17,6 +17,8 @@
import logging
import json
+import hmac
+import hashlib
import requests
from bson import ObjectId
@@ -45,6 +47,7 @@ class WebhookCreateForm(schema.Schema):
[unicode(ac._id) for ac in self.triggered_by]))
url = fev.URL(not_empty=True)
+ secret = fev.UnicodeString()
class WebhookControllerMeta(type):
@@ -76,10 +79,13 @@ class WebhookController(BaseController):
@expose()
@require_post()
- def create(self, url, app):
+ def create(self, url, app, secret=None):
+ if not secret:
+ secret = h.cryptographic_nonce(20)
# TODO: catch DuplicateKeyError
wh = M.Webhook(
hook_url=url,
+ secret=secret,
app_config_id=ObjectId(app),
type=self.webhook.type)
session(wh).flush(wh)
@@ -90,8 +96,15 @@ class WebhookController(BaseController):
def send_webhook(webhook_id, payload):
webhook = M.Webhook.query.get(_id=webhook_id)
url = webhook.hook_url
- headers = {'content-type': 'application/json'}
json_payload = json.dumps(payload, cls=DateJSONEncoder)
+ signature = hmac.new(
+ webhook.secret.encode('utf-8'),
+ json_payload.encode('utf-8'),
+ hashlib.sha1)
+ signature = 'sha1=' + signature.hexdigest()
+ headers = {'content-type': 'application/json',
+ 'User-Agent': 'Allura Webhook (https://allura.apache.org/)',
+ 'X-Allura-Signature': signature}
# TODO: catch
# TODO: configurable timeout
r = requests.post(url, data=json_payload, headers=headers, timeout=30)
[20/37] allura git commit: [#4542] ticket:714 Audit log of webhooks
added
Posted by je...@apache.org.
[#4542] ticket:714 Audit log of webhooks added
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/7d8c47ba
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/7d8c47ba
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/7d8c47ba
Branch: refs/heads/ib/4542
Commit: 7d8c47bac3efbc322e98da47b952f6812ee41f1b
Parents: 0f529d6
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jan 28 11:37:06 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:16:48 2015 +0000
----------------------------------------------------------------------
Allura/allura/webhooks.py | 2 ++
1 file changed, 2 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/7d8c47ba/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 5c792be..9bb99e3 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -89,6 +89,8 @@ class WebhookController(BaseController):
app_config_id=ObjectId(app),
type=self.webhook.type)
session(wh).flush(wh)
+ M.AuditLog.log('add webhook %s %s %s',
+ wh.type, wh.hook_url, wh.app_config.url())
redirect(c.project.url() + 'admin/webhooks/')
[12/37] allura git commit: [#7823] ticket:724 Change
EmailAddress.canonical behavior on invalid emails
Posted by je...@apache.org.
[#7823] ticket:724 Change EmailAddress.canonical behavior on invalid emails
Return None, instead of 'nobody@example.com'.
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/0d6a4431
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/0d6a4431
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/0d6a4431
Branch: refs/heads/ib/4542
Commit: 0d6a44318b399203a9e732d276b6ebf873e36d8c
Parents: 1c2d973
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 12 13:10:32 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 13 20:48:35 2015 +0000
----------------------------------------------------------------------
Allura/allura/controllers/auth.py | 32 +++++++++++++++-------------
Allura/allura/lib/utils.py | 38 +++++++++++++++++++++++++++++++++-
Allura/allura/model/auth.py | 28 +++++++++++++++++--------
Allura/allura/tests/test_utils.py | 19 ++++++++++++++++-
4 files changed, 92 insertions(+), 25 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/0d6a4431/Allura/allura/controllers/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/auth.py b/Allura/allura/controllers/auth.py
index 453ad8a..2183032 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -232,7 +232,8 @@ class AuthController(BaseController):
user.set_tool_data('allura', pwd_reset_preserve_session=session.id) # else the first password set causes this session to be invalidated
if require_email:
em = user.claim_address(email)
- em.send_verification_link()
+ if em:
+ em.send_verification_link()
flash('User "%s" registered. Verification link was sent to your email.' % username)
else:
plugin.AuthenticationProvider.get(request).login(user)
@@ -490,22 +491,25 @@ class PreferencesController(BaseController):
elif mail_util.isvalid(new_addr['addr']):
em = M.EmailAddress.create(new_addr['addr'])
- user.email_addresses.append(em.email)
- em.claimed_by_user_id = user._id
-
- confirmed_emails = filter(lambda email: email.confirmed, claimed_emails)
- if not confirmed_emails:
- if not admin:
- em.send_verification_link()
+ if em:
+ user.email_addresses.append(em.email)
+ em.claimed_by_user_id = user._id
+
+ confirmed_emails = filter(lambda email: email.confirmed, claimed_emails)
+ if not confirmed_emails:
+ if not admin:
+ em.send_verification_link()
+ else:
+ AuthController()._verify_addr(em)
else:
- AuthController()._verify_addr(em)
- else:
- em.send_claim_attempt()
+ em.send_claim_attempt()
- if not admin:
- flash('A verification email has been sent. Please check your email and click to confirm.')
+ if not admin:
+ flash('A verification email has been sent. Please check your email and click to confirm.')
- h.auditlog_user('New email address: %s', new_addr['addr'], user=user)
+ h.auditlog_user('New email address: %s', new_addr['addr'], user=user)
+ else:
+ flash('Email address %s is invalid' % new_addr['addr'], 'error')
else:
flash('Email address %s is invalid' % new_addr['addr'], 'error')
if not primary_addr and not user.get_pref('email_address') and user.email_addresses:
http://git-wip-us.apache.org/repos/asf/allura/blob/0d6a4431/Allura/allura/lib/utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index 35bc039..7a56603 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -48,6 +48,7 @@ import html5lib.sanitizer
from ew import jinja2_ew as ew
from ming.utils import LazyProperty
+from ming.odm.odmsession import ODMCursor
MARKDOWN_EXTENSIONS = ['.markdown', '.mdown', '.mkdn', '.mkd', '.md']
@@ -555,4 +556,39 @@ def ip_address(request):
ip = request.remote_addr
if tg.config.get('ip_address_header'):
ip = request.headers.get(tg.config['ip_address_header']) or ip
- return ip
\ No newline at end of file
+ return ip
+
+
+class EmptyCursor(ODMCursor):
+ """Ming cursor with no results"""
+
+ def __init__(self, *args, **kw):
+ pass
+
+ @property
+ def extensions(self):
+ return []
+
+ def count(self):
+ return 0
+
+ def _next_impl(self):
+ raise StopIteration
+
+ def next(self):
+ raise StopIteration
+
+ def options(self, **kw):
+ return self
+
+ def limit(self, limit):
+ return self
+
+ def skip(self, skip):
+ return self
+
+ def hint(self, index_or_name):
+ return self
+
+ def sort(self, *args, **kw):
+ return self
http://git-wip-us.apache.org/repos/asf/allura/blob/0d6a4431/Allura/allura/model/auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/auth.py b/Allura/allura/model/auth.py
index e8bd8a6..0b92443 100644
--- a/Allura/allura/model/auth.py
+++ b/Allura/allura/model/auth.py
@@ -128,7 +128,11 @@ class EmailAddress(MappedClass):
'''Equivalent to Ming's query.get but calls self.canonical on address
before lookup. You should always use this instead of query.get'''
if kw.get('email'):
- kw['email'] = cls.canonical(kw['email'])
+ email = cls.canonical(kw['email'])
+ if email is not None:
+ kw['email'] = email
+ else:
+ return None
return cls.query.get(**kw)
@classmethod
@@ -137,7 +141,11 @@ class EmailAddress(MappedClass):
before lookup. You should always use this instead of query.find'''
if q:
if q.get('email'):
- q['email'] = cls.canonical(q['email'])
+ email = cls.canonical(q['email'])
+ if email is not None:
+ q['email'] = email
+ else:
+ return utils.EmptyCursor()
return cls.query.find(q)
return cls.query.find()
@@ -152,7 +160,8 @@ class EmailAddress(MappedClass):
@classmethod
def create(cls, addr):
addr = cls.canonical(addr)
- return cls(email=addr)
+ if addr is not None:
+ return cls(email=addr)
@classmethod
def canonical(cls, addr):
@@ -163,7 +172,7 @@ class EmailAddress(MappedClass):
user, domain = addr.split('@')
return '%s@%s' % (user, domain.lower())
else:
- return 'nobody@example.com'
+ return None
def send_claim_attempt(self):
confirmed_email = self.find(dict(email=self.email, confirmed=True)).all()
@@ -666,11 +675,12 @@ class User(MappedClass, ActivityNode, ActivityObject, SearchIndexable):
def claim_address(self, email_address):
addr = EmailAddress.canonical(email_address)
email_addr = EmailAddress.create(addr)
- email_addr.claimed_by_user_id = self._id
- if addr not in self.email_addresses:
- self.email_addresses.append(addr)
- session(email_addr).flush(email_addr)
- return email_addr
+ if email_addr:
+ email_addr.claimed_by_user_id = self._id
+ if addr not in self.email_addresses:
+ self.email_addresses.append(addr)
+ session(email_addr).flush(email_addr)
+ return email_addr
@classmethod
def register(cls, doc, make_project=True):
http://git-wip-us.apache.org/repos/asf/allura/blob/0d6a4431/Allura/allura/tests/test_utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_utils.py b/Allura/allura/tests/test_utils.py
index 209d0df..e5f9c43 100644
--- a/Allura/allura/tests/test_utils.py
+++ b/Allura/allura/tests/test_utils.py
@@ -23,7 +23,7 @@ from os import path
from webob import Request
from mock import Mock, patch
-from nose.tools import assert_equal
+from nose.tools import assert_equal, assert_raises
from pygments import highlight
from pygments.lexers import get_lexer_for_filename
from tg import config
@@ -278,3 +278,20 @@ def test_ip_address_header_not_set():
with h.push_config(config, **{'ip_address_header': 'X_FORWARDED_FOR'}):
assert_equal(utils.ip_address(req),
'1.2.3.4')
+
+
+def test_empty_cursor():
+ """EmptyCursors conforms to specification of Ming's ODMCursor"""
+ cursor = utils.EmptyCursor()
+ assert_equal(cursor.count(), 0)
+ assert_equal(cursor.first(), None)
+ assert_equal(cursor.all(), [])
+ assert_equal(cursor.limit(10), cursor)
+ assert_equal(cursor.skip(10), cursor)
+ assert_equal(cursor.sort('name', 1), cursor)
+ assert_equal(cursor.hint('index'), cursor)
+ assert_equal(cursor.extensions, [])
+ assert_equal(cursor.options(arg1='val1', arg2='val2'), cursor)
+ assert_raises(ValueError, cursor.one)
+ assert_raises(StopIteration, cursor.next)
+ assert_raises(StopIteration, cursor._next_impl)
[14/37] allura git commit: [#7823] ticket:724 s/emai/email/
Posted by je...@apache.org.
[#7823] ticket:724 s/emai/email/
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/f155a687
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/f155a687
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/f155a687
Branch: refs/heads/ib/4542
Commit: f155a687eff70872b852b6dd0c21c52ebb7658ab
Parents: 17d4276
Author: Igor Bondarenko <je...@gmail.com>
Authored: Thu Feb 12 10:28:05 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Feb 13 20:48:35 2015 +0000
----------------------------------------------------------------------
Allura/allura/model/repo_refresh.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/f155a687/Allura/allura/model/repo_refresh.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repo_refresh.py b/Allura/allura/model/repo_refresh.py
index cdd71dc..3dbad4a 100644
--- a/Allura/allura/model/repo_refresh.py
+++ b/Allura/allura/model/repo_refresh.py
@@ -142,7 +142,7 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False):
if user is not None:
g.statsUpdater.newCommit(new, repo.app_config.project, user)
actor = user or TransientActor(
- activity_name=new.committed.name or new.committed.emai)
+ activity_name=new.committed.name or new.committed.email)
g.director.create_activity(actor, 'committed', new,
related_nodes=[repo.app_config.project],
tags=['commit', repo.tool.lower()])
[36/37] allura git commit: [#4542] ticket:728 Don't enforce limit if
multiple branches pushed at once
Posted by je...@apache.org.
[#4542] ticket:728 Don't enforce limit if multiple branches pushed at once
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/a60ccc94
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/a60ccc94
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/a60ccc94
Branch: refs/heads/ib/4542
Commit: a60ccc945e61bd0ac91ddd46e0c9abd44f2368bd
Parents: 3fc560e
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 13 16:26:31 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:42 2015 +0000
----------------------------------------------------------------------
Allura/allura/model/repo_refresh.py | 7 +++++--
Allura/allura/webhooks.py | 18 ++++++++++++++----
2 files changed, 19 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/a60ccc94/Allura/allura/model/repo_refresh.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repo_refresh.py b/Allura/allura/model/repo_refresh.py
index 38ea295..1dc17ba 100644
--- a/Allura/allura/model/repo_refresh.py
+++ b/Allura/allura/model/repo_refresh.py
@@ -166,12 +166,15 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False):
commits_by_tags[t].append(commit)
from allura.webhooks import RepoPushWebhookSender
+ params = []
for b, commits in commits_by_branches.iteritems():
ref = u'refs/heads/{}'.format(b)
- RepoPushWebhookSender().send(commit_ids=commits, ref=ref)
+ params.append(dict(commit_ids=commits, ref=ref))
for t, commits in commits_by_tags.iteritems():
ref = u'refs/tags/{}'.format(t)
- RepoPushWebhookSender().send(commit_ids=commits, ref=ref)
+ params.append(dict(commit_ids=commits, ref=ref))
+ if params:
+ RepoPushWebhookSender().send(params)
log.info('Refresh complete for %s', repo.full_fs_path)
g.post_event('repo_refreshed', len(commit_ids), all_commits, new_clone)
http://git-wip-us.apache.org/repos/asf/allura/blob/a60ccc94/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index d2302d8..f727b63 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -284,18 +284,28 @@ class WebhookSender(object):
"""Return a dict with webhook payload"""
raise NotImplementedError('get_payload')
- def send(self, **kw):
- """Post a task that will send webhook payload"""
+ def send(self, params_or_list):
+ """Post a task that will send webhook payload
+
+ :param:`params_or_list` - dict with keyword parameters to be passed to
+ :meth:`get_payload` or a list of such dicts. If it's a list for each
+ element appropriate payload will be submitted, but limit will be
+ enforced only once for each webhook.
+ """
+ if not isinstance(params_or_list, list):
+ params_or_list = [params_or_list]
webhooks = M.Webhook.query.find(dict(
app_config_id=c.app.config._id,
type=self.type,
)).all()
if webhooks:
- payload = self.get_payload(**kw)
+ payloads = [self.get_payload(**params)
+ for params in params_or_list]
for webhook in webhooks:
if webhook.enforce_limit():
webhook.update_limit()
- send_webhook.post(webhook._id, payload)
+ for payload in payloads:
+ send_webhook.post(webhook._id, payload)
else:
log.warn('Webhook fires too often: %s. Skipping', webhook)
[02/37] allura git commit: [#5726] Refactored post_to_feed related
functional tests.
Posted by je...@apache.org.
[#5726] Refactored post_to_feed related functional tests.
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/c9577b91
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/c9577b91
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/c9577b91
Branch: refs/heads/ib/4542
Commit: c9577b91192571e5267fe60f35299ac418b612d2
Parents: ef5610b
Author: Heith Seewald <hs...@slashdotmedia.com>
Authored: Mon Feb 2 12:47:22 2015 -0500
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Mon Feb 2 18:24:52 2015 +0000
----------------------------------------------------------------------
Allura/allura/templates/widgets/edit_post.html | 2 +-
.../templates/widgets/new_topic_post.html | 2 +-
.../forgediscussion/controllers/root.py | 1 +
.../tests/functional/test_forum.py | 85 ++++++++------------
4 files changed, 37 insertions(+), 53 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/c9577b91/Allura/allura/templates/widgets/edit_post.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/widgets/edit_post.html b/Allura/allura/templates/widgets/edit_post.html
index a42803e..b3d814c 100644
--- a/Allura/allura/templates/widgets/edit_post.html
+++ b/Allura/allura/templates/widgets/edit_post.html
@@ -18,7 +18,7 @@
-#}
{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
<div>
- <form method="post" action="{{action}}"
+ <form id="edit_{{ action[-4:] }}" method="post" action="{{action}}"
enctype="multipart/form-data">
{% if show_subject %}
<input name="{{ widget.context_for(widget.fields.subject)['rendered_name'] }}" style="width:97%"
http://git-wip-us.apache.org/repos/asf/allura/blob/c9577b91/Allura/allura/templates/widgets/new_topic_post.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/widgets/new_topic_post.html b/Allura/allura/templates/widgets/new_topic_post.html
index 1c03b94..c7f572a 100644
--- a/Allura/allura/templates/widgets/new_topic_post.html
+++ b/Allura/allura/templates/widgets/new_topic_post.html
@@ -17,7 +17,7 @@
under the License.
-#}
{% import 'allura:templates/jinja_master/lib.html' as lib with context %}
-<form method="post" action="{{action}}">
+<form id="create_new_topic" method="post" action="{{action}}">
{% if show_subject %}
<div class="grid-19"> </div>
<div class="grid-19">
http://git-wip-us.apache.org/repos/asf/allura/blob/c9577b91/ForgeDiscussion/forgediscussion/controllers/root.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/controllers/root.py b/ForgeDiscussion/forgediscussion/controllers/root.py
index 9174fba..7548cd0 100644
--- a/ForgeDiscussion/forgediscussion/controllers/root.py
+++ b/ForgeDiscussion/forgediscussion/controllers/root.py
@@ -57,6 +57,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
class W(object):
forum_subscription_form = FW.ForumSubscriptionForm()
new_topic = DW.NewTopicPost(submit_text='Post')
+
announcements_table = FW.AnnouncementsTable()
add_forum = AddForumShort()
search_results = SearchResults()
http://git-wip-us.apache.org/repos/asf/allura/blob/c9577b91/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
index 3f45b46..732c5c5 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
@@ -40,7 +40,6 @@ log = logging.getLogger(__name__)
class TestForumEmail(TestController):
-
def setUp(self):
TestController.setUp(self)
c.user = M.User.by_username('test-admin')
@@ -127,7 +126,6 @@ class TestForumEmail(TestController):
class TestForumAsync(TestController):
-
def setUp(self):
TestController.setUp(self)
self.app.get('/discussion/')
@@ -269,7 +267,6 @@ class TestForumAsync(TestController):
class TestForum(TestController):
-
def setUp(self):
TestController.setUp(self)
self.app.get('/discussion/')
@@ -290,6 +287,33 @@ class TestForum(TestController):
r = self.app.get('/admin/discussion/forums')
assert 'childforum' in r
+ @staticmethod
+ def fill_thread_reply(r):
+ form = r.forms['edit_post']
+ for field in form.fields.values():
+ field = field[0]
+ if field.id is None:
+ continue
+ if 'text' in field.id:
+ form[field.name] = 'Test_Reply'
+ return form
+
+ @staticmethod
+ def fill_new_topic_form(r):
+ form = r.forms['create_new_topic']
+ for field in form.fields.values():
+ field = field[0]
+ if field.id is None:
+ continue
+ if 'subject' in field.id:
+ form[field.name] = 'Test_Subject'
+ if 'forum' in field.id:
+ form[field.name] = 'testforum'
+ if 'text' in field.id:
+ form[field.name] = 'Test_Description'
+ return form
+
+
def test_unicode_name(self):
r = self.app.get('/admin/discussion/forums')
r.forms[1]['add_forum.shortname'] = u'téstforum'.encode('utf-8')
@@ -559,8 +583,7 @@ class TestForum(TestController):
'value') and field['value'] or ''
params[f.find('textarea')['name']] = 'Post text'
params[f.find('select')['name']] = 'testforum'
- params[f.find('input', {'style': 'width: 90%'})
- ['name']] = 'Post subject'
+ params[f.find('input', {'style': 'width: 90%'})['name']] = 'Post subject'
thread = self.app.post(
'/discussion/save_new_topic', params=params).follow()
assert M.Notification.query.find(
@@ -636,65 +659,26 @@ class TestForum(TestController):
def test_post_to_feed(self):
# Create a new topic
r = self.app.get('/discussion/create_topic/')
- f = r.html.find(
- 'form', {'action': '/p/test/discussion/save_new_topic'})
- params = dict()
- inputs = f.findAll('input')
- for field in inputs:
- if field.has_key('name'):
- params[field['name']] = field.has_key(
- 'value') and field['value'] or ''
- params[f.find('textarea')['name']] = 'XYZ'
- params[f.find('select')['name']] = 'testforum'
- params[f.find('input', {'style': 'width: 90%'})['name']] = 'AAAA'
- thread = self.app.post('/discussion/save_new_topic', params=params).follow()
+ form = self.fill_new_topic_form(r)
+ thread = form.submit().follow()
url = thread.request.url
# Check that the newly created topic is the most recent in the rss feed
f = self.app.get('/discussion/feed.rss').body
f = feedparser.parse(f)
newest_entry = f['entries'][0]['summary_detail']['value'].split("</p>")[0].split("<p>")[-1]
- assert newest_entry == 'XYZ'
+ assert newest_entry == 'Test_Description'
# Reply to the newly created thread.
thread = self.app.get(url)
- t = thread.html.find(
- 'div', {'class': 'row reply_post_form'}).find('form')
- rep_url = t.get('action')
- params = dict()
- inputs = t.findAll('input')
- for field in inputs:
- if field.has_key('name'):
- params[field['name']] = field.has_key(
- 'value') and field['value'] or ''
- params[t.find('textarea')['name']] = 'bbb'
- self.app.post(str(rep_url), params=params)
+ form = self.fill_thread_reply(thread)
+ form.submit()
# Check that reply matches the newest in the rss feed
f = self.app.get('/discussion/feed.rss').body
f = feedparser.parse(f)
newest_reply = f['entries'][0]['summary_detail']['value'].split("</p>")[0].split("<p>")[-1]
- assert newest_reply == 'bbb'
-
- def test_post_to_feed(self):
- r = self.app.get('/discussion/create_topic/')
- f = r.html.find(
- 'form', {'action': '/p/test/discussion/save_new_topic'})
- params = dict()
- inputs = f.findAll('input')
- for field in inputs:
- if field.has_key('name'):
- params[field['name']] = field.has_key(
- 'value') and field['value'] or ''
- params[f.find('textarea')['name']] = 'XYZ'
- params[f.find('select')['name']] = 'testforum'
- params[f.find('input', {'style': 'width: 90%'})['name']] = 'AAAA'
- self.app.post('/discussion/save_new_topic', params=params)
-
- f = self.app.get('/discussion/feed.rss').body
- f = feedparser.parse(f)
- newest_entry = f['entries'][0]['summary_detail']['value'].split("</p>")[0].split("<p>")[-1]
- assert newest_entry == 'XYZ'
+ assert newest_reply == 'Test_Reply'
def test_thread_sticky(self):
r = self.app.get('/discussion/create_topic/')
@@ -884,7 +868,6 @@ class TestForum(TestController):
class TestForumStats(TestController):
-
def test_stats(self):
self.app.get('/discussion/stats', status=200)
[04/37] allura git commit: [#6248] ticket:718 Fixed the truncation of
long list items in comments
Posted by je...@apache.org.
[#6248] ticket:718 Fixed the truncation of long list items in comments
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/1daebd15
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/1daebd15
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/1daebd15
Branch: refs/heads/ib/4542
Commit: 1daebd1524139042b57cd185cb06bba8f258c731
Parents: 78b0707
Author: Aleksey 'LXj' Alekseyev <go...@gmail.com>
Authored: Wed Feb 4 22:37:06 2015 +0200
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Feb 6 09:04:23 2015 +0000
----------------------------------------------------------------------
Allura/allura/nf/allura/css/site_style.css | 3 +++
1 file changed, 3 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/1daebd15/Allura/allura/nf/allura/css/site_style.css
----------------------------------------------------------------------
diff --git a/Allura/allura/nf/allura/css/site_style.css b/Allura/allura/nf/allura/css/site_style.css
index 4c62314..9f8f1b3 100644
--- a/Allura/allura/nf/allura/css/site_style.css
+++ b/Allura/allura/nf/allura/css/site_style.css
@@ -2529,6 +2529,9 @@ div.attachment_thumb .file_type span {
left: 27px;
}
+#comment .grid-14 { overflow: visible; }
+#comment .display_post { margin-right: 5px; }
+
#comment ul {
list-style: none;
margin: 0 20px 20px 0;
[27/37] allura git commit: [#4542] ticket:726 Fix tests
Posted by je...@apache.org.
[#4542] ticket:726 Fix tests
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/1ec65829
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/1ec65829
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/1ec65829
Branch: refs/heads/ib/4542
Commit: 1ec65829bff616b2b31dcf86739374b3fac4cc7f
Parents: 10cbeea
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Feb 11 15:54:51 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:40 2015 +0000
----------------------------------------------------------------------
Allura/allura/tests/test_webhooks.py | 145 +++++++++++-------------------
1 file changed, 53 insertions(+), 92 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/1ec65829/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 5362da7..3d51f5c 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -18,7 +18,6 @@ from tg import config
from allura import model as M
from allura.lib import helpers as h
from allura.webhooks import (
- MingOneOf,
WebhookValidator,
WebhookController,
send_webhook,
@@ -58,28 +57,12 @@ class TestWebhookBase(object):
class TestValidators(TestWebhookBase):
- def test_ming_one_of(self):
- ids = [ac._id for ac in M.AppConfig.query.find().all()[:2]]
- v = MingOneOf(cls=M.AppConfig, ids=ids, not_empty=True)
- with assert_raises(Invalid) as cm:
- v.to_python(None)
- assert_equal(cm.exception.msg, u'Please enter a value')
- with assert_raises(Invalid) as cm:
- v.to_python('invalid id')
- assert_equal(cm.exception.msg,
- u'Object must be one of: %s, not invalid id' % ids)
- assert_equal(v.to_python(ids[0]), M.AppConfig.query.get(_id=ids[0]))
- assert_equal(v.to_python(ids[1]), M.AppConfig.query.get(_id=ids[1]))
- assert_equal(v.to_python(unicode(ids[0])),
- M.AppConfig.query.get(_id=ids[0]))
- assert_equal(v.to_python(unicode(ids[1])),
- M.AppConfig.query.get(_id=ids[1]))
-
+ @with_git2
def test_webhook_validator(self):
sender = Mock(type='repo-push')
- ids = [ac._id for ac in M.AppConfig.query.find().all()[:3]]
- ids, invalid_id = ids[:2], ids[2]
- v = WebhookValidator(sender=sender, ac_ids=ids, not_empty=True)
+ app = self.git
+ invalid_app = self.project.app_instance('src2')
+ v = WebhookValidator(sender=sender, app=app, not_empty=True)
with assert_raises(Invalid) as cm:
v.to_python(None)
assert_equal(cm.exception.msg, u'Please enter a value')
@@ -88,21 +71,23 @@ class TestValidators(TestWebhookBase):
assert_equal(cm.exception.msg, u'Invalid webhook')
wh = M.Webhook(type='invalid type',
- app_config_id=invalid_id,
- hook_url='http://httpbin.org/post',
+ app_config_id=invalid_app.config._id,
+ hook_url='http://hooks.slack.com',
secret='secret')
session(wh).flush(wh)
+ # invalid type
with assert_raises(Invalid) as cm:
v.to_python(wh._id)
assert_equal(cm.exception.msg, u'Invalid webhook')
wh.type = 'repo-push'
session(wh).flush(wh)
+ # invalild app
with assert_raises(Invalid) as cm:
v.to_python(wh._id)
assert_equal(cm.exception.msg, u'Invalid webhook')
- wh.app_config_id = ids[0]
+ wh.app_config_id = app.config._id
session(wh).flush(wh)
assert_equal(v.to_python(wh._id), wh)
assert_equal(v.to_python(unicode(wh._id)), wh)
@@ -118,8 +103,7 @@ class TestWebhookController(TestController):
p.start()
self.project = M.Project.query.get(shortname=test_project_with_repo)
self.git = self.project.app_instance('src')
- self.git2 = self.project.app_instance('src2')
- self.url = str(self.project.url() + 'admin/webhooks')
+ self.url = str(self.git.admin_url + 'webhooks')
def tearDown(self):
super(TestWebhookController, self).tearDown()
@@ -127,7 +111,6 @@ class TestWebhookController(TestController):
p.stop()
@with_git
- @with_git2
def setup_with_tools(self):
pass
@@ -139,8 +122,9 @@ class TestWebhookController(TestController):
autospec=True)
return [gen_secret]
- def create_webhook(self, data):
- r = self.app.post(self.url + '/repo-push/create', data)
+ def create_webhook(self, data, url=None):
+ url = url or self.url
+ r = self.app.post(url + '/repo-push/create', data)
wf = json.loads(self.webflash(r))
assert_equal(wf['status'], 'ok')
assert_equal(wf['message'], 'Created successfully')
@@ -151,8 +135,7 @@ class TestWebhookController(TestController):
if field == '_the_form':
error = form.findPrevious('div', attrs={'class': 'error'})
else:
- widget = 'select' if field == 'app' else 'input'
- error = form.find(widget, attrs={'name': field})
+ error = form.find('input', attrs={'name': field})
error = error.findNext('div', attrs={'class': 'error'})
if error:
assert_in(h.escape(msg), error.getText())
@@ -169,7 +152,7 @@ class TestWebhookController(TestController):
status=302)
assert_equal(r.location,
'http://localhost/auth/'
- '?return_to=%2Fadobe%2Fadobe-1%2Fadmin%2Fwebhooks%2Frepo-push%2F')
+ '?return_to=%2Fadobe%2Fadobe-1%2Fadmin%2Fsrc%2Fwebhooks%2Frepo-push%2F')
def test_invalid_hook_type(self):
self.app.get(self.url + '/invalid-hook-type/', status=404)
@@ -180,12 +163,11 @@ class TestWebhookController(TestController):
assert_in('<h1>repo-push</h1>', r)
assert_not_in('http://httpbin.org/post', r)
data = {'url': u'http://httpbin.org/post',
- 'app': unicode(self.git.config._id),
'secret': ''}
msg = 'add webhook repo-push {} {}'.format(
data['url'], self.git.config.url())
with td.audits(msg):
- r = self.create_webhook(data).follow().follow(status=200)
+ r = self.create_webhook(data).follow()
assert_in('http://httpbin.org/post', r)
hooks = M.Webhook.query.find().all()
@@ -207,9 +189,8 @@ class TestWebhookController(TestController):
limit = json.dumps({'git': 1})
with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
data = {'url': u'http://httpbin.org/post',
- 'app': unicode(self.git.config._id),
'secret': ''}
- r = self.create_webhook(data).follow().follow(status=200)
+ r = self.create_webhook(data).follow()
assert_equal(M.Webhook.query.find().count(), 1)
r = self.app.post(self.url + '/repo-push/create', data)
@@ -217,7 +198,7 @@ class TestWebhookController(TestController):
assert_equal(wf['status'], 'error')
assert_equal(
wf['message'],
- 'You have exceeded the maximum number of projects '
+ 'You have exceeded the maximum number of webhooks '
'you are allowed to create for this project/app')
assert_equal(M.Webhook.query.find().count(), 1)
@@ -226,43 +207,33 @@ class TestWebhookController(TestController):
r = self.app.post(
self.url + '/repo-push/create', {}, status=404)
- data = {'url': '', 'app': '', 'secret': ''}
+ data = {'url': '', 'secret': ''}
r = self.app.post(self.url + '/repo-push/create', data)
self.find_error(r, 'url', 'Please enter a value')
- self.find_error(r, 'app', 'Please enter a value')
- data = {'url': 'qwer', 'app': '123', 'secret': 'qwe'}
+ data = {'url': 'qwer', 'secret': 'qwe'}
r = self.app.post(self.url + '/repo-push/create', data)
self.find_error(r, 'url',
'You must provide a full domain name (like qwer.com)')
- self.find_error(r, 'app', 'Object must be one of: ')
- self.find_error(r, 'app', '%s' % self.git.config._id)
- self.find_error(r, 'app', '%s' % self.git2.config._id)
def test_edit(self):
data1 = {'url': u'http://httpbin.org/post',
- 'app': unicode(self.git.config._id),
'secret': u'secret'}
data2 = {'url': u'http://example.com/hook',
- 'app': unicode(self.git2.config._id),
'secret': u'secret2'}
- self.create_webhook(data1).follow().follow(status=200)
- self.create_webhook(data2).follow().follow(status=200)
+ self.create_webhook(data1).follow()
+ self.create_webhook(data2).follow()
assert_equal(M.Webhook.query.find().count(), 2)
wh1 = M.Webhook.query.get(hook_url=data1['url'])
r = self.app.get(self.url + '/repo-push/%s' % wh1._id)
form = r.forms[0]
assert_equal(form['url'].value, data1['url'])
- assert_equal(form['app'].value, data1['app'])
assert_equal(form['secret'].value, data1['secret'])
assert_equal(form['webhook'].value, unicode(wh1._id))
form['url'] = 'http://host.org/hook'
- form['app'] = unicode(self.git2.config._id)
form['secret'] = 'new secret'
- msg = 'edit webhook repo-push\n{} => {}\n{} => {}\n{}'.format(
- data1['url'], form['url'].value,
- self.git.config.url(), self.git2.config.url(),
- 'secret changed')
+ msg = 'edit webhook repo-push\n{} => {}\n{}'.format(
+ data1['url'], form['url'].value, 'secret changed')
with td.audits(msg):
r = form.submit()
wf = json.loads(self.webflash(r))
@@ -271,7 +242,7 @@ class TestWebhookController(TestController):
assert_equal(M.Webhook.query.find().count(), 2)
wh1 = M.Webhook.query.get(_id=wh1._id)
assert_equal(wh1.hook_url, 'http://host.org/hook')
- assert_equal(wh1.app_config_id, self.git2.config._id)
+ assert_equal(wh1.app_config_id, self.git.config._id)
assert_equal(wh1.secret, 'new secret')
assert_equal(wh1.type, 'repo-push')
@@ -279,10 +250,9 @@ class TestWebhookController(TestController):
r = self.app.get(self.url + '/repo-push/%s' % wh1._id)
form = r.forms[0]
form['url'] = data2['url']
- form['app'] = data2['app']
r = form.submit()
self.find_error(r, '_the_form',
- u'"repo-push" webhook already exists for Git2 http://example.com/hook',
+ u'"repo-push" webhook already exists for Git http://example.com/hook',
form_type='edit')
def test_edit_validation(self):
@@ -295,9 +265,8 @@ class TestWebhookController(TestController):
self.app.get(self.url + '/repo-push/%s' % invalid._id, status=404)
data = {'url': u'http://httpbin.org/post',
- 'app': unicode(self.git.config._id),
'secret': u'secret'}
- self.create_webhook(data).follow().follow(status=200)
+ self.create_webhook(data).follow()
wh = M.Webhook.query.get(hook_url=data['url'], type='repo-push')
# invalid id in hidden field, just in case
@@ -307,25 +276,19 @@ class TestWebhookController(TestController):
self.app.post(self.url + '/repo-push/edit', data, status=404)
# empty values
- data = {'url': '', 'app': '', 'secret': '', 'webhook': str(wh._id)}
+ data = {'url': '', 'secret': '', 'webhook': str(wh._id)}
r = self.app.post(self.url + '/repo-push/edit', data)
self.find_error(r, 'url', 'Please enter a value', 'edit')
- self.find_error(r, 'app', 'Please enter a value', 'edit')
- data = {'url': 'qwe', 'app': '123', 'secret': 'qwe',
- 'webhook': str(wh._id)}
+ data = {'url': 'qwe', 'secret': 'qwe', 'webhook': str(wh._id)}
r = self.app.post(self.url + '/repo-push/edit', data)
self.find_error(r, 'url',
'You must provide a full domain name (like qwe.com)', 'edit')
- self.find_error(r, 'app', 'Object must be one of:', 'edit')
- self.find_error(r, 'app', '%s' % self.git.config._id, 'edit')
- self.find_error(r, 'app', '%s' % self.git2.config._id, 'edit')
def test_delete(self):
data = {'url': u'http://httpbin.org/post',
- 'app': unicode(self.git.config._id),
'secret': u'secret'}
- self.create_webhook(data).follow().follow(status=200)
+ self.create_webhook(data).follow()
assert_equal(M.Webhook.query.find().count(), 1)
wh = M.Webhook.query.get(hook_url=data['url'])
data = {'webhook': unicode(wh._id)}
@@ -352,15 +315,19 @@ class TestWebhookController(TestController):
self.app.post(self.url + '/repo-push/delete', data, status=404)
assert_equal(M.Webhook.query.find().count(), 1)
+ @with_git2
def test_list_webhooks(self):
+ git2 = self.project.app_instance('src2')
+ url2 = str(git2.admin_url + 'webhooks')
data1 = {'url': u'http://httpbin.org/post',
- 'app': unicode(self.git.config._id),
'secret': 'secret'}
data2 = {'url': u'http://another-host.org/',
- 'app': unicode(self.git2.config._id),
'secret': 'secret2'}
- self.create_webhook(data1).follow().follow(status=200)
- self.create_webhook(data2).follow().follow(status=200)
+ data3 = {'url': u'http://another-app.org/',
+ 'secret': 'secret3'}
+ self.create_webhook(data1).follow()
+ self.create_webhook(data2).follow()
+ self.create_webhook(data3, url=url2).follow()
wh1 = M.Webhook.query.get(hook_url=data1['url'])
wh2 = M.Webhook.query.get(hook_url=data2['url'])
@@ -370,22 +337,23 @@ class TestWebhookController(TestController):
assert_equal(len(rows), 2)
rows = sorted([self._format_row(row) for row in rows])
expected_rows = sorted([
- [{'href': self.url + '/repo-push/' + str(wh1._id),
- 'text': wh1.hook_url},
- {'href': self.git.url,
- 'text': self.git.config.options.mount_label},
+ [{'text': wh1.hook_url},
{'text': wh1.secret},
+ {'href': self.url + '/repo-push/' + str(wh1._id),
+ 'text': u'Edit'},
{'href': self.url + '/repo-push/delete',
'data-id': str(wh1._id)}],
- [{'href': self.url + '/repo-push/' + str(wh2._id),
- 'text': wh2.hook_url},
- {'href': self.git2.url,
- 'text': self.git2.config.options.mount_label},
+ [{'text': wh2.hook_url},
{'text': wh2.secret},
+ {'href': self.url + '/repo-push/' + str(wh2._id),
+ 'text': u'Edit'},
{'href': self.url + '/repo-push/delete',
'data-id': str(wh2._id)}],
])
assert_equal(rows, expected_rows)
+ # make sure webhooks for another app is not visible
+ assert_not_in(u'http://another-app.org/', r)
+ assert_not_in(u'secret3', r)
def _format_row(self, row):
def link(td):
@@ -397,7 +365,7 @@ class TestWebhookController(TestController):
a = td.find('a')
return {'href': a.get('href'), 'data-id': a.get('data-id')}
tds = row.findAll('td')
- return [link(tds[0]), link(tds[1]), text(tds[2]), delete_btn(tds[3])]
+ return [text(tds[0]), text(tds[1]), link(tds[2]), delete_btn(tds[3])]
class TestSendWebhookHelper(TestWebhookBase):
@@ -568,30 +536,23 @@ class TestRepoPushWebhookSender(TestWebhookBase):
sender = RepoPushWebhookSender()
# default
- assert_equal(sender.enforce_limit(self.git.config), True)
+ assert_equal(sender.enforce_limit(self.git), True)
add_webhooks('one', 3)
- assert_equal(sender.enforce_limit(self.git.config), False)
+ assert_equal(sender.enforce_limit(self.git), False)
# config
limit = json.dumps({'git': 5})
with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
- assert_equal(sender.enforce_limit(self.git.config), True)
+ assert_equal(sender.enforce_limit(self.git), True)
add_webhooks('two', 3)
- assert_equal(sender.enforce_limit(self.git.config), False)
+ assert_equal(sender.enforce_limit(self.git), False)
class TestModels(TestWebhookBase):
- def test_webhook_find(self):
- p = M.Project.query.get(shortname='test')
- assert_equal(M.Webhook.find('smth', p), [])
- assert_equal(M.Webhook.find('repo-push', p), [])
- assert_equal(M.Webhook.find('smth', self.project), [])
- assert_equal(M.Webhook.find('repo-push', self.project), [self.wh])
-
def test_webhook_url(self):
assert_equal(self.wh.url(),
- '/adobe/adobe-1/admin/webhooks/repo-push/{}'.format(self.wh._id))
+ '/adobe/adobe-1/admin/src/webhooks/repo-push/{}'.format(self.wh._id))
def test_webhook_enforce_limit(self):
self.wh.last_sent = None
[16/37] allura git commit: [#4542] ticket:714 Basic webhooks
framework & repo-push hook
Posted by je...@apache.org.
[#4542] ticket:714 Basic webhooks framework & repo-push hook
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/0eb35a94
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/0eb35a94
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/0eb35a94
Branch: refs/heads/ib/4542
Commit: 0eb35a94ff74dc7d456f21a0bfa334763955916b
Parents: 0d6a443
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Jan 28 10:15:57 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:16:47 2015 +0000
----------------------------------------------------------------------
Allura/allura/ext/admin/admin_main.py | 29 ++++
.../ext/admin/templates/webhooks_list.html | 48 ++++++
Allura/allura/lib/utils.py | 8 +
Allura/allura/model/__init__.py | 1 +
Allura/allura/model/repo_refresh.py | 2 +
Allura/allura/model/webhook.py | 42 ++++++
.../allura/templates/webhooks/create_form.html | 90 +++++++++++
Allura/allura/webhooks.py | 148 +++++++++++++++++++
Allura/setup.py | 3 +
9 files changed, 371 insertions(+)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/0eb35a94/Allura/allura/ext/admin/admin_main.py
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index 26b058f..52b80f9 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -30,6 +30,7 @@ from webob import exc
from bson import ObjectId
from ming.orm.ormsession import ThreadLocalORMSession
from ming.odm import session
+from ming.utils import LazyProperty
from allura.app import Application, DefaultAdminController, SitemapEntry
from allura.lib import helpers as h
from allura import version
@@ -147,6 +148,7 @@ class AdminApp(Application):
SitemapEntry('Categorization', admin_url + 'trove')
]
links.append(SitemapEntry('Tools', admin_url + 'tools'))
+ links.append(SitemapEntry('Webhooks', admin_url + 'webhooks'))
if asbool(config.get('bulk_export_enabled', True)):
links.append(SitemapEntry('Export', admin_url + 'export'))
if c.project.is_root and has_access(c.project, 'admin')():
@@ -191,6 +193,32 @@ class AdminExtensionLookup(object):
raise exc.HTTPNotFound, name
+class WebhooksLookup(BaseController):
+
+ @LazyProperty
+ def _webhooks(self):
+ webhooks = h.iter_entry_points('allura.webhooks')
+ webhooks = [ep.load() for ep in webhooks]
+ return webhooks
+
+ @with_trailing_slash
+ @expose('jinja:allura.ext.admin:templates/webhooks_list.html')
+ def index(self):
+ webhooks = self._webhooks
+ configured_hooks = {}
+ for hook in webhooks:
+ configured_hooks[hook.type] = M.Webhook.find(hook.type, c.project)
+ return {'webhooks': webhooks,
+ 'configured_hooks': configured_hooks}
+
+ @expose()
+ def _lookup(self, name, *remainder):
+ for hook in self._webhooks:
+ if hook.type == name and hook.controller:
+ return hook.controller(hook), remainder
+ raise exc.HTTPNotFound, name
+
+
class ProjectAdminController(BaseController):
def _check_security(self):
require_access(c.project, 'admin')
@@ -200,6 +228,7 @@ class ProjectAdminController(BaseController):
self.groups = GroupsController()
self.audit = AuditController()
self.ext = AdminExtensionLookup()
+ self.webhooks = WebhooksLookup()
@with_trailing_slash
@expose('jinja:allura.ext.admin:templates/project_admin.html')
http://git-wip-us.apache.org/repos/asf/allura/blob/0eb35a94/Allura/allura/ext/admin/templates/webhooks_list.html
----------------------------------------------------------------------
diff --git a/Allura/allura/ext/admin/templates/webhooks_list.html b/Allura/allura/ext/admin/templates/webhooks_list.html
new file mode 100644
index 0000000..9bb2476
--- /dev/null
+++ b/Allura/allura/ext/admin/templates/webhooks_list.html
@@ -0,0 +1,48 @@
+{#-
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-#}
+{% extends g.theme.master %}
+
+{% block title %}{{c.project.name}} / Webhooks{% endblock %}
+{% block header %}Webhooks{% endblock %}
+
+{% block content %}
+ {% for hook in webhooks %}
+ <h1>{{ hook.type }}</h1>
+ <p><a href="{{ hook.type }}">Create</a></p>
+ {% if configured_hooks[hook.type] %}
+ <table>
+ {% for h in configured_hooks[hook.type] %}
+ <tr>
+ <td>
+ <a href="{{ h.url() }}">{{ h.hook_url }}</a>
+ </td>
+ <td>
+ <a href="{{ h.app_config.url() }}">{{ h.app_config.options.mount_label }}</a>
+ </td>
+ <td>
+ <a href="#" title="Delete">
+ <b data-icon="{{g.icons['delete'].char}}" class="ico {{g.icons['delete'].css}}" title="Delete"></b>
+ </a>
+ </td>
+ </tr>
+ {% endfor %}
+ </table>
+ {% endif %}
+ {% endfor %}
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/0eb35a94/Allura/allura/lib/utils.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/utils.py b/Allura/allura/lib/utils.py
index 7a56603..83950c6 100644
--- a/Allura/allura/lib/utils.py
+++ b/Allura/allura/lib/utils.py
@@ -32,6 +32,7 @@ import collections
import tg
import pylons
+import json
import webob.multidict
from formencode import Invalid
from tg.decorators import before_validate
@@ -592,3 +593,10 @@ class EmptyCursor(ODMCursor):
def sort(self, *args, **kw):
return self
+
+
+class DateJSONEncoder(json.JSONEncoder):
+ def default(self, obj):
+ if isinstance(obj, datetime.datetime):
+ return obj.strftime('%Y-%m-%dT%H:%M:%SZ')
+ return json.JSONEncoder.default(self, obj)
http://git-wip-us.apache.org/repos/asf/allura/blob/0eb35a94/Allura/allura/model/__init__.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/__init__.py b/Allura/allura/model/__init__.py
index a74bff6..784d1af 100644
--- a/Allura/allura/model/__init__.py
+++ b/Allura/allura/model/__init__.py
@@ -34,6 +34,7 @@ from .repository import MergeRequest, GitLikeTree
from .stats import Stats
from .oauth import OAuthToken, OAuthConsumerToken, OAuthRequestToken, OAuthAccessToken
from .monq_model import MonQTask
+from .webhook import Webhook
from .types import ACE, ACL, EVERYONE, ALL_PERMISSIONS, DENY_ALL, MarkdownCache
from .session import main_doc_session, main_orm_session
http://git-wip-us.apache.org/repos/asf/allura/blob/0eb35a94/Allura/allura/model/repo_refresh.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repo_refresh.py b/Allura/allura/model/repo_refresh.py
index 3dbad4a..86953cc 100644
--- a/Allura/allura/model/repo_refresh.py
+++ b/Allura/allura/model/repo_refresh.py
@@ -146,6 +146,8 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False):
g.director.create_activity(actor, 'committed', new,
related_nodes=[repo.app_config.project],
tags=['commit', repo.tool.lower()])
+ from allura.webhooks import RepoPushWebhookSender
+ RepoPushWebhookSender().send(commit_ids=commit_ids)
log.info('Refresh complete for %s', repo.full_fs_path)
g.post_event('repo_refreshed', len(commit_ids), all_commits, new_clone)
http://git-wip-us.apache.org/repos/asf/allura/blob/0eb35a94/Allura/allura/model/webhook.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
new file mode 100644
index 0000000..73b9540
--- /dev/null
+++ b/Allura/allura/model/webhook.py
@@ -0,0 +1,42 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 ming.odm import FieldProperty
+from allura.model import Artifact
+
+
+class Webhook(Artifact):
+
+ class __mongometa__:
+ name = 'webhook'
+ unique_indexes = [('app_config_id', 'type', 'hook_url')]
+
+ type = FieldProperty(str)
+ hook_url = FieldProperty(str)
+
+ def url(self):
+ return '{}{}/{}/{}'.format(
+ self.app_config.project.url(),
+ 'admin/webhooks',
+ self.type,
+ self._id)
+
+ @classmethod
+ def find(cls, type, project):
+ ac_ids = [ac._id for ac in project.app_configs]
+ hooks = cls.query.find(dict(type=type, app_config_id={'$in': ac_ids}))
+ return hooks.all()
http://git-wip-us.apache.org/repos/asf/allura/blob/0eb35a94/Allura/allura/templates/webhooks/create_form.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/webhooks/create_form.html b/Allura/allura/templates/webhooks/create_form.html
new file mode 100644
index 0000000..a44bb5b
--- /dev/null
+++ b/Allura/allura/templates/webhooks/create_form.html
@@ -0,0 +1,90 @@
+{#-
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you 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.
+-#}
+{% extends g.theme.master %}
+
+{% block title %}{{c.project.name}} / Create {{webhook.type}} webhook{% endblock %}
+
+{% block header %}Create {{webhook.type}} webhook{% endblock %}
+
+{% block extra_css %}
+ <style type="text/css">
+ form {
+ padding: 0 20px 20px 20px;
+ }
+ form label {
+ display: inline-block;
+ width: 30%;
+ vertical-align: top;
+ }
+ form > div {
+ margin-bottom: 10px;
+ }
+ form > div input {
+ width: 30%;
+ vertical-align: top;
+ }
+ form > div input[type="checkbox"] {
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ -o-box-shadow: none;
+ box-shadow: none;
+ width: 1em;
+ }
+ form .error {
+ display: inline-block;
+ color: #f00;
+ background: none;
+ border: none;
+ margin: 0;
+ width: 30%;
+ }
+ </style>
+{% endblock %}
+
+{%- macro error(field_name) %}
+ {% if c.form_errors[field_name] %}
+ <div class="error">{{c.form_errors[field_name]}}</div>
+ {% endif %}
+{%- endmacro %}
+
+{% block content %}
+<form action="create" method="post" enctype="multipart/form-data">
+ <div>
+ <label for="url">url</label>
+ <input name="url" value="{{ c.form_values['url'] }}">
+ {{ error('url') }}
+ </div>
+ <div>
+ <label for="app">app</label>
+ <select name="app">
+ {% for ac in form.triggered_by %}
+ <option value="{{ac._id}}"{% if h.really_unicode(ac._id) == c.form_values['app'] %} selected{% endif %}>
+ {{ ac.options.mount_label }}
+ </option>
+ {% endfor %}
+ </select>
+ {{ error('app') }}
+ </div>
+
+ {% block additional_fields %}{% endblock %}
+
+ <input type="submit" value="Create">
+ {{lib.csrf_token()}}
+</form>
+{% endblock %}
http://git-wip-us.apache.org/repos/asf/allura/blob/0eb35a94/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
new file mode 100644
index 0000000..c5dc37e
--- /dev/null
+++ b/Allura/allura/webhooks.py
@@ -0,0 +1,148 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+import logging
+import json
+
+import requests
+from bson import ObjectId
+from tg import expose, validate, redirect
+from tg.decorators import with_trailing_slash
+from pylons import tmpl_context as c
+from formencode import validators as fev, schema
+from ming.odm import session
+
+from allura.controllers import BaseController
+from allura.lib import helpers as h
+from allura.lib.decorators import require_post, task
+from allura.lib.utils import DateJSONEncoder
+from allura import model as M
+
+
+log = logging.getLogger(__name__)
+
+
+class WebhookCreateForm(schema.Schema):
+ def __init__(self, hook):
+ super(WebhookCreateForm, self).__init__()
+ self.triggered_by = [ac for ac in c.project.app_configs
+ if ac.tool_name.lower() in hook.triggered_by]
+ self.add_field('app', fev.OneOf(
+ [unicode(ac._id) for ac in self.triggered_by]))
+
+ url = fev.URL(not_empty=True)
+
+
+class WebhookControllerMeta(type):
+ def __call__(cls, hook, *args, **kw):
+ """Decorate the `create` post handler with a validator that references
+ the appropriate webhook sender for this controller.
+ """
+ if hasattr(cls, 'create'):
+ cls.create = validate(
+ cls.create_form(hook),
+ error_handler=cls.index.__func__,
+ )(cls.create)
+ return type.__call__(cls, hook, *args, **kw)
+
+
+class WebhookController(BaseController):
+ __metaclass__ = WebhookControllerMeta
+ create_form = WebhookCreateForm
+
+ def __init__(self, hook):
+ super(WebhookController, self).__init__()
+ self.webhook = hook
+
+ @with_trailing_slash
+ @expose('jinja:allura:templates/webhooks/create_form.html')
+ def index(self, **kw):
+ return {'webhook': self.webhook,
+ 'form': self.create_form(self.webhook)}
+
+ @expose()
+ @require_post()
+ def create(self, url, app):
+ # TODO: catch DuplicateKeyError
+ wh = M.Webhook(
+ hook_url=url,
+ app_config_id=ObjectId(app),
+ type=self.webhook.type)
+ session(wh).flush(wh)
+ redirect(c.project.url() + 'admin/webhooks/')
+
+
+@task()
+def send_webhook(webhook_id, payload):
+ webhook = M.Webhook.query.get(_id=webhook_id)
+ url = webhook.hook_url
+ headers = {'content-type': 'application/json'}
+ json_payload = json.dumps(payload, cls=DateJSONEncoder)
+ # TODO: catch
+ # TODO: configurable timeout
+ r = requests.post(url, data=json_payload, headers=headers, timeout=30)
+ if r.status_code >= 200 and r.status_code <= 300:
+ log.info('Webhook successfully sent: %s %s %s',
+ webhook.type, webhook.hook_url, webhook.app_config.url())
+ else:
+ # TODO: retry
+ # TODO: configurable retries
+ log.error('Webhook send error: %s %s %s %s %s',
+ webhook.type, webhook.hook_url,
+ webhook.app_config.url(),
+ r.status_code, r.reason)
+
+
+class WebhookSender(object):
+ """Base class for webhook senders.
+
+ Subclasses are required to implement :meth:`get_payload()` and set
+ :attr:`type` and :attr:`triggered_by`.
+ """
+
+ type = None
+ triggered_by = []
+ controller = WebhookController
+
+ def get_payload(self, **kw):
+ """Return a dict with webhook payload"""
+ raise NotImplementedError('get_payload')
+
+ def send(self, **kw):
+ """Post a task that will send webhook payload"""
+ webhooks = M.Webhook.query.find(dict(
+ app_config_id=c.app.config._id,
+ type=self.type,
+ )).all()
+ if webhooks:
+ payload = self.get_payload(**kw)
+ for webhook in webhooks:
+ send_webhook.post(webhook._id, payload)
+
+
+class RepoPushWebhookSender(WebhookSender):
+ type = 'repo-push'
+ triggered_by = ['git', 'hg', 'svn']
+
+ def get_payload(self, commit_ids, **kw):
+ app = kw.get('app') or c.app
+ payload = {
+ 'url': h.absurl(app.url),
+ 'count': len(commit_ids),
+ 'revisions': [app.repo.commit(ci).info for ci in commit_ids],
+ }
+ return payload
http://git-wip-us.apache.org/repos/asf/allura/blob/0eb35a94/Allura/setup.py
----------------------------------------------------------------------
diff --git a/Allura/setup.py b/Allura/setup.py
index 8eabf60..e44c0c2 100644
--- a/Allura/setup.py
+++ b/Allura/setup.py
@@ -135,6 +135,9 @@ setup(
tools = allura.ext.user_profile.user_main:ToolsSection
social = allura.ext.user_profile.user_main:SocialSection
+ [allura.webhooks]
+ repo-push = allura.webhooks:RepoPushWebhookSender
+
[paste.paster_command]
taskd = allura.command.taskd:TaskdCommand
taskd_cleanup = allura.command.taskd_cleanup:TaskdCleanupCommand
[03/37] allura git commit: [#7795] ticket:717 Fix test failure due to
mim access from two threads
Posted by je...@apache.org.
[#7795] ticket:717 Fix test failure due to mim access from two threads
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/78b07073
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/78b07073
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/78b07073
Branch: refs/heads/ib/4542
Commit: 78b0707396d45deea91de1eeff4dfe5fe82c4b84
Parents: c9577b9
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Feb 3 12:09:31 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Tue Feb 3 12:09:31 2015 +0000
----------------------------------------------------------------------
ForgeWiki/forgewiki/tests/test_models.py | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/78b07073/ForgeWiki/forgewiki/tests/test_models.py
----------------------------------------------------------------------
diff --git a/ForgeWiki/forgewiki/tests/test_models.py b/ForgeWiki/forgewiki/tests/test_models.py
index ac07669..5c10e50 100644
--- a/ForgeWiki/forgewiki/tests/test_models.py
+++ b/ForgeWiki/forgewiki/tests/test_models.py
@@ -30,18 +30,24 @@ class TestPageSnapshots(TestController):
# details https://sourceforge.net/p/allura/tickets/7647/
import time
import random
- from threading import Thread
+ from threading import Thread, Lock
page = Page.upsert('test-page')
page.commit()
+ lock = Lock()
def run(n):
setup_global_objects()
for i in range(10):
page = Page.query.get(title='test-page')
page.text = 'Test Page %s.%s' % (n, i)
time.sleep(random.random())
- page.commit()
+ # tests use mim (mongo-in-memory), which isn't thread-safe
+ lock.acquire()
+ try:
+ page.commit()
+ finally:
+ lock.release()
t1 = Thread(target=lambda: run(1))
t2 = Thread(target=lambda: run(2))
[23/37] allura git commit: [#4542] ticket:715 Add thresholds
Posted by je...@apache.org.
[#4542] ticket:715 Add thresholds
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/e7c60a34
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/e7c60a34
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/e7c60a34
Branch: refs/heads/ib/4542
Commit: e7c60a345d82844f9d529d9171991b74b60f3515
Parents: 5c82af91
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 2 12:53:03 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:38 2015 +0000
----------------------------------------------------------------------
Allura/allura/tests/test_webhooks.py | 42 +++++++++++++++++++++++++++++++
Allura/allura/webhooks.py | 31 +++++++++++++++++------
Allura/development.ini | 6 +++++
3 files changed, 72 insertions(+), 7 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/e7c60a34/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 99e5f91..791128e 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -202,6 +202,25 @@ class TestWebhookController(TestController):
'"repo-push" webhook already exists for Git http://httpbin.org/post')
assert_equal(M.Webhook.query.find().count(), 1)
+ def test_create_limit_reached(self):
+ assert_equal(M.Webhook.query.find().count(), 0)
+ limit = json.dumps({'git': 1})
+ with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
+ data = {'url': u'http://httpbin.org/post',
+ 'app': unicode(self.git.config._id),
+ 'secret': ''}
+ r = self.create_webhook(data).follow().follow(status=200)
+ assert_equal(M.Webhook.query.find().count(), 1)
+
+ r = self.app.post(self.url + '/repo-push/create', data)
+ wf = json.loads(self.webflash(r))
+ assert_equal(wf['status'], 'error')
+ assert_equal(
+ wf['message'],
+ 'You have exceeded the maximum number of projects '
+ 'you are allowed to create for this project/app')
+ assert_equal(M.Webhook.query.find().count(), 1)
+
def test_create_validation(self):
assert_equal(M.Webhook.query.find().count(), 0)
r = self.app.post(
@@ -468,6 +487,29 @@ class TestRepoPushWebhookSender(TestWebhookBase):
}
assert_equal(result, expected_result)
+ def test_enforce_limit(self):
+ def add_webhooks(suffix, n):
+ for i in range(n):
+ webhook = M.Webhook(
+ type='repo-push',
+ app_config_id=self.git.config._id,
+ hook_url='http://httpbin.org/{}/{}'.format(suffix, i),
+ secret='secret')
+ session(webhook).flush(webhook)
+
+ sender = RepoPushWebhookSender()
+ # default
+ assert_equal(sender.enforce_limit(self.git.config), True)
+ add_webhooks('one', 3)
+ assert_equal(sender.enforce_limit(self.git.config), False)
+
+ # config
+ limit = json.dumps({'git': 5})
+ with h.push_config(config, **{'webhook.repo_push.max_hooks': limit}):
+ assert_equal(sender.enforce_limit(self.git.config), True)
+ add_webhooks('two', 3)
+ assert_equal(sender.enforce_limit(self.git.config), False)
+
class TestModels(TestWebhookBase):
http://git-wip-us.apache.org/repos/asf/allura/blob/e7c60a34/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index e13086d..7df8a42 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -22,7 +22,7 @@ import hashlib
import requests
from bson import ObjectId
-from tg import expose, validate, redirect, flash
+from tg import expose, validate, redirect, flash, config
from tg.decorators import with_trailing_slash, without_trailing_slash
from pylons import tmpl_context as c
from formencode import validators as fev, schema, Invalid
@@ -125,7 +125,7 @@ class WebhookController(BaseController):
def __init__(self, sender):
super(WebhookController, self).__init__()
- self.sender = sender
+ self.sender = sender()
def gen_secret(self):
return h.cryptographic_nonce(20)
@@ -169,11 +169,15 @@ class WebhookController(BaseController):
@expose()
@require_post()
def create(self, url, app, secret):
- wh = M.Webhook(type=self.sender.type)
- self.update_webhook(wh, url, app, secret)
- M.AuditLog.log('add webhook %s %s %s',
- wh.type, wh.hook_url, wh.app_config.url())
- flash('Created successfully', 'ok')
+ if self.sender.enforce_limit(app):
+ wh = M.Webhook(type=self.sender.type)
+ self.update_webhook(wh, url, app, secret)
+ M.AuditLog.log('add webhook %s %s %s',
+ wh.type, wh.hook_url, wh.app_config.url())
+ flash('Created successfully', 'ok')
+ else:
+ flash('You have exceeded the maximum number of projects '
+ 'you are allowed to create for this project/app', 'error')
redirect(c.project.url() + 'admin/webhooks/')
@expose()
@@ -278,6 +282,19 @@ class WebhookSender(object):
else:
log.warn('Webhook fires too often: %s. Skipping', webhook)
+ def enforce_limit(self, app_config):
+ '''
+ Checks if limit of webhooks created for given project/app is reached.
+ Returns False if limit is reached, True otherwise.
+ '''
+ _type = self.type.replace('-', '_')
+ limits = json.loads(config.get('webhook.%s.max_hooks' % _type, '{}'))
+ count = M.Webhook.query.find(dict(
+ app_config_id=app_config._id,
+ type=self.type,
+ )).count()
+ return count < limits.get(app_config.tool_name.lower(), 3)
+
class RepoPushWebhookSender(WebhookSender):
type = 'repo-push'
http://git-wip-us.apache.org/repos/asf/allura/blob/e7c60a34/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index 39a9efa..f08ffb7 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -135,6 +135,12 @@ user_prefs.maximum_claimed_emails = 20
# e.g. for repo-push webhook:
# webhook.repo_push.limit = 10
+# Limit max number of hooks that can be created for given project/app
+# Option name format: same as above.
+# Value format: json dict, where keys are app names (as appears in
+# `WebhookSender.triggered_by`) and values are actual limits (default=3), e.g.:
+# webhook.repo_push.max_hooks = {"git": 3, "hg": 3, "svn": 3}
+
# Additional fields for admin project/user search
# Note: whitespace after comma is important!
# search.project.additional_search_fields = private, url, title
[10/37] allura git commit: Fix unicode handling in changelog script
Posted by je...@apache.org.
Fix unicode handling in changelog script
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/1f019a1e
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/1f019a1e
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/1f019a1e
Branch: refs/heads/ib/4542
Commit: 1f019a1e1713d47f9d6872bf2691bba1b7e9769c
Parents: 6face2d
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 13 12:42:37 2015 +0200
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Fri Feb 13 12:48:03 2015 +0200
----------------------------------------------------------------------
scripts/changelog.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/1f019a1e/scripts/changelog.py
----------------------------------------------------------------------
diff --git a/scripts/changelog.py b/scripts/changelog.py
index d6ed617..8f49e94 100755
--- a/scripts/changelog.py
+++ b/scripts/changelog.py
@@ -66,7 +66,7 @@ def print_changelog(version, summaries):
'date': datetime.utcnow().strftime('%B %Y'),
})
for ticket in sorted(summaries.keys()):
- print " * [#{0}] {1}".format(ticket, summaries[ticket])
+ print " * [#{0}] {1}".format(ticket, summaries[ticket].encode('utf-8'))
if __name__ == '__main__':
main()
[31/37] allura git commit: [#4542] ticket:728 Add ref to webhook
payload
Posted by je...@apache.org.
[#4542] ticket:728 Add ref to webhook payload
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/3fc560e5
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/3fc560e5
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/3fc560e5
Branch: refs/heads/ib/4542
Commit: 3fc560e566c1dbd850514e368b109b2c621dabb0
Parents: a22f6f6
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 13 15:19:49 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:41 2015 +0000
----------------------------------------------------------------------
Allura/allura/model/repo_refresh.py | 26 +++++++++++++++++++++++++-
Allura/allura/webhooks.py | 22 +++++++++++++++++++++-
2 files changed, 46 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/3fc560e5/Allura/allura/model/repo_refresh.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repo_refresh.py b/Allura/allura/model/repo_refresh.py
index 86953cc..38ea295 100644
--- a/Allura/allura/model/repo_refresh.py
+++ b/Allura/allura/model/repo_refresh.py
@@ -134,6 +134,10 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False):
log.info('Compute last commit info %d: %s', (i + 1), ci._id)
if not all_commits and not new_clone:
+ commits_by_branches = {}
+ commits_by_tags = {}
+ current_branches = []
+ current_tags = []
for commit in commit_ids:
new = repo.commit(commit)
user = User.by_email_address(new.committed.email)
@@ -146,8 +150,28 @@ def refresh_repo(repo, all_commits=False, notify=True, new_clone=False):
g.director.create_activity(actor, 'committed', new,
related_nodes=[repo.app_config.project],
tags=['commit', repo.tool.lower()])
+
+ branches, tags = repo.symbolics_for_commit(new)
+ if branches:
+ current_branches = branches
+ if tags:
+ current_tags = tags
+ for b in current_branches:
+ if b not in commits_by_branches.keys():
+ commits_by_branches[b] = []
+ commits_by_branches[b].append(commit)
+ for t in current_tags:
+ if t not in commits_by_tags.keys():
+ commits_by_tags[t] = []
+ commits_by_tags[t].append(commit)
+
from allura.webhooks import RepoPushWebhookSender
- RepoPushWebhookSender().send(commit_ids=commit_ids)
+ for b, commits in commits_by_branches.iteritems():
+ ref = u'refs/heads/{}'.format(b)
+ RepoPushWebhookSender().send(commit_ids=commits, ref=ref)
+ for t, commits in commits_by_tags.iteritems():
+ ref = u'refs/tags/{}'.format(t)
+ RepoPushWebhookSender().send(commit_ids=commits, ref=ref)
log.info('Refresh complete for %s', repo.full_fs_path)
g.post_event('repo_refreshed', len(commit_ids), all_commits, new_clone)
http://git-wip-us.apache.org/repos/asf/allura/blob/3fc560e5/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 233c3d5..d2302d8 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -36,7 +36,6 @@ from paste.deploy.converters import asint, aslist
from allura.controllers import BaseController
from allura.lib import helpers as h
-from allura.lib import validators as av
from allura.lib.decorators import require_post, task
from allura.lib.utils import DateJSONEncoder
from allura import model as M
@@ -318,16 +317,37 @@ class RepoPushWebhookSender(WebhookSender):
type = 'repo-push'
triggered_by = ['git', 'hg', 'svn']
+ def _before(self, repo, commit_ids):
+ if len(commit_ids) > 0:
+ ci = commit_ids[-1]
+ parents = repo.commit(ci).parent_ids
+ if len(parents) > 0:
+ # Merge commit will have multiple parents. As far as I can tell
+ # the last one will be the branch head before merge
+ return parents[-1]
+ return u''
+
+ def _after(self, repo, commit_ids):
+ if len(commit_ids) > 0:
+ return commit_ids[0]
+ return u''
+
def get_payload(self, commit_ids, **kw):
app = kw.get('app') or c.app
commits = [app.repo.commit(ci).webhook_info for ci in commit_ids]
+ before = self._before(app.repo, commit_ids)
+ after = self._after(app.repo, commit_ids)
payload = {
'size': len(commits),
'commits': commits,
+ 'before': before,
+ 'after': after,
'repository': {
'name': app.config.options.mount_label,
'full_name': app.url,
'url': h.absurl(app.url),
},
}
+ if kw.get('ref'):
+ payload['ref'] = kw['ref']
return payload
[09/37] allura git commit: [#7831] Fixed an issue that prevented
users from logging out
Posted by je...@apache.org.
[#7831] Fixed an issue that prevented users from logging out
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/6bc50feb
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/6bc50feb
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/6bc50feb
Branch: refs/heads/ib/4542
Commit: 6bc50feb600b2ba642c84354442e3028dce12dc8
Parents: 70df2e8
Author: Heith Seewald <hs...@slashdotmedia.com>
Authored: Tue Feb 10 15:40:54 2015 -0500
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Wed Feb 11 17:10:21 2015 +0000
----------------------------------------------------------------------
.../allura/controllers/basetest_project_root.py | 33 +++++++++++---------
Allura/allura/lib/plugin.py | 1 +
Allura/allura/tests/functional/test_auth.py | 17 ++++++++--
3 files changed, 35 insertions(+), 16 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/6bc50feb/Allura/allura/controllers/basetest_project_root.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/basetest_project_root.py b/Allura/allura/controllers/basetest_project_root.py
index a2df7a2..459a62f 100644
--- a/Allura/allura/controllers/basetest_project_root.py
+++ b/Allura/allura/controllers/basetest_project_root.py
@@ -41,7 +41,6 @@ log = logging.getLogger(__name__)
class BasetestProjectRootController(WsgiDispatchController, ProjectController):
-
'''Root controller for testing -- it behaves just like a
ProjectController for test/ except that all tools are mounted,
on-demand, at the mount point that is the same as their entry point
@@ -119,23 +118,32 @@ class BasetestProjectRootController(WsgiDispatchController, ProjectController):
return app.root, remainder
def __call__(self, environ, start_response):
+ """ Called from a turbo gears 'app' instance.
+
+
+ :param environ: Extra environment variables.
+ Example: self.app.get('/auth/', extra_environ={'disable_auth_magic': "True"})
+ """
c.app = None
c.project = M.Project.query.get(
shortname='test', neighborhood_id=self.p_nbhd._id)
- auth = plugin.AuthenticationProvider.get(request)
- user = auth.by_username(environ.get('username', 'test-admin'))
- if not user:
- user = M.User.anonymous()
- environ['beaker.session']['username'] = user.username
- # save and persist, so that a creation time is set
- environ['beaker.session'].save()
- environ['beaker.session'].persist()
- c.user = auth.authenticate_request()
+ if 'disable_auth_magic' in environ:
+ auth = plugin.AuthenticationProvider.get(request)
+ c.user = auth.authenticate_request()
+ else:
+ auth = plugin.AuthenticationProvider.get(request)
+ user = auth.by_username(environ.get('username', 'test-admin'))
+ if not user:
+ user = M.User.anonymous()
+ environ['beaker.session']['username'] = user.username
+ # save and persist, so that a creation time is set
+ environ['beaker.session'].save()
+ environ['beaker.session'].persist()
+ c.user = auth.authenticate_request()
return WsgiDispatchController.__call__(self, environ, start_response)
class DispatchTest(object):
-
@expose()
def _lookup(self, *args):
if args:
@@ -145,7 +153,6 @@ class DispatchTest(object):
class NamedController(object):
-
def __init__(self, name):
self.name = name
@@ -159,7 +166,6 @@ class NamedController(object):
class SecurityTests(object):
-
@expose()
def _lookup(self, name, *args):
name = unquote(name)
@@ -169,7 +175,6 @@ class SecurityTests(object):
class SecurityTest(object):
-
def __init__(self):
from forgewiki import model as WM
c.app = c.project.app_instance('wiki')
http://git-wip-us.apache.org/repos/asf/allura/blob/6bc50feb/Allura/allura/lib/plugin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index f3f90a8..a901ba9 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -176,6 +176,7 @@ class AuthenticationProvider(object):
def logout(self):
self.session.invalidate()
+ self.session.save()
response.delete_cookie('allura-loggedin')
def validate_password(self, user, password):
http://git-wip-us.apache.org/repos/asf/allura/blob/6bc50feb/Allura/allura/tests/functional/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_auth.py b/Allura/allura/tests/functional/test_auth.py
index ce0e4e4..bfd3c72 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -74,6 +74,21 @@ class TestAuth(TestController):
username='test-usera', password='foo'))
assert 'Invalid login' in str(r), r.showbrowser()
+ def test_logout(self):
+ environ = {'disable_auth_magic': "True"}
+ r = self.app.get('/auth/', extra_environ=environ)
+ f = r.forms[0]
+ f['username'] = 'test-user'
+ f['password'] = 'foo'
+ r = f.submit().follow(extra_environ=environ)
+ logged_in_session = r.session['_id']
+ assert r.html.nav('a')[-1].string == "Log Out"
+
+ r = self.app.get('/auth/logout', extra_environ=environ).follow(extra_environ=environ)
+ logged_out_session = r.session['_id']
+ assert logged_in_session is not logged_out_session
+ assert r.html.nav('a')[-1].string == 'Log In'
+
def test_track_login(self):
user = M.User.by_username('test-user')
assert_equal(user.last_access['login_date'], None)
@@ -297,7 +312,6 @@ class TestAuth(TestController):
params=dict(a=email_address),
extra_environ=dict(username='test-user'))
-
user1 = M.User.query.get(username='test-user-1')
user1.claim_address(email_address)
email1 = M.EmailAddress.find(dict(email=email_address, claimed_by_user_id=user1._id)).first()
@@ -1503,7 +1517,6 @@ class TestDisableAccount(TestController):
class TestPasswordExpire(TestController):
-
def login(self, username='test-user', pwd='foo', query_string=''):
r = self.app.get('/auth/' + query_string, extra_environ={'username': '*anonymous'})
f = r.forms[0]
[08/37] allura git commit: [#7831] minor code improvements, including:
Posted by je...@apache.org.
[#7831] minor code improvements, including:
* disable_auth_magic=False should do what you expect
* set extra_environ on the app instead of on each request in the test
* assert_equal for more helpful failure output
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/6face2df
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/6face2df
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/6face2df
Branch: refs/heads/ib/4542
Commit: 6face2dfee01a32bd1559f772a5b09c2d09c142b
Parents: 6bc50fe
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Wed Feb 11 17:01:23 2015 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Wed Feb 11 17:10:21 2015 +0000
----------------------------------------------------------------------
Allura/allura/controllers/basetest_project_root.py | 8 ++++----
Allura/allura/tests/functional/test_auth.py | 12 ++++++------
2 files changed, 10 insertions(+), 10 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/6face2df/Allura/allura/controllers/basetest_project_root.py
----------------------------------------------------------------------
diff --git a/Allura/allura/controllers/basetest_project_root.py b/Allura/allura/controllers/basetest_project_root.py
index 459a62f..f655437 100644
--- a/Allura/allura/controllers/basetest_project_root.py
+++ b/Allura/allura/controllers/basetest_project_root.py
@@ -25,6 +25,7 @@ from pylons import tmpl_context as c
from pylons import request
from webob import exc
from tg import expose
+from paste.deploy.converters import asbool
from allura.lib.base import WsgiDispatchController
from allura.lib.security import require, require_authenticated, require_access, has_access
@@ -118,7 +119,7 @@ class BasetestProjectRootController(WsgiDispatchController, ProjectController):
return app.root, remainder
def __call__(self, environ, start_response):
- """ Called from a turbo gears 'app' instance.
+ """ Called from a WebTest 'app' instance.
:param environ: Extra environment variables.
@@ -127,11 +128,10 @@ class BasetestProjectRootController(WsgiDispatchController, ProjectController):
c.app = None
c.project = M.Project.query.get(
shortname='test', neighborhood_id=self.p_nbhd._id)
- if 'disable_auth_magic' in environ:
- auth = plugin.AuthenticationProvider.get(request)
+ auth = plugin.AuthenticationProvider.get(request)
+ if asbool(environ.get('disable_auth_magic')):
c.user = auth.authenticate_request()
else:
- auth = plugin.AuthenticationProvider.get(request)
user = auth.by_username(environ.get('username', 'test-admin'))
if not user:
user = M.User.anonymous()
http://git-wip-us.apache.org/repos/asf/allura/blob/6face2df/Allura/allura/tests/functional/test_auth.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_auth.py b/Allura/allura/tests/functional/test_auth.py
index bfd3c72..b71ffcd 100644
--- a/Allura/allura/tests/functional/test_auth.py
+++ b/Allura/allura/tests/functional/test_auth.py
@@ -75,19 +75,19 @@ class TestAuth(TestController):
assert 'Invalid login' in str(r), r.showbrowser()
def test_logout(self):
- environ = {'disable_auth_magic': "True"}
- r = self.app.get('/auth/', extra_environ=environ)
+ self.app.extra_environ = {'disable_auth_magic': 'True'}
+ r = self.app.get('/auth/')
f = r.forms[0]
f['username'] = 'test-user'
f['password'] = 'foo'
- r = f.submit().follow(extra_environ=environ)
+ r = f.submit().follow()
logged_in_session = r.session['_id']
- assert r.html.nav('a')[-1].string == "Log Out"
+ assert_equal(r.html.nav('a')[-1].string, "Log Out")
- r = self.app.get('/auth/logout', extra_environ=environ).follow(extra_environ=environ)
+ r = self.app.get('/auth/logout').follow()
logged_out_session = r.session['_id']
assert logged_in_session is not logged_out_session
- assert r.html.nav('a')[-1].string == 'Log In'
+ assert_equal(r.html.nav('a')[-1].string, 'Log In')
def test_track_login(self):
user = M.User.by_username('test-user')
[25/37] allura git commit: [#4542] ticket:723 Tweak error message
Posted by je...@apache.org.
[#4542] ticket:723 Tweak error message
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/1b1dcfdf
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/1b1dcfdf
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/1b1dcfdf
Branch: refs/heads/ib/4542
Commit: 1b1dcfdf92874c5506f74dd5b7b1d3831694bd51
Parents: 072654c
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Feb 10 11:17:11 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:39 2015 +0000
----------------------------------------------------------------------
Allura/allura/tests/test_webhooks.py | 19 +++++++++++++------
Allura/allura/webhooks.py | 7 ++++---
2 files changed, 17 insertions(+), 9 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/1b1dcfdf/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index ab82cb8..1879beb 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -430,9 +430,14 @@ class TestSendWebhookHelper(TestWebhookBase):
assert_equal(
self.h.log_msg('OK'),
'OK: repo-push http://httpbin.org/post /adobe/adobe-1/src/')
+ response = Mock(
+ status_code=500,
+ text='that is why',
+ headers={'Content-Type': 'application/json'})
assert_equal(
- self.h.log_msg('Error', response=Mock(status_code=500, reason='that is why')),
- 'Error: repo-push http://httpbin.org/post /adobe/adobe-1/src/ 500 that is why')
+ self.h.log_msg('Error', response=response),
+ "Error: repo-push http://httpbin.org/post /adobe/adobe-1/src/ 500 "
+ "that is why {'Content-Type': 'application/json'}")
@patch('allura.webhooks.SendWebhookHelper', autospec=True)
def test_send_webhook_task(self, swh):
@@ -473,11 +478,12 @@ class TestSendWebhookHelper(TestWebhookBase):
call('Retrying webhook in %s seconds', 240)])
assert_equal(log.error.call_count, 4)
log.error.assert_called_with(
- 'Webhook send error: %s %s %s %s %s' % (
+ 'Webhook send error: %s %s %s %s %s %s' % (
self.wh.type, self.wh.hook_url,
self.wh.app_config.url(),
requests.post.return_value.status_code,
- requests.post.return_value.reason))
+ requests.post.return_value.text,
+ requests.post.return_value.headers))
@patch('allura.webhooks.time', autospec=True)
@patch('allura.webhooks.requests', autospec=True)
@@ -491,11 +497,12 @@ class TestSendWebhookHelper(TestWebhookBase):
log.info.assert_called_once_with('Retrying webhook in: %s', [])
assert_equal(log.error.call_count, 1)
log.error.assert_called_with(
- 'Webhook send error: %s %s %s %s %s' % (
+ 'Webhook send error: %s %s %s %s %s %s' % (
self.wh.type, self.wh.hook_url,
self.wh.app_config.url(),
requests.post.return_value.status_code,
- requests.post.return_value.reason))
+ requests.post.return_value.text,
+ requests.post.return_value.headers))
class TestRepoPushWebhookSender(TestWebhookBase):
http://git-wip-us.apache.org/repos/asf/allura/blob/1b1dcfdf/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index ebfb3d0..0f9b54d 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -256,11 +256,12 @@ class SendWebhookHelper(object):
self.webhook.type,
self.webhook.hook_url,
self.webhook.app_config.url())
- if response:
- message = '{} {} {}'.format(
+ if response is not None:
+ message = '{} {} {} {}'.format(
message,
response.status_code,
- response.reason)
+ response.text,
+ response.headers)
return message
def send(self):
[24/37] allura git commit: [#4542] ticket:715 Better error handling &
retries
Posted by je...@apache.org.
[#4542] ticket:715 Better error handling & retries
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/072654cf
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/072654cf
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/072654cf
Branch: refs/heads/ib/4542
Commit: 072654cfe94503a669d004a8094ad271572c80be
Parents: e7c60a3
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 2 15:45:42 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:39 2015 +0000
----------------------------------------------------------------------
Allura/allura/tests/test_webhooks.py | 106 +++++++++++++++++++++++-------
Allura/allura/webhooks.py | 99 +++++++++++++++++++++-------
Allura/development.ini | 5 ++
3 files changed, 163 insertions(+), 47 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/072654cf/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 791128e..ab82cb8 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -3,7 +3,7 @@ import hmac
import hashlib
import datetime as dt
-from mock import Mock, patch
+from mock import Mock, patch, call
from nose.tools import (
assert_raises,
assert_equal,
@@ -17,13 +17,13 @@ from tg import config
from allura import model as M
from allura.lib import helpers as h
-from allura.lib.utils import DateJSONEncoder
from allura.webhooks import (
MingOneOf,
WebhookValidator,
WebhookController,
send_webhook,
RepoPushWebhookSender,
+ SendWebhookHelper,
)
from allura.tests import decorators as td
from alluratest.controller import setup_basic_test, TestController
@@ -400,45 +400,103 @@ class TestWebhookController(TestController):
return [link(tds[0]), link(tds[1]), text(tds[2]), delete_btn(tds[3])]
-class TestTasks(TestWebhookBase):
+class TestSendWebhookHelper(TestWebhookBase):
- @patch('allura.webhooks.requests', autospec=True)
- @patch('allura.webhooks.log', autospec=True)
- def test_send_webhook(self, log, requests):
- requests.post.return_value = Mock(status_code=200)
- payload = {'some': ['data']}
- json_payload = json.dumps(payload, cls=DateJSONEncoder)
- send_webhook(self.wh._id, payload)
+ def setUp(self, *args, **kw):
+ super(TestSendWebhookHelper, self).setUp(*args, **kw)
+ self.payload = {'some': ['data', 23]}
+ self.h = SendWebhookHelper(self.wh, self.payload)
+
+ def test_timeout(self):
+ assert_equal(self.h.timeout, 30)
+ with h.push_config(config, **{'webhook.timeout': 10}):
+ assert_equal(self.h.timeout, 10)
+
+ def test_retries(self):
+ assert_equal(self.h.retries, [60, 120, 240])
+ with h.push_config(config, **{'webhook.retry': '1 2 3 4 5 6'}):
+ assert_equal(self.h.retries, [1, 2, 3, 4, 5, 6])
+
+ def test_sign(self):
+ json_payload = json.dumps(self.payload)
signature = hmac.new(
self.wh.secret.encode('utf-8'),
json_payload.encode('utf-8'),
hashlib.sha1)
signature = 'sha1=' + signature.hexdigest()
+ assert_equal(self.h.sign(json_payload), signature)
+
+ def test_log_msg(self):
+ assert_equal(
+ self.h.log_msg('OK'),
+ 'OK: repo-push http://httpbin.org/post /adobe/adobe-1/src/')
+ assert_equal(
+ self.h.log_msg('Error', response=Mock(status_code=500, reason='that is why')),
+ 'Error: repo-push http://httpbin.org/post /adobe/adobe-1/src/ 500 that is why')
+
+ @patch('allura.webhooks.SendWebhookHelper', autospec=True)
+ def test_send_webhook_task(self, swh):
+ send_webhook(self.wh._id, self.payload)
+ swh.assert_called_once_with(self.wh, self.payload)
+
+ @patch('allura.webhooks.requests', autospec=True)
+ @patch('allura.webhooks.log', autospec=True)
+ def test_send(self, log, requests):
+ requests.post.return_value = Mock(status_code=200)
+ self.h.sign = Mock(return_value='sha1=abc')
+ self.h.send()
headers = {'content-type': 'application/json',
'User-Agent': 'Allura Webhook (https://allura.apache.org/)',
- 'X-Allura-Signature': signature}
+ 'X-Allura-Signature': 'sha1=abc'}
requests.post.assert_called_once_with(
self.wh.hook_url,
- data=json_payload,
+ data=json.dumps(self.payload),
headers=headers,
timeout=30)
log.info.assert_called_once_with(
- 'Webhook successfully sent: %s %s %s',
- self.wh.type, self.wh.hook_url, self.wh.app_config.url())
+ 'Webhook successfully sent: %s %s %s' % (
+ self.wh.type, self.wh.hook_url, self.wh.app_config.url()))
+ @patch('allura.webhooks.time', autospec=True)
@patch('allura.webhooks.requests', autospec=True)
@patch('allura.webhooks.log', autospec=True)
- def test_send_webhook_error(self, log, requests):
+ def test_send_error_response_status(self, log, requests, time):
requests.post.return_value = Mock(status_code=500)
- send_webhook(self.wh._id, {})
- assert_equal(requests.post.call_count, 1)
- assert_equal(log.info.call_count, 0)
- log.error.assert_called_once_with(
- 'Webhook send error: %s %s %s %s %s',
- self.wh.type, self.wh.hook_url,
- self.wh.app_config.url(),
- requests.post.return_value.status_code,
- requests.post.return_value.reason)
+ self.h.send()
+ assert_equal(requests.post.call_count, 4) # initial call + 3 retries
+ assert_equal(time.sleep.call_args_list,
+ [call(60), call(120), call(240)])
+ assert_equal(log.info.call_args_list, [
+ call('Retrying webhook in: %s', [60, 120, 240]),
+ call('Retrying webhook in %s seconds', 60),
+ call('Retrying webhook in %s seconds', 120),
+ call('Retrying webhook in %s seconds', 240)])
+ assert_equal(log.error.call_count, 4)
+ log.error.assert_called_with(
+ 'Webhook send error: %s %s %s %s %s' % (
+ self.wh.type, self.wh.hook_url,
+ self.wh.app_config.url(),
+ requests.post.return_value.status_code,
+ requests.post.return_value.reason))
+
+ @patch('allura.webhooks.time', autospec=True)
+ @patch('allura.webhooks.requests', autospec=True)
+ @patch('allura.webhooks.log', autospec=True)
+ def test_send_error_no_retries(self, log, requests, time):
+ requests.post.return_value = Mock(status_code=500)
+ with h.push_config(config, **{'webhook.retry': ''}):
+ self.h.send()
+ assert_equal(requests.post.call_count, 1)
+ assert_equal(time.call_count, 0)
+ log.info.assert_called_once_with('Retrying webhook in: %s', [])
+ assert_equal(log.error.call_count, 1)
+ log.error.assert_called_with(
+ 'Webhook send error: %s %s %s %s %s' % (
+ self.wh.type, self.wh.hook_url,
+ self.wh.app_config.url(),
+ requests.post.return_value.status_code,
+ requests.post.return_value.reason))
+
class TestRepoPushWebhookSender(TestWebhookBase):
http://git-wip-us.apache.org/repos/asf/allura/blob/072654cf/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 7df8a42..ebfb3d0 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -19,6 +19,9 @@ import logging
import json
import hmac
import hashlib
+import time
+import socket
+import ssl
import requests
from bson import ObjectId
@@ -29,6 +32,7 @@ from formencode import validators as fev, schema, Invalid
from ming.odm import session
from webob import exc
from pymongo.errors import DuplicateKeyError
+from paste.deploy.converters import asint, aslist
from allura.controllers import BaseController
from allura.lib import helpers as h
@@ -224,32 +228,81 @@ class WebhookController(BaseController):
'form': form}
+class SendWebhookHelper(object):
+
+ def __init__(self, webhook, payload):
+ self.webhook = webhook
+ self.payload = payload
+
+ @property
+ def timeout(self):
+ return asint(config.get('webhook.timeout', 30))
+
+ @property
+ def retries(self):
+ t = aslist(config.get('webhook.retry', [60, 120, 240]))
+ return map(int, t)
+
+ def sign(self, json_payload):
+ signature = hmac.new(
+ self.webhook.secret.encode('utf-8'),
+ json_payload.encode('utf-8'),
+ hashlib.sha1)
+ return 'sha1=' + signature.hexdigest()
+
+ def log_msg(self, msg, response=None):
+ message = '{}: {} {} {}'.format(
+ msg,
+ self.webhook.type,
+ self.webhook.hook_url,
+ self.webhook.app_config.url())
+ if response:
+ message = '{} {} {}'.format(
+ message,
+ response.status_code,
+ response.reason)
+ return message
+
+ def send(self):
+ json_payload = json.dumps(self.payload, cls=DateJSONEncoder)
+ signature = self.sign(json_payload)
+ headers = {'content-type': 'application/json',
+ 'User-Agent': 'Allura Webhook (https://allura.apache.org/)',
+ 'X-Allura-Signature': signature}
+ ok = self._send(self.webhook.hook_url, json_payload, headers)
+ if not ok:
+ log.info('Retrying webhook in: %s', self.retries)
+ for t in self.retries:
+ log.info('Retrying webhook in %s seconds', t)
+ time.sleep(t)
+ ok = self._send(self.webhook.hook_url, json_payload, headers)
+ if ok:
+ return
+
+ def _send(self, url, data, headers):
+ try:
+ r = requests.post(
+ url,
+ data=data,
+ headers=headers,
+ timeout=self.timeout)
+ except (requests.exceptions.RequestException,
+ socket.timeout,
+ ssl.SSLError):
+ log.exception(self.log_msg('Webhook send error'))
+ return False
+ if r.status_code >= 200 and r.status_code < 300:
+ log.info(self.log_msg('Webhook successfully sent'))
+ return True
+ else:
+ log.error(self.log_msg('Webhook send error', response=r))
+ return False
+
+
@task()
def send_webhook(webhook_id, payload):
webhook = M.Webhook.query.get(_id=webhook_id)
- url = webhook.hook_url
- json_payload = json.dumps(payload, cls=DateJSONEncoder)
- signature = hmac.new(
- webhook.secret.encode('utf-8'),
- json_payload.encode('utf-8'),
- hashlib.sha1)
- signature = 'sha1=' + signature.hexdigest()
- headers = {'content-type': 'application/json',
- 'User-Agent': 'Allura Webhook (https://allura.apache.org/)',
- 'X-Allura-Signature': signature}
- # TODO: catch
- # TODO: configurable timeout
- r = requests.post(url, data=json_payload, headers=headers, timeout=30)
- if r.status_code >= 200 and r.status_code < 300:
- log.info('Webhook successfully sent: %s %s %s',
- webhook.type, webhook.hook_url, webhook.app_config.url())
- else:
- # TODO: retry
- # TODO: configurable retries
- log.error('Webhook send error: %s %s %s %s %s',
- webhook.type, webhook.hook_url,
- webhook.app_config.url(),
- r.status_code, r.reason)
+ SendWebhookHelper(webhook, payload).send()
class WebhookSender(object):
http://git-wip-us.apache.org/repos/asf/allura/blob/072654cf/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index f08ffb7..68b6267 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -129,6 +129,11 @@ user_prefs_storage.ldap.fields.display_name = cn
# Limit the number of emails a user can claim.
user_prefs.maximum_claimed_emails = 20
+# webhook.timeout = 30 # seconds, default = 30
+
+# List of pauses between retries, if hook fails (in seconds)
+# webhook.retry = 60 120 240
+
# Limit rate of webhook firing (in seconds, default = 30)
# Option format: webhook.<hook type>.limit,
# all '-' in hook type must be changed to '_'
[34/37] allura git commit: [#4542] ticket:728 Fix tests
Posted by je...@apache.org.
[#4542] ticket:728 Fix tests
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/a433fa97
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/a433fa97
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/a433fa97
Branch: refs/heads/ib/4542
Commit: a433fa9778b8aaea2efada1f8df23f20291f09cf
Parents: fea9a04
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 13 17:35:24 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:42 2015 +0000
----------------------------------------------------------------------
Allura/allura/tests/test_webhooks.py | 20 +++++++++++---------
.../forgegit/tests/model/test_repository.py | 16 ++++++----------
.../forgesvn/tests/model/test_repository.py | 17 ++++++-----------
3 files changed, 23 insertions(+), 30 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/a433fa97/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 3d51f5c..629b322 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -3,7 +3,7 @@ import hmac
import hashlib
import datetime as dt
-from mock import Mock, patch, call
+from mock import Mock, MagicMock, patch, call
from nose.tools import (
assert_raises,
assert_equal,
@@ -480,7 +480,7 @@ class TestRepoPushWebhookSender(TestWebhookBase):
sender = RepoPushWebhookSender()
sender.get_payload = Mock()
with h.push_config(c, app=self.git):
- sender.send(arg1=1, arg2=2)
+ sender.send(dict(arg1=1, arg2=2))
send_webhook.post.assert_called_once_with(
self.wh._id,
sender.get_payload.return_value)
@@ -492,7 +492,7 @@ class TestRepoPushWebhookSender(TestWebhookBase):
sender.get_payload = Mock()
self.wh.enforce_limit = Mock(return_value=False)
with h.push_config(c, app=self.git):
- sender.send(arg1=1, arg2=2)
+ sender.send(dict(arg1=1, arg2=2))
assert_equal(send_webhook.post.call_count, 0)
log.warn.assert_called_once_with(
'Webhook fires too often: %s. Skipping', self.wh)
@@ -503,19 +503,21 @@ class TestRepoPushWebhookSender(TestWebhookBase):
session(self.wh).flush(self.wh)
sender = RepoPushWebhookSender()
with h.push_config(c, app=self.git):
- sender.send(arg1=1, arg2=2)
+ sender.send(dict(arg1=1, arg2=2))
assert_equal(send_webhook.post.call_count, 0)
def test_get_payload(self):
sender = RepoPushWebhookSender()
- _ci = list(range(1, 4))
- _se = [Mock(webhook_info=str(x)) for x in _ci]
- with patch.object(self.git.repo, 'commit', autospec=True, side_effect=_se):
+ _ci = lambda x: MagicMock(webhook_info={'id': str(x)}, parent_ids=['0'])
+ with patch.object(self.git.repo, 'commit', new=_ci):
with h.push_config(c, app=self.git):
- result = sender.get_payload(commit_ids=_ci)
+ result = sender.get_payload(commit_ids=['1', '2', '3'], ref='ref')
expected_result = {
'size': 3,
- 'commits': ['1', '2', '3'],
+ 'commits': [{'id': '1'}, {'id': '2'}, {'id': '3'}],
+ 'ref': u'ref',
+ 'after': u'1',
+ 'before': u'0',
'repository': {
'full_name': u'/adobe/adobe-1/src/',
'name': u'Git',
http://git-wip-us.apache.org/repos/asf/allura/blob/a433fa97/ForgeGit/forgegit/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index b67a7ae..231e479 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -31,6 +31,7 @@ from ming.base import Object
from ming.orm import ThreadLocalORMSession, session
from nose.tools import assert_equal
from testfixtures import TempDirectory
+from datadiff.tools import assert_equals
from alluratest.controller import setup_basic_test, setup_global_objects
from allura.lib import helpers as h
@@ -546,9 +547,12 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
sender = RepoPushWebhookSender()
cids = list(self.repo.all_commit_ids())[:2]
- payload = sender.get_payload(commit_ids=cids)
+ payload = sender.get_payload(commit_ids=cids, ref='refs/heads/zz')
expected_payload = {
'size': 2,
+ 'ref': u'refs/heads/zz',
+ 'after': u'5c47243c8e424136fd5cdd18cd94d34c66d1955c',
+ 'before': u'df30427c488aeab84b2352bdf88a3b19223f9d7a',
'commits': [{
'id': u'5c47243c8e424136fd5cdd18cd94d34c66d1955c',
'url': u'http://localhost/p/test/src-git/ci/5c47243c8e424136fd5cdd18cd94d34c66d1955c/',
@@ -586,15 +590,7 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
'url': u'http://localhost/p/test/src-git/',
},
}
-
- def _diff(one, two):
- from difflib import Differ
- from pprint import pformat
- one, two = pformat(one), pformat(two)
- diff = Differ().compare(one.splitlines(), two.splitlines())
- print '\n'.join(diff)
-
- assert payload == expected_payload, _diff(expected_payload, payload)
+ assert_equals(payload, expected_payload)
class TestGitImplementation(unittest.TestCase):
http://git-wip-us.apache.org/repos/asf/allura/blob/a433fa97/ForgeSVN/forgesvn/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeSVN/forgesvn/tests/model/test_repository.py b/ForgeSVN/forgesvn/tests/model/test_repository.py
index 030931a..eb952e1 100644
--- a/ForgeSVN/forgesvn/tests/model/test_repository.py
+++ b/ForgeSVN/forgesvn/tests/model/test_repository.py
@@ -28,6 +28,7 @@ from collections import defaultdict
from pylons import tmpl_context as c, app_globals as g
import mock
from nose.tools import assert_equal
+from datadiff.tools import assert_equals
import tg
import ming
from ming.base import Object
@@ -584,8 +585,10 @@ class TestSVNRepo(unittest.TestCase, RepoImplTestBase):
payload = sender.get_payload(commit_ids=cids)
expected_payload = {
'size': 2,
+ 'after': 'r6',
+ 'before': 'r4',
'commits': [{
- 'id': u'{}:6'.format(self.repo._id),
+ 'id': u'r6',
'url': u'http://localhost/p/test/src/6/',
'timestamp': datetime(2013, 11, 8, 13, 38, 11, 152000),
'message': u'',
@@ -600,7 +603,7 @@ class TestSVNRepo(unittest.TestCase, RepoImplTestBase):
'modified': [],
'copied': []
}, {
- 'id': u'{}:5'.format(self.repo._id),
+ 'id': u'r5',
'url': u'http://localhost/p/test/src/5/',
'timestamp': datetime(2010, 11, 18, 20, 14, 21, 515000),
'message': u'Copied a => b',
@@ -621,15 +624,7 @@ class TestSVNRepo(unittest.TestCase, RepoImplTestBase):
'url': u'http://localhost/p/test/src/',
},
}
-
- def _diff(one, two):
- from difflib import Differ
- from pprint import pformat
- one, two = pformat(one), pformat(two)
- diff = Differ().compare(one.splitlines(), two.splitlines())
- print '\n'.join(diff)
-
- assert payload == expected_payload, _diff(expected_payload, payload)
+ assert_equals(payload, expected_payload)
class TestSVNRev(unittest.TestCase):
[26/37] allura git commit: [#4542] ticket:723 Change repo-push hook
payload
Posted by je...@apache.org.
[#4542] ticket:723 Change repo-push hook payload
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/13a8a607
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/13a8a607
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/13a8a607
Branch: refs/heads/ib/4542
Commit: 13a8a607ac69ef602ed0ef9992af310a58f92051
Parents: 1b1dcfd
Author: Igor Bondarenko <je...@gmail.com>
Authored: Tue Feb 10 13:55:28 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:39 2015 +0000
----------------------------------------------------------------------
Allura/allura/model/repository.py | 35 +++++++-
Allura/allura/tests/test_webhooks.py | 12 ++-
Allura/allura/webhooks.py | 11 ++-
.../forgegit/tests/model/test_repository.py | 88 ++++++++++++++++----
.../forgesvn/tests/model/test_repository.py | 73 ++++++++++++----
5 files changed, 174 insertions(+), 45 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/13a8a607/Allura/allura/model/repository.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/repository.py b/Allura/allura/model/repository.py
index 5650d35..0030aa5 100644
--- a/Allura/allura/model/repository.py
+++ b/Allura/allura/model/repository.py
@@ -939,14 +939,22 @@ class Commit(RepoObject, ActivityObject):
self.repo = repo
@LazyProperty
+ def authored_user(self):
+ return User.by_email_address(self.authored.email)
+
+ @LazyProperty
+ def committed_user(self):
+ return User.by_email_address(self.committed.email)
+
+ @LazyProperty
def author_url(self):
- u = User.by_email_address(self.authored.email)
+ u = self.authored_user
if u:
return u.url()
@LazyProperty
def committer_url(self):
- u = User.by_email_address(self.committed.email)
+ u = self.committed_user
if u:
return u.url()
@@ -1222,6 +1230,29 @@ class Commit(RepoObject, ActivityObject):
summary=self.summary
)
+ @LazyProperty
+ def webhook_info(self):
+ return {
+ 'id': self._id,
+ 'url': h.absurl(self.url()),
+ 'timestamp': self.authored.date,
+ 'message': self.summary,
+ 'author': {
+ 'name': self.authored.name,
+ 'email': self.authored.email,
+ 'username': self.authored_user.username if self.authored_user else u'',
+ },
+ 'committer': {
+ 'name': self.committed.name,
+ 'email': self.committed.email,
+ 'username': self.committed_user.username if self.committed_user else u'',
+ },
+ 'added': self.diffs.added,
+ 'removed': self.diffs.removed,
+ 'modified': self.diffs.changed,
+ 'copied': self.diffs.copied,
+ }
+
class Tree(RepoObject):
# Ephemeral attrs
http://git-wip-us.apache.org/repos/asf/allura/blob/13a8a607/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index 1879beb..5362da7 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -541,14 +541,18 @@ class TestRepoPushWebhookSender(TestWebhookBase):
def test_get_payload(self):
sender = RepoPushWebhookSender()
_ci = list(range(1, 4))
- _se = [Mock(info=str(x)) for x in _ci]
+ _se = [Mock(webhook_info=str(x)) for x in _ci]
with patch.object(self.git.repo, 'commit', autospec=True, side_effect=_se):
with h.push_config(c, app=self.git):
result = sender.get_payload(commit_ids=_ci)
expected_result = {
- 'url': 'http://localhost/adobe/adobe-1/src/',
- 'count': 3,
- 'revisions': ['1', '2', '3'],
+ 'size': 3,
+ 'commits': ['1', '2', '3'],
+ 'repository': {
+ 'full_name': u'/adobe/adobe-1/src/',
+ 'name': u'Git',
+ 'url': u'http://localhost/adobe/adobe-1/src/',
+ },
}
assert_equal(result, expected_result)
http://git-wip-us.apache.org/repos/asf/allura/blob/13a8a607/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 0f9b54d..17d7ebf 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -356,9 +356,14 @@ class RepoPushWebhookSender(WebhookSender):
def get_payload(self, commit_ids, **kw):
app = kw.get('app') or c.app
+ commits = [app.repo.commit(ci).webhook_info for ci in commit_ids]
payload = {
- 'url': h.absurl(app.url),
- 'count': len(commit_ids),
- 'revisions': [app.repo.commit(ci).info for ci in commit_ids],
+ 'size': len(commits),
+ 'commits': commits,
+ 'repository': {
+ 'name': app.config.options.mount_label,
+ 'full_name': app.url,
+ 'url': h.absurl(app.url),
+ },
}
return payload
http://git-wip-us.apache.org/repos/asf/allura/blob/13a8a607/ForgeGit/forgegit/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/model/test_repository.py b/ForgeGit/forgegit/tests/model/test_repository.py
index 5e86ab0..b67a7ae 100644
--- a/ForgeGit/forgegit/tests/model/test_repository.py
+++ b/ForgeGit/forgegit/tests/model/test_repository.py
@@ -113,6 +113,20 @@ class TestNewGit(unittest.TestCase):
'/p/test/src-git/ci/'
'1e146e67985dcd71c74de79613719bef7bddca4a/')
+ assert_equal(self.rev.authored_user, None)
+ assert_equal(self.rev.committed_user, None)
+ user = M.User.upsert('rick')
+ email = user.claim_address('rcopeland@geek.net')
+ email.confirmed = True
+ session(email).flush(email)
+ rev = self.repo.commit(self.rev._id) # to update cached values of LazyProperty
+ assert_equal(rev.authored_user, user)
+ assert_equal(rev.committed_user, user)
+ assert_equal(
+ sorted(rev.webhook_info.keys()),
+ sorted(['id', 'url', 'timestamp', 'message', 'author',
+ 'committer', 'added', 'removed', 'modified', 'copied']))
+
class TestGitRepo(unittest.TestCase, RepoImplTestBase):
@@ -521,28 +535,66 @@ class TestGitRepo(unittest.TestCase, RepoImplTestBase):
'https://user@foo.com/')
def test_webhook_payload(self):
+ user = M.User.upsert('cory')
+ email = user.claim_address('cjohns@slashdotmedia.com')
+ email.confirmed = True
+ session(email).flush(email)
+ user = M.User.upsert('rick')
+ email = user.claim_address('rcopeland@geek.net')
+ email.confirmed = True
+ session(email).flush(email)
+
sender = RepoPushWebhookSender()
cids = list(self.repo.all_commit_ids())[:2]
payload = sender.get_payload(commit_ids=cids)
expected_payload = {
- 'url': 'http://localhost/p/test/src-git/',
- 'count': 2,
- 'revisions': [
- {'author': u'Cory Johns',
- 'author_email': u'cjohns@slashdotmedia.com',
- 'author_url': None,
- 'date': datetime.datetime(2013, 3, 28, 18, 54, 16),
- 'id': u'5c47243c8e424136fd5cdd18cd94d34c66d1955c',
- 'shortlink': u'[5c4724]',
- 'summary': u'Not repo root'},
- {'author': u'Rick Copeland',
- 'author_email': u'rcopeland@geek.net',
- 'author_url': None,
- 'date': datetime.datetime(2010, 10, 7, 18, 44, 11),
- 'id': u'1e146e67985dcd71c74de79613719bef7bddca4a',
- 'shortlink': u'[1e146e]',
- 'summary': u'Change README'}]}
- assert_equal(payload, expected_payload)
+ 'size': 2,
+ 'commits': [{
+ 'id': u'5c47243c8e424136fd5cdd18cd94d34c66d1955c',
+ 'url': u'http://localhost/p/test/src-git/ci/5c47243c8e424136fd5cdd18cd94d34c66d1955c/',
+ 'timestamp': datetime.datetime(2013, 3, 28, 18, 54, 16),
+ 'message': u'Not repo root',
+ 'author': {'name': u'Cory Johns',
+ 'email': u'cjohns@slashdotmedia.com',
+ 'username': 'cory'},
+ 'committer': {'name': u'Cory Johns',
+ 'email': u'cjohns@slashdotmedia.com',
+ 'username': 'cory'},
+ 'added': [u'bad'],
+ 'removed': [],
+ 'modified': [],
+ 'copied': []
+ }, {
+ 'id': u'1e146e67985dcd71c74de79613719bef7bddca4a',
+ 'url': u'http://localhost/p/test/src-git/ci/1e146e67985dcd71c74de79613719bef7bddca4a/',
+ 'timestamp': datetime.datetime(2010, 10, 7, 18, 44, 11),
+ 'message': u'Change README',
+ 'author': {'name': u'Rick Copeland',
+ 'email': u'rcopeland@geek.net',
+ 'username': 'rick'},
+ 'committer': {'name': u'Rick Copeland',
+ 'email': u'rcopeland@geek.net',
+ 'username': 'rick'},
+ 'added': [],
+ 'removed': [],
+ 'modified': [u'README'],
+ 'copied': []
+ }],
+ 'repository': {
+ 'name': u'Git',
+ 'full_name': u'/p/test/src-git/',
+ 'url': u'http://localhost/p/test/src-git/',
+ },
+ }
+
+ def _diff(one, two):
+ from difflib import Differ
+ from pprint import pformat
+ one, two = pformat(one), pformat(two)
+ diff = Differ().compare(one.splitlines(), two.splitlines())
+ print '\n'.join(diff)
+
+ assert payload == expected_payload, _diff(expected_payload, payload)
class TestGitImplementation(unittest.TestCase):
http://git-wip-us.apache.org/repos/asf/allura/blob/13a8a607/ForgeSVN/forgesvn/tests/model/test_repository.py
----------------------------------------------------------------------
diff --git a/ForgeSVN/forgesvn/tests/model/test_repository.py b/ForgeSVN/forgesvn/tests/model/test_repository.py
index 5267614..030931a 100644
--- a/ForgeSVN/forgesvn/tests/model/test_repository.py
+++ b/ForgeSVN/forgesvn/tests/model/test_repository.py
@@ -1,3 +1,4 @@
+# coding: utf-8
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
@@ -98,6 +99,13 @@ class TestNewRepo(unittest.TestCase):
assert self.rev.tree['a']['b']['c'].ls() == []
self.assertRaises(KeyError, lambda: self.rev.tree['a']['b']['d'])
+ assert_equal(self.rev.authored_user, None)
+ assert_equal(self.rev.committed_user, None)
+ assert_equal(
+ sorted(self.rev.webhook_info.keys()),
+ sorted(['id', 'url', 'timestamp', 'message', 'author',
+ 'committer', 'added', 'removed', 'modified', 'copied']))
+
class TestSVNRepo(unittest.TestCase, RepoImplTestBase):
@@ -575,24 +583,53 @@ class TestSVNRepo(unittest.TestCase, RepoImplTestBase):
cids = list(self.repo.all_commit_ids())[:2]
payload = sender.get_payload(commit_ids=cids)
expected_payload = {
- 'url': 'http://localhost/p/test/src/',
- 'count': 2,
- 'revisions': [
- {'author': u'coldmind',
- 'author_email': u'',
- 'author_url': None,
- 'date': datetime(2013, 11, 8, 13, 38, 11, 152000),
- 'id': u'{}:6'.format(self.repo._id),
- 'shortlink': '[r6]',
- 'summary': ''},
- {'author': u'rick446',
- 'author_email': u'',
- 'author_url': None,
- 'date': datetime(2010, 11, 18, 20, 14, 21, 515000),
- 'id': u'{}:5'.format(self.repo._id),
- 'shortlink': '[r5]',
- 'summary': u'Copied a => b'}]}
- assert_equal(payload, expected_payload)
+ 'size': 2,
+ 'commits': [{
+ 'id': u'{}:6'.format(self.repo._id),
+ 'url': u'http://localhost/p/test/src/6/',
+ 'timestamp': datetime(2013, 11, 8, 13, 38, 11, 152000),
+ 'message': u'',
+ 'author': {'name': u'coldmind',
+ 'email': u'',
+ 'username': u''},
+ 'committer': {'name': u'coldmind',
+ 'email': u'',
+ 'username': u''},
+ 'added': [u'/ЗРЯЧИЙ_ТА_ПОБАЧИТЬ'],
+ 'removed': [],
+ 'modified': [],
+ 'copied': []
+ }, {
+ 'id': u'{}:5'.format(self.repo._id),
+ 'url': u'http://localhost/p/test/src/5/',
+ 'timestamp': datetime(2010, 11, 18, 20, 14, 21, 515000),
+ 'message': u'Copied a => b',
+ 'author': {'name': u'rick446',
+ 'email': u'',
+ 'username': u''},
+ 'committer': {'name': u'rick446',
+ 'email': u'',
+ 'username': u''},
+ 'added': [u'/b'],
+ 'removed': [],
+ 'modified': [],
+ 'copied': []
+ }],
+ 'repository': {
+ 'name': u'SVN',
+ 'full_name': u'/p/test/src/',
+ 'url': u'http://localhost/p/test/src/',
+ },
+ }
+
+ def _diff(one, two):
+ from difflib import Differ
+ from pprint import pformat
+ one, two = pformat(one), pformat(two)
+ diff = Differ().compare(one.splitlines(), two.splitlines())
+ print '\n'.join(diff)
+
+ assert payload == expected_payload, _diff(expected_payload, payload)
class TestSVNRev(unittest.TestCase):
[33/37] allura git commit: [#4542] ticket:728 Convert svn ids
Posted by je...@apache.org.
[#4542] ticket:728 Convert svn ids
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/fea9a040
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/fea9a040
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/fea9a040
Branch: refs/heads/ib/4542
Commit: fea9a04054614af5e46fffe51e7bfc7f4eaf69b4
Parents: 5e5a19d
Author: Igor Bondarenko <je...@gmail.com>
Authored: Fri Feb 13 17:01:08 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:42 2015 +0000
----------------------------------------------------------------------
Allura/allura/webhooks.py | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/fea9a040/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index f727b63..03a909d 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -334,17 +334,24 @@ class RepoPushWebhookSender(WebhookSender):
if len(parents) > 0:
# Merge commit will have multiple parents. As far as I can tell
# the last one will be the branch head before merge
- return parents[-1]
+ return self._convert_id(parents[-1])
return u''
def _after(self, repo, commit_ids):
if len(commit_ids) > 0:
- return commit_ids[0]
+ return self._convert_id(commit_ids[0])
return u''
+ def _convert_id(self, _id):
+ if ':' in _id:
+ _id = u'r' + _id.rsplit(':', 1)[1]
+ return _id
+
def get_payload(self, commit_ids, **kw):
app = kw.get('app') or c.app
commits = [app.repo.commit(ci).webhook_info for ci in commit_ids]
+ for ci in commits:
+ ci['id'] = self._convert_id(ci['id'])
before = self._before(app.repo, commit_ids)
after = self._after(app.repo, commit_ids)
payload = {
[22/37] allura git commit: [#4542] ticket:715 Add rate limits
Posted by je...@apache.org.
[#4542] ticket:715 Add rate limits
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/5c82af91
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/5c82af91
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/5c82af91
Branch: refs/heads/ib/4542
Commit: 5c82af9102913513a368f3c9db3adf5397e5f42e
Parents: e7ace57
Author: Igor Bondarenko <je...@gmail.com>
Authored: Mon Feb 2 12:01:13 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:38 2015 +0000
----------------------------------------------------------------------
Allura/allura/model/webhook.py | 23 ++++++++++++++++++-
Allura/allura/tests/test_webhooks.py | 38 +++++++++++++++++++++++++++++++
Allura/allura/webhooks.py | 6 ++++-
Allura/development.ini | 6 +++++
4 files changed, 71 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/5c82af91/Allura/allura/model/webhook.py
----------------------------------------------------------------------
diff --git a/Allura/allura/model/webhook.py b/Allura/allura/model/webhook.py
index 05eb436..09cc7fa 100644
--- a/Allura/allura/model/webhook.py
+++ b/Allura/allura/model/webhook.py
@@ -15,7 +15,12 @@
# specific language governing permissions and limitations
# under the License.
-from ming.odm import FieldProperty
+import datetime as dt
+
+from ming.odm import FieldProperty, session
+from paste.deploy.converters import asint
+from tg import config
+
from allura.model import Artifact
@@ -28,6 +33,7 @@ class Webhook(Artifact):
type = FieldProperty(str)
hook_url = FieldProperty(str)
secret = FieldProperty(str)
+ last_sent = FieldProperty(dt.datetime, if_missing=None)
def url(self):
return '{}{}/{}/{}'.format(
@@ -41,3 +47,18 @@ class Webhook(Artifact):
ac_ids = [ac._id for ac in project.app_configs]
hooks = cls.query.find(dict(type=type, app_config_id={'$in': ac_ids}))
return hooks.all()
+
+ def enforce_limit(self):
+ '''Returns False if limit is reached, otherwise True'''
+ if self.last_sent is None:
+ return True
+ now = dt.datetime.utcnow()
+ config_type = self.type.replace('-', '_')
+ limit = asint(config.get('webhook.%s.limit' % config_type, 30))
+ if (now - self.last_sent) > dt.timedelta(seconds=limit):
+ return True
+ return False
+
+ def update_limit(self):
+ self.last_sent = dt.datetime.utcnow()
+ session(self).flush(self)
http://git-wip-us.apache.org/repos/asf/allura/blob/5c82af91/Allura/allura/tests/test_webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/test_webhooks.py b/Allura/allura/tests/test_webhooks.py
index fa0305f..99e5f91 100644
--- a/Allura/allura/tests/test_webhooks.py
+++ b/Allura/allura/tests/test_webhooks.py
@@ -1,6 +1,7 @@
import json
import hmac
import hashlib
+import datetime as dt
from mock import Mock, patch
from nose.tools import (
@@ -12,6 +13,7 @@ from nose.tools import (
from formencode import Invalid
from ming.odm import session
from pylons import tmpl_context as c
+from tg import config
from allura import model as M
from allura.lib import helpers as h
@@ -431,6 +433,18 @@ class TestRepoPushWebhookSender(TestWebhookBase):
self.wh._id,
sender.get_payload.return_value)
+ @patch('allura.webhooks.log', autospec=True)
+ @patch('allura.webhooks.send_webhook', autospec=True)
+ def test_send_limit_reached(self, send_webhook, log):
+ sender = RepoPushWebhookSender()
+ sender.get_payload = Mock()
+ self.wh.enforce_limit = Mock(return_value=False)
+ with h.push_config(c, app=self.git):
+ sender.send(arg1=1, arg2=2)
+ assert_equal(send_webhook.post.call_count, 0)
+ log.warn.assert_called_once_with(
+ 'Webhook fires too often: %s. Skipping', self.wh)
+
@patch('allura.webhooks.send_webhook', autospec=True)
def test_send_no_configured_webhooks(self, send_webhook):
self.wh.delete()
@@ -467,3 +481,27 @@ class TestModels(TestWebhookBase):
def test_webhook_url(self):
assert_equal(self.wh.url(),
'/adobe/adobe-1/admin/webhooks/repo-push/{}'.format(self.wh._id))
+
+ def test_webhook_enforce_limit(self):
+ self.wh.last_sent = None
+ assert_equal(self.wh.enforce_limit(), True)
+ # default value
+ self.wh.last_sent = dt.datetime.utcnow() - dt.timedelta(seconds=31)
+ assert_equal(self.wh.enforce_limit(), True)
+ self.wh.last_sent = dt.datetime.utcnow() - dt.timedelta(seconds=15)
+ assert_equal(self.wh.enforce_limit(), False)
+ # value from config
+ with h.push_config(config, **{'webhook.repo_push.limit': 100}):
+ self.wh.last_sent = dt.datetime.utcnow() - dt.timedelta(seconds=101)
+ assert_equal(self.wh.enforce_limit(), True)
+ self.wh.last_sent = dt.datetime.utcnow() - dt.timedelta(seconds=35)
+ assert_equal(self.wh.enforce_limit(), False)
+
+ @patch('allura.model.webhook.dt', autospec=True)
+ def test_update_limit(self, dt_mock):
+ _now = dt.datetime(2015, 02, 02, 13, 39)
+ dt_mock.datetime.utcnow.return_value = _now
+ assert_equal(self.wh.last_sent, None)
+ self.wh.update_limit()
+ session(self.wh).expunge(self.wh)
+ assert_equal(M.Webhook.query.get(_id=self.wh._id).last_sent, _now)
http://git-wip-us.apache.org/repos/asf/allura/blob/5c82af91/Allura/allura/webhooks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/webhooks.py b/Allura/allura/webhooks.py
index 2393acd..e13086d 100644
--- a/Allura/allura/webhooks.py
+++ b/Allura/allura/webhooks.py
@@ -272,7 +272,11 @@ class WebhookSender(object):
if webhooks:
payload = self.get_payload(**kw)
for webhook in webhooks:
- send_webhook.post(webhook._id, payload)
+ if webhook.enforce_limit():
+ webhook.update_limit()
+ send_webhook.post(webhook._id, payload)
+ else:
+ log.warn('Webhook fires too often: %s. Skipping', webhook)
class RepoPushWebhookSender(WebhookSender):
http://git-wip-us.apache.org/repos/asf/allura/blob/5c82af91/Allura/development.ini
----------------------------------------------------------------------
diff --git a/Allura/development.ini b/Allura/development.ini
index c6425a8..39a9efa 100644
--- a/Allura/development.ini
+++ b/Allura/development.ini
@@ -129,6 +129,12 @@ user_prefs_storage.ldap.fields.display_name = cn
# Limit the number of emails a user can claim.
user_prefs.maximum_claimed_emails = 20
+# Limit rate of webhook firing (in seconds, default = 30)
+# Option format: webhook.<hook type>.limit,
+# all '-' in hook type must be changed to '_'
+# e.g. for repo-push webhook:
+# webhook.repo_push.limit = 10
+
# Additional fields for admin project/user search
# Note: whitespace after comma is important!
# search.project.additional_search_fields = private, url, title
[32/37] allura git commit: [#4542] ticket:726 Fix git & svn admin
menu tests
Posted by je...@apache.org.
[#4542] ticket:726 Fix git & svn admin menu tests
Project: http://git-wip-us.apache.org/repos/asf/allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/allura/commit/289eed1f
Tree: http://git-wip-us.apache.org/repos/asf/allura/tree/289eed1f
Diff: http://git-wip-us.apache.org/repos/asf/allura/diff/289eed1f
Branch: refs/heads/ib/4542
Commit: 289eed1fa5d62824c67b36d933380328124b8bed
Parents: 1ec6582
Author: Igor Bondarenko <je...@gmail.com>
Authored: Wed Feb 11 16:19:36 2015 +0000
Committer: Igor Bondarenko <je...@gmail.com>
Committed: Mon Feb 16 10:17:41 2015 +0000
----------------------------------------------------------------------
ForgeGit/forgegit/tests/test_git_app.py | 2 +-
ForgeSVN/forgesvn/tests/test_svn_app.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/allura/blob/289eed1f/ForgeGit/forgegit/tests/test_git_app.py
----------------------------------------------------------------------
diff --git a/ForgeGit/forgegit/tests/test_git_app.py b/ForgeGit/forgegit/tests/test_git_app.py
index 41034b1..0566249 100644
--- a/ForgeGit/forgegit/tests/test_git_app.py
+++ b/ForgeGit/forgegit/tests/test_git_app.py
@@ -40,7 +40,7 @@ class TestGitApp(unittest.TestCase):
ThreadLocalORMSession.close_all()
def test_admin_menu(self):
- assert_equals(len(c.app.admin_menu()), 6)
+ assert_equals(len(c.app.admin_menu()), 7)
def test_uninstall(self):
from allura import model as M
http://git-wip-us.apache.org/repos/asf/allura/blob/289eed1f/ForgeSVN/forgesvn/tests/test_svn_app.py
----------------------------------------------------------------------
diff --git a/ForgeSVN/forgesvn/tests/test_svn_app.py b/ForgeSVN/forgesvn/tests/test_svn_app.py
index 7fd8545..6ea576a 100644
--- a/ForgeSVN/forgesvn/tests/test_svn_app.py
+++ b/ForgeSVN/forgesvn/tests/test_svn_app.py
@@ -40,7 +40,7 @@ class TestSVNApp(unittest.TestCase):
ThreadLocalORMSession.close_all()
def test_admin_menu(self):
- assert_equals(len(c.app.admin_menu()), 6)
+ assert_equals(len(c.app.admin_menu()), 7)
assert_equals(c.app.admin_menu()[0].label, 'Checkout URL')
def test_uninstall(self):