diff --git a/pkgs/python-packages.nix b/pkgs/python-packages.nix
--- a/pkgs/python-packages.nix
+++ b/pkgs/python-packages.nix
@@ -831,6 +831,19 @@
license = [ pkgs.lib.licenses.bsdOriginal ];
};
};
+ marshmallow = super.buildPythonPackage {
+ name = "marshmallow-2.8.0";
+ buildInputs = with self; [];
+ doCheck = false;
+ propagatedBuildInputs = with self; [];
+ src = fetchurl {
+ url = "https://pypi.python.org/packages/4f/64/9393d77847d86981c84b88bbea627d30ff71b5ab1402636b366f73737817/marshmallow-2.8.0.tar.gz";
+ md5 = "204513fc123a3d9bdd7b63b9747f02e6";
+ };
+ meta = {
+ license = [ pkgs.lib.licenses.mit ];
+ };
+ };
mccabe = super.buildPythonPackage {
name = "mccabe-0.3";
buildInputs = with self; [];
@@ -1355,7 +1368,7 @@
name = "rhodecode-enterprise-ce-4.3.0";
buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner];
doCheck = true;
- propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery colander decorator docutils gunicorn infrae.cache ipython iso8601 kombu msgpack-python packaging psycopg2 py-gfm pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson waitress zope.cachedescriptors dogpile.cache dogpile.core psutil py-bcrypt];
+ propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery colander decorator docutils gunicorn infrae.cache ipython iso8601 kombu marshmallow msgpack-python packaging psycopg2 py-gfm pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson waitress zope.cachedescriptors dogpile.cache dogpile.core psutil py-bcrypt];
src = ./.;
meta = {
license = [ { fullName = "AGPLv3, and Commercial License"; } ];
@@ -1637,5 +1650,5 @@
### Test requirements
-
+
}
diff --git a/requirements.txt b/requirements.txt
--- a/requirements.txt
+++ b/requirements.txt
@@ -84,6 +84,7 @@ iso8601==0.1.11
itsdangerous==0.24
kombu==1.5.1
lxml==3.4.4
+marshmallow==2.8.0
mccabe==0.3
meld3==1.0.2
mock==1.0.1
diff --git a/rhodecode/api/tests/test_update_repo.py b/rhodecode/api/tests/test_update_repo.py
--- a/rhodecode/api/tests/test_update_repo.py
+++ b/rhodecode/api/tests/test_update_repo.py
@@ -47,9 +47,14 @@ class TestApiUpdateRepo(object):
({'enable_statistics': True}, SAME_AS_UPDATES),
({'enable_locking': True}, SAME_AS_UPDATES),
({'enable_downloads': True}, SAME_AS_UPDATES),
- ({'name': 'new_repo_name'}, {'repo_name': 'new_repo_name'}),
- ({'group': 'test_group_for_update'},
- {'repo_name': 'test_group_for_update/%s' % UPDATE_REPO_NAME}),
+ ({'name': 'new_repo_name'}, {
+ 'repo_name': 'new_repo_name',
+ 'url': 'http://test.example.com:80/new_repo_name',
+ }),
+ ({'group': 'test_group_for_update'}, {
+ 'repo_name': 'test_group_for_update/%s' % UPDATE_REPO_NAME,
+ 'url': 'http://test.example.com:80/test_group_for_update/%s' % UPDATE_REPO_NAME
+ }),
])
def test_api_update_repo(self, updates, expected, backend):
repo_name = UPDATE_REPO_NAME
diff --git a/rhodecode/controllers/pullrequests.py b/rhodecode/controllers/pullrequests.py
--- a/rhodecode/controllers/pullrequests.py
+++ b/rhodecode/controllers/pullrequests.py
@@ -454,6 +454,7 @@ class PullrequestsController(BaseRepoCon
h.flash(_('Successfully opened new pull request'),
category='success')
except Exception as e:
+ raise
msg = _('Error occurred during sending pull request')
log.exception(msg)
h.flash(msg, category='error')
diff --git a/rhodecode/events/__init__.py b/rhodecode/events/__init__.py
--- a/rhodecode/events/__init__.py
+++ b/rhodecode/events/__init__.py
@@ -19,12 +19,6 @@
from pyramid.threadlocal import get_current_registry
-class RhodecodeEvent(object):
- """
- Base event class for all Rhodecode events
- """
-
-
def trigger(event):
"""
Helper method to send an event. This wraps the pyramid logic to send an
@@ -37,6 +31,8 @@ def trigger(event):
registry.notify(event)
+from rhodecode.events.base import RhodecodeEvent
+
from rhodecode.events.user import (
UserPreCreate,
UserPreUpdate,
@@ -44,6 +40,7 @@ from rhodecode.events.user import (
)
from rhodecode.events.repo import (
+ RepoEvent,
RepoPreCreateEvent, RepoCreatedEvent,
RepoPreDeleteEvent, RepoDeletedEvent,
RepoPrePushEvent, RepoPushEvent,
@@ -51,6 +48,7 @@ from rhodecode.events.repo import (
)
from rhodecode.events.pullrequest import (
+ PullRequestEvent,
PullRequestCreateEvent,
PullRequestUpdateEvent,
PullRequestReviewEvent,
diff --git a/rhodecode/events/base.py b/rhodecode/events/base.py
new file mode 100644
--- /dev/null
+++ b/rhodecode/events/base.py
@@ -0,0 +1,69 @@
+# Copyright (C) 2016-2016 RhodeCode GmbH
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License, version 3
+# (only), as published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+# This program is dual-licensed. If you wish to learn more about the
+# RhodeCode Enterprise Edition, including its added features, Support services,
+# and proprietary license terms, please see https://rhodecode.com/licenses/
+
+from datetime import datetime
+from marshmallow import Schema, fields
+from pyramid.threadlocal import get_current_request
+from rhodecode.lib.utils2 import AttributeDict
+
+
+SYSTEM_USER = AttributeDict(dict(
+ username='__SYSTEM__'
+))
+
+
+class UserSchema(Schema):
+ """
+ Marshmallow schema for a user
+ """
+ username = fields.Str()
+
+
+class RhodecodeEventSchema(Schema):
+ """
+ Marshmallow schema for a rhodecode event
+ """
+ utc_timestamp = fields.DateTime()
+ acting_user = fields.Nested(UserSchema)
+ acting_ip = fields.Str()
+
+
+class RhodecodeEvent(object):
+ """
+ Base event class for all Rhodecode events
+ """
+ MarshmallowSchema = RhodecodeEventSchema
+
+ def __init__(self):
+ self.request = get_current_request()
+ self.utc_timestamp = datetime.utcnow()
+
+ @property
+ def acting_user(self):
+ if self.request:
+ return self.request.user.get_instance()
+ return SYSTEM_USER
+
+ @property
+ def acting_ip(self):
+ if self.request:
+ return self.request.user.ip_addr
+ return ''
+
+ def as_dict(self):
+ return self.MarshmallowSchema().dump(self).data
\ No newline at end of file
diff --git a/rhodecode/events/pullrequest.py b/rhodecode/events/pullrequest.py
--- a/rhodecode/events/pullrequest.py
+++ b/rhodecode/events/pullrequest.py
@@ -16,15 +16,40 @@
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
+from marshmallow import Schema, fields
+
from rhodecode.events.repo import RepoEvent
+def get_pull_request_url(pull_request):
+ from rhodecode.model.pull_request import PullRequestModel
+ return PullRequestModel().get_url(pull_request)
+
+
+class PullRequestSchema(Schema):
+ """
+ Marshmallow schema for a pull request
+ """
+ pull_request_id = fields.Integer()
+ url = fields.Function(get_pull_request_url)
+ title = fields.Str()
+
+
+class PullRequestEventSchema(RepoEvent.MarshmallowSchema):
+ """
+ Marshmallow schema for a pull request event
+ """
+ pullrequest = fields.Nested(PullRequestSchema)
+
+
class PullRequestEvent(RepoEvent):
"""
- Base class for events acting on a repository.
+ Base class for pull request events.
- :param repo: a :class:`Repository` instance
+ :param pullrequest: a :class:`PullRequest` instance
"""
+ MarshmallowSchema = PullRequestEventSchema
+
def __init__(self, pullrequest):
super(PullRequestEvent, self).__init__(pullrequest.target_repo)
self.pullrequest = pullrequest
diff --git a/rhodecode/events/repo.py b/rhodecode/events/repo.py
--- a/rhodecode/events/repo.py
+++ b/rhodecode/events/repo.py
@@ -16,8 +16,31 @@
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
+from marshmallow import Schema, fields
+
from rhodecode.model.db import Repository, Session
-from rhodecode.events import RhodecodeEvent
+from rhodecode.events.base import RhodecodeEvent
+
+
+def get_pull_request_url(repo):
+ from rhodecode.model.repo import RepoModel
+ return RepoModel().get_url(repo)
+
+
+class RepositorySchema(Schema):
+ """
+ Marshmallow schema for a repository
+ """
+ repo_id = fields.Integer()
+ repo_name = fields.Str()
+ url = fields.Function(get_pull_request_url)
+
+
+class RepoEventSchema(RhodecodeEvent.MarshmallowSchema):
+ """
+ Marshmallow schema for a repository event
+ """
+ repository = fields.Nested(RepositorySchema)
class RepoEvent(RhodecodeEvent):
@@ -26,7 +49,10 @@ class RepoEvent(RhodecodeEvent):
:param repo: a :class:`Repository` instance
"""
+ MarshmallowSchema = RepoEventSchema
+
def __init__(self, repo):
+ super(RepoEvent, self).__init__()
self.repo = repo
@@ -73,6 +99,16 @@ class RepoVCSEvent(RepoEvent):
self.extras = extras
super(RepoVCSEvent, self).__init__(self.repo)
+ @property
+ def acting_user(self):
+ if self.extras.get('username'):
+ return User.get_by_username(extras['username'])
+
+ @property
+ def acting_ip(self):
+ if self.extras.get('ip'):
+ return User.get_by_username(extras['ip'])
+
class RepoPrePullEvent(RepoVCSEvent):
"""
diff --git a/rhodecode/events/user.py b/rhodecode/events/user.py
--- a/rhodecode/events/user.py
+++ b/rhodecode/events/user.py
@@ -17,7 +17,8 @@
# and proprietary license terms, please see https://rhodecode.com/licenses/
from zope.interface import implementer
-from rhodecode.events import RhodecodeEvent
+
+from rhodecode.events.base import RhodecodeEvent
from rhodecode.events.interfaces import (
IUserRegistered, IUserPreCreate, IUserPreUpdate)
diff --git a/rhodecode/model/db.py b/rhodecode/model/db.py
--- a/rhodecode/model/db.py
+++ b/rhodecode/model/db.py
@@ -1641,6 +1641,7 @@ class Repository(Base, BaseModel):
'repo_name': repo.repo_name,
'repo_type': repo.repo_type,
'clone_uri': repo.clone_uri or '',
+ 'url': url('summary_home', repo_name=self.repo_name, qualified=True),
'private': repo.private,
'created_on': repo.created_on,
'description': repo.description,
@@ -3112,10 +3113,9 @@ class PullRequest(Base, _PullRequestBase
merge_status = PullRequestModel().merge_status(pull_request)
data = {
'pull_request_id': pull_request.pull_request_id,
- 'url': url('pullrequest_show',
- repo_name=pull_request.target_repo.repo_name,
- pull_request_id=pull_request.pull_request_id,
- qualified=True),
+ 'url': url('pullrequest_show', repo_name=self.target_repo.repo_name,
+ pull_request_id=self.pull_request_id,
+ qualified=True),
'title': pull_request.title,
'description': pull_request.description,
'status': pull_request.status,
diff --git a/rhodecode/model/pull_request.py b/rhodecode/model/pull_request.py
--- a/rhodecode/model/pull_request.py
+++ b/rhodecode/model/pull_request.py
@@ -748,6 +748,11 @@ class PullRequestModel(BaseModel):
return ids_to_add, ids_to_remove
+ def get_url(self, pull_request):
+ return url('pullrequest_show', repo_name=self.target_repo.repo_name,
+ pull_request_id=self.pull_request_id,
+ qualified=True)
+
def notify_reviewers(self, pull_request, reviewers_ids):
# notification to reviewers
if not reviewers_ids:
diff --git a/rhodecode/model/repo.py b/rhodecode/model/repo.py
--- a/rhodecode/model/repo.py
+++ b/rhodecode/model/repo.py
@@ -141,6 +141,9 @@ class RepoModel(BaseModel):
return None
+ def get_url(self, repo):
+ return url('summary_home', repo_name=repo.repo_name, qualified=True)
+
def get_users(self, name_contains=None, limit=20, only_active=True):
# TODO: mikhail: move this method to the UserModel.
query = self.sa.query(User)
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -78,6 +78,7 @@ requirements = [
'ipython',
'iso8601',
'kombu',
+ 'marshmallow',
'msgpack-python',
'packaging',
'psycopg2',