You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2013/09/13 19:56:07 UTC

[1/9] git commit: [#6545] add "timestamp" index back to ForumPost which was removed from Artifact in [58083948]

Updated Branches:
  refs/heads/db/5966 8e8ddbe1e -> 243c0cf50 (forced update)


[#6545] add "timestamp" index back to ForumPost which was removed from Artifact in [58083948]


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/224017c7
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/224017c7
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/224017c7

Branch: refs/heads/db/5966
Commit: 224017c76aec0b4009ff069e4a834bf969befe7d
Parents: 8e5201d
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Wed Sep 11 19:57:07 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Wed Sep 11 19:57:07 2013 +0000

----------------------------------------------------------------------
 ForgeDiscussion/forgediscussion/model/forum.py | 3 +++
 1 file changed, 3 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/224017c7/ForgeDiscussion/forgediscussion/model/forum.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/model/forum.py b/ForgeDiscussion/forgediscussion/model/forum.py
index 84040da..c13bb57 100644
--- a/ForgeDiscussion/forgediscussion/model/forum.py
+++ b/ForgeDiscussion/forgediscussion/model/forum.py
@@ -205,6 +205,9 @@ class ForumPost(M.Post):
     class __mongometa__:
         name='forum_post'
         history_class = ForumPostHistory
+        indexes = [
+            'timestamp',  # for the posts_24hr site_stats query
+        ]
     type_s = 'Post'
 
     discussion_id = ForeignIdProperty(Forum)


[2/9] git commit: [#6613] ticket:428 Tracker API include config & milestones & saved bins

Posted by br...@apache.org.
[#6613]  ticket:428  Tracker API include config & milestones & saved bins


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/023a0a34
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/023a0a34
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/023a0a34

Branch: refs/heads/db/5966
Commit: 023a0a34c9c106efe6fd640a93573c8a5ec3c2af
Parents: 224017c
Author: Yuriy Arhipov <yu...@yandex.ru>
Authored: Thu Sep 12 14:48:43 2013 +0400
Committer: Yuriy Arhipov <yu...@yandex.ru>
Committed: Thu Sep 12 14:48:43 2013 +0400

----------------------------------------------------------------------
 ForgeTracker/forgetracker/model/ticket.py               |  2 +-
 ForgeTracker/forgetracker/tests/functional/test_rest.py | 11 +++++++++++
 ForgeTracker/forgetracker/tracker_main.py               |  3 +++
 3 files changed, 15 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/023a0a34/ForgeTracker/forgetracker/model/ticket.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/model/ticket.py b/ForgeTracker/forgetracker/model/ticket.py
index 71fe6c5..f7534e9 100644
--- a/ForgeTracker/forgetracker/model/ticket.py
+++ b/ForgeTracker/forgetracker/model/ticket.py
@@ -513,7 +513,7 @@ class Bin(Artifact, ActivityObject):
         return result
 
     def __json__(self):
-        return dict(super(Bin,self).__json__(),
+        return dict(
             _id=self._id,
             summary=self.summary,
             terms=self.terms,

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/023a0a34/ForgeTracker/forgetracker/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_rest.py b/ForgeTracker/forgetracker/tests/functional/test_rest.py
index 750bd0e..df0a422 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_rest.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_rest.py
@@ -84,6 +84,17 @@ class TestRestUpdateTicket(TestTrackerApiBase):
         assert len(tickets.json['tickets']) == 1, tickets.json
         assert (tickets.json['tickets'][0]
                 == dict(ticket_num=1, summary='test new ticket')), tickets.json['tickets'][0]
+        assert tickets.json['tracker_config']['options']['mount_point'] == 'bugs'
+        assert tickets.json['tracker_config']['options']['TicketMonitoringType'] == 'AllTicketChanges'
+        assert not tickets.json['tracker_config']['options']['EnableVoting']
+        assert tickets.json['tracker_config']['options']['TicketMonitoringEmail'] == 'test@localhost'
+        assert tickets.json['tracker_config']['options']['mount_label'] == 'Tickets'
+        assert tickets.json['saved_bins'][0]['sort'] == 'mod_date_dt desc'
+        assert tickets.json['saved_bins'][0]['terms'] == '!status:wont-fix && !status:closed'
+        assert tickets.json['saved_bins'][0]['summary'] == 'Changes'
+        assert len(tickets.json['saved_bins'][0]) == 4
+        assert tickets.json['milestones'][0]['name'] == '1.0'
+        assert tickets.json['milestones'][1]['name'] == '2.0'
 
     def test_update_ticket(self):
         args = dict(self.ticket_args, summary='test update ticket', labels='',

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/023a0a34/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index df1e084..bc66e6c 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1583,6 +1583,9 @@ class RootRestController(BaseController):
                                         limit=int(limit), page=int(page))
         results['tickets'] = [dict(ticket_num=t.ticket_num, summary=t.summary)
                               for t in results['tickets']]
+        results['tracker_config'] = c.app.config
+        results['milestones'] = c.app.milestones
+        results['saved_bins'] = c.app.bins
         results.pop('q', None)
         results.pop('sort', None)
         return results


[9/9] git commit: [#5966] change sf.net/p/allura URLs (except tickets) to apache

Posted by br...@apache.org.
[#5966] change sf.net/p/allura URLs (except tickets) to apache


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/243c0cf5
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/243c0cf5
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/243c0cf5

Branch: refs/heads/db/5966
Commit: 243c0cf502604176fdb95e980df55b1509ed4a3e
Parents: c825e57
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Fri Sep 13 17:53:00 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 13 17:55:46 2013 +0000

----------------------------------------------------------------------
 Allura/allura/templates/jinja_master/theme_macros.html | 4 ++--
 Allura/docs/extending.rst                              | 4 ++--
 Allura/docs/installation.rst                           | 2 +-
 Allura/docs/online.rst                                 | 4 ++--
 Allura/setup.py                                        | 7 +++----
 CONTRIBUTING                                           | 6 ++----
 ForgeImporters/forgeimporters/base.py                  | 2 +-
 ForgeImporters/forgeimporters/tests/test_base.py       | 2 +-
 README.markdown                                        | 2 +-
 9 files changed, 15 insertions(+), 18 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/243c0cf5/Allura/allura/templates/jinja_master/theme_macros.html
----------------------------------------------------------------------
diff --git a/Allura/allura/templates/jinja_master/theme_macros.html b/Allura/allura/templates/jinja_master/theme_macros.html
index 61f5e2f..e8b1c3f 100644
--- a/Allura/allura/templates/jinja_master/theme_macros.html
+++ b/Allura/allura/templates/jinja_master/theme_macros.html
@@ -36,7 +36,7 @@
 {%- macro footer(year, path_to_static='') %}
 <footer id="site-footer">
   <nav>
-	  This project is powered by <a href="http://sourceforge.net/p/allura/">Allura</a>.
+	  This project is powered by <a href="https://forge-allura.apache.org/p/allura/">Allura</a>.
 	</nav>
 </footer>
 {%- endmacro %}
@@ -112,4 +112,4 @@
          {% endif %}
       </div>
   {% endif %}
-{%- endmacro %}
\ No newline at end of file
+{%- endmacro %}

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/243c0cf5/Allura/docs/extending.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/extending.rst b/Allura/docs/extending.rst
index 6a6abe0..5458b69 100644
--- a/Allura/docs/extending.rst
+++ b/Allura/docs/extending.rst
@@ -35,7 +35,7 @@ The available extension points for Allura are:
 * ``site_stats`` in the root API data.  Docs in :class:`allura.controllers.rest.RestController`
 * :mod:`allura.lib.package_path_loader` (for overriding templates)
 
-A listing of available 3rd-party extensions is at https://sourceforge.net/p/allura/wiki/Extensions/
+A listing of available 3rd-party extensions is at https://forge-allura.apache.org/p/allura/wiki/Extensions/
 
 To disable any Allura entry point, simply add an entry in your ``.ini`` config file
 with names and values corresponding to entry points defined in any ``setup.py`` file.
@@ -60,4 +60,4 @@ The events that allura publishes are:
 * project_updated
 * repo_cloned
 * repo_refreshed
-* repo_clone_task_failed
\ No newline at end of file
+* repo_clone_task_failed

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/243c0cf5/Allura/docs/installation.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/installation.rst b/Allura/docs/installation.rst
index 8b36cfc..44eb0ad 100644
--- a/Allura/docs/installation.rst
+++ b/Allura/docs/installation.rst
@@ -21,7 +21,7 @@ Installation
 Easy Setup
 ---------------
 
-Our easy setup instructions are in our README.rst file.  You can read it online at https://sourceforge.net/p/allura/git/#readme
+Our easy setup instructions are in our README.rst file.  You can read it online at https://forge-allura.apache.org/p/allura/git/ci/master/tree/README.markdown
 
 You should be able to get Allura up and running in well under an hour by following those instructions.
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/243c0cf5/Allura/docs/online.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/online.rst b/Allura/docs/online.rst
index 37c3103..632d1c9 100644
--- a/Allura/docs/online.rst
+++ b/Allura/docs/online.rst
@@ -22,9 +22,9 @@ Generated API docs, useful for browsing through the code, viewing inheritance, e
 
 * http://allura.sourceforge.net/epydoc/
 
-Our SourceForge project page, including tickets, discussion forums, etc.:
+Our project page, including tickets, discussion forums, etc.:
 
-* http://sourceforge.net/p/allura/home/
+* https://forge-allura.apache.org/p/allura/
 
 
 Much of the current forge was inspired by Roundup

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/243c0cf5/Allura/setup.py
----------------------------------------------------------------------
diff --git a/Allura/setup.py b/Allura/setup.py
index 4a5f4f1..32e6eb4 100644
--- a/Allura/setup.py
+++ b/Allura/setup.py
@@ -36,10 +36,9 @@ setup(
     version=__version__,
     description='Base distribution of the Allura development platform',
     long_description=PROJECT_DESCRIPTION,
-    author='SourceForge Team',
-    author_email='develop@discussion.allura.p.re.sf.net',
-    url='http://sourceforge.net/p/allura',
-    keywords='sourceforge allura turbogears pylons jinja2 mongodb rabbitmq',
+    author='Allura Team',
+    url='https://forge-allura.apache.org/p/allura/',
+    keywords='allura forge turbogears pylons jinja2 mongodb',
     license='Apache License, http://www.apache.org/licenses/LICENSE-2.0',
     platforms=[
         'Linux',

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/243c0cf5/CONTRIBUTING
----------------------------------------------------------------------
diff --git a/CONTRIBUTING b/CONTRIBUTING
index c8ed765..e52c1f1 100644
--- a/CONTRIBUTING
+++ b/CONTRIBUTING
@@ -25,7 +25,7 @@ number of individual projects.
 SourceForge.net is running an instance of Allura (aka New Forge, or
 Forge 2.0); and Allura itself is a project managed there:
 
-  <https://sourceforge.net/p/allura/>
+  <https://forge-allura.apache.org/p/allura/>
 
 The source for Allura is available there from a Git repo under the
 Apache License, Version 2.0.
@@ -83,8 +83,6 @@ The code is self-hosted in a public git repository.  Get it by cloning:
 
   git clone https://git-wip-us.apache.org/repos/asf/incubator-allura.git allura
 
-You can also 'fork' our repo at <https://sourceforge.net/p/allura/git/>.
-
 - CONTRIBUTING CODE
 
 Develop and test your patches locally and then get them to us in one of
@@ -134,7 +132,7 @@ mailing list:
 
 First, is your question already answered in the FAQ?
 
-  <https://sourceforge.net/p/allura/wiki/FAQ/>
+  <https://forge-allura.apache.org/p/allura/wiki/FAQ/>
 
 If not, then the right place to ask is either the mailing list (above)
 or the IRC channel:

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/243c0cf5/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index d7479b3..97d5536 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -117,7 +117,7 @@ class ProjectExtractor(object):
     @staticmethod
     def urlopen(url, retries=3, codes=(408,), **kw):
         req = urllib2.Request(url, **kw)
-        req.add_header('User-Agent', 'Allura Data Importer (http://sf.net/p/allura)')
+        req.add_header('User-Agent', 'Allura Data Importer (https://forge-allura.apache.org/p/allura/)')
         return h.urlopen(req, retries=retries, codes=codes)
 
     def get_page(self, page_name_or_url, parser=None, **kw):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/243c0cf5/ForgeImporters/forgeimporters/tests/test_base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/test_base.py b/ForgeImporters/forgeimporters/tests/test_base.py
index 4aefc35..1a3606d 100644
--- a/ForgeImporters/forgeimporters/tests/test_base.py
+++ b/ForgeImporters/forgeimporters/tests/test_base.py
@@ -35,7 +35,7 @@ class TestProjectExtractor(TestCase):
         Request.assert_called_once_with('myurl', data='foo')
         req = Request.return_value
         req.add_header.assert_called_once_with(
-                'User-Agent', 'Allura Data Importer (http://sf.net/p/allura)')
+                'User-Agent', 'Allura Data Importer (https://forge-allura.apache.org/p/allura/)')
         urlopen.assert_called_once_with(req, retries=3, codes=(408,))
         self.assertEqual(r, urlopen.return_value)
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/243c0cf5/README.markdown
----------------------------------------------------------------------
diff --git a/README.markdown b/README.markdown
index dcebab0..9869152 100644
--- a/README.markdown
+++ b/README.markdown
@@ -146,4 +146,4 @@ register a new project in your own forge, visit /p/add_project
     * Including how to enable extra features: <http://allura.sourceforge.net/installation.html>
 * Run the test suite (slow): `$ ALLURA_VALIDATION=none ./run_tests`
 * File bug reports at <https://sourceforge.net/p/allura/tickets/new/> (login required)
-* Contribute code according to this guide: <http://sourceforge.net/p/allura/wiki/Contributing%20Code/>
+* Contribute code according to this guide: <https://forge-allura.apache.org/p/allura/wiki/Contributing%20Code/>


[3/9] git commit: [#6613] don't expose TicketMonitoringEmail in API, and associated TestRestApiBase changes

Posted by br...@apache.org.
[#6613] don't expose TicketMonitoringEmail in API, and associated TestRestApiBase changes


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/cd1be45d
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/cd1be45d
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/cd1be45d

Branch: refs/heads/db/5966
Commit: cd1be45da425cc914b50690588585741dad3fd01
Parents: 023a0a3
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Thu Sep 12 17:59:12 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 12 17:59:12 2013 +0000

----------------------------------------------------------------------
 .../tests/functional/test_rest_api_tickets.py   |  3 +-
 AlluraTest/alluratest/controller.py             | 34 +++++++++++++-------
 .../tests/functional/test_import.py             |  8 ++---
 .../tests/functional/test_import.py             | 10 +++---
 .../forgetracker/tests/functional/test_rest.py  |  4 +++
 ForgeTracker/forgetracker/tracker_main.py       |  7 +++-
 6 files changed, 44 insertions(+), 22 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cd1be45d/Allura/allura/tests/functional/test_rest_api_tickets.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_rest_api_tickets.py b/Allura/allura/tests/functional/test_rest_api_tickets.py
index 11e149f..145e143 100644
--- a/Allura/allura/tests/functional/test_rest_api_tickets.py
+++ b/Allura/allura/tests/functional/test_rest_api_tickets.py
@@ -29,7 +29,8 @@ class TestApiTicket(TestRestApiBase):
     def set_api_ticket(self, expire=None):
         if not expire:
             expire = timedelta(days=1)
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities={'import': ['Projects','test']},
+        test_admin = M.User.query.get(username='test-admin')
+        api_ticket = M.ApiTicket(user_id=test_admin._id, capabilities={'import': ['Projects','test']},
                                  expires=datetime.utcnow() + expire)
         session(api_ticket).flush()
         self.set_api_token(api_ticket)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cd1be45d/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 7b38700..577088d 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -145,23 +145,35 @@ class TestRestApiBase(TestController):
     def setUp(self):
         super(TestRestApiBase, self).setUp()
         setup_global_objects()
-#        h.set_context('test', 'home')
-        self.user = M.User.query.get(username='test-admin')
-        self.token = M.ApiToken(user_id=self.user._id)
-        ming.orm.session(self.token).flush()
+        self._use_token = None
+        self._token_cache = {}
 
     def set_api_token(self, token):
-        self.token = token
+        self._use_token = token
+
+    def token(self, username):
+        if self._use_token:
+            return self._use_token
+
+        # only create token once, else ming gets dupe key error
+        if username not in self._token_cache:
+            user = M.User.query.get(username=username)
+            token = M.ApiToken(user_id=user._id)
+            ming.orm.session(token).flush()
+            self._token_cache[username] = token
+
+        return self._token_cache[username]
 
     def _api_getpost(self, method, path, api_key=None, api_timestamp=None, api_signature=None,
-                 wrap_args=None, **params):
+                 wrap_args=None, user='test-admin', **params):
         if wrap_args:
             params = {wrap_args: params}
         params = variabledecode.variable_encode(params, add_repetitions=False)
         if api_key: params['api_key'] = api_key
         if api_timestamp: params['api_timestamp'] = api_timestamp
         if api_signature: params['api_signature'] = api_signature
-        params = self.token.sign_request(path, params)
+
+        params = self.token(user).sign_request(path, params)
 
         fn = self.app.post if method=='POST' else self.app.get
 
@@ -175,9 +187,9 @@ class TestRestApiBase(TestController):
             return response
 
     def api_get(self, path, api_key=None, api_timestamp=None, api_signature=None,
-                 wrap_args=None, **params):
-        return self._api_getpost('GET', path, api_key, api_timestamp, api_signature, wrap_args, **params)
+                 wrap_args=None, user='test-admin', **params):
+        return self._api_getpost('GET', path, api_key, api_timestamp, api_signature, wrap_args, user, **params)
 
     def api_post(self, path, api_key=None, api_timestamp=None, api_signature=None,
-                 wrap_args=None, **params):
-        return self._api_getpost('POST', path, api_key, api_timestamp, api_signature, wrap_args, **params)
+                 wrap_args=None, user='test-admin', **params):
+        return self._api_getpost('POST', path, api_key, api_timestamp, api_signature, wrap_args, user, **params)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cd1be45d/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
----------------------------------------------------------------------
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
index 17d13c7..76d7896 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_import.py
@@ -57,7 +57,7 @@ class TestImportController(TestRestApiBase):#TestController):
         assert not r.json['errors']
 
     def test_import_anon(self):
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities={'import': ['Projects', 'test']},
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
                                  expires=datetime.utcnow() + timedelta(days=1))
         ming.orm.session(api_ticket).flush()
         self.set_api_token(api_ticket)
@@ -76,7 +76,7 @@ class TestImportController(TestRestApiBase):#TestController):
         assert 'Anonymous' in str(r)
 
     def test_import_map(self):
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities={'import': ['Projects', 'test']},
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
                                  expires=datetime.utcnow() + timedelta(days=1))
         ming.orm.session(api_ticket).flush()
         self.set_api_token(api_ticket)
@@ -97,7 +97,7 @@ class TestImportController(TestRestApiBase):#TestController):
         assert 'Anonymous' not in str(r)
 
     def test_import_create(self):
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities={'import': ['Projects', 'test']},
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities={'import': ['Projects', 'test']},
                                  expires=datetime.utcnow() + timedelta(days=1))
         ming.orm.session(api_ticket).flush()
         self.set_api_token(api_ticket)
@@ -117,7 +117,7 @@ class TestImportController(TestRestApiBase):#TestController):
         assert 'test-rick446' in str(r)
 
     def set_api_ticket(self, caps={'import': ['Projects', 'test']}):
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities=caps,
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities=caps,
                                  expires=datetime.utcnow() + timedelta(days=1))
         ming.orm.session(api_ticket).flush()
         self.set_api_token(api_ticket)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cd1be45d/ForgeTracker/forgetracker/tests/functional/test_import.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_import.py b/ForgeTracker/forgetracker/tests/functional/test_import.py
index f876b93..8192115 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_import.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_import.py
@@ -46,7 +46,7 @@ class TestImportController(TestRestApiBase):
         return resp.follow()
 
     def set_api_ticket(self, caps={'import': ['Projects','test']}):
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities=caps,
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities=caps,
                                  expires=datetime.utcnow() + timedelta(days=1))
         ming.orm.session(api_ticket).flush()
         self.set_api_token(api_ticket)
@@ -107,7 +107,7 @@ class TestImportController(TestRestApiBase):
             '/admin/bugs/set_custom_fields',
             params=variable_encode(params))
         here_dir = os.path.dirname(__file__)
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities={'import': ['Projects','test']},
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities={'import': ['Projects','test']},
                                  expires=datetime.utcnow() + timedelta(days=1))
         ming.orm.session(api_ticket).flush()
         self.set_api_token(api_ticket)
@@ -129,7 +129,7 @@ class TestImportController(TestRestApiBase):
     @td.with_tracker
     def test_import(self):
         here_dir = os.path.dirname(__file__)
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities={'import': ['Projects','test']},
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities={'import': ['Projects','test']},
                                  expires=datetime.utcnow() + timedelta(days=1))
         ming.orm.session(api_ticket).flush()
         self.set_api_token(api_ticket)
@@ -184,7 +184,7 @@ class TestImportController(TestRestApiBase):
 
     @td.with_tracker
     def test_links(self):
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities={'import': ['Projects','test']},
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities={'import': ['Projects','test']},
                                  expires=datetime.utcnow() + timedelta(days=1))
         ming.orm.session(api_ticket).flush()
         self.set_api_token(api_ticket)
@@ -207,7 +207,7 @@ class TestImportController(TestRestApiBase):
 
     @td.with_tracker
     def test_slug(self):
-        api_ticket = M.ApiTicket(user_id=self.user._id, capabilities={'import': ['Projects','test']},
+        api_ticket = M.ApiTicket(user_id=c.user._id, capabilities={'import': ['Projects','test']},
                                  expires=datetime.utcnow() + timedelta(days=1))
         ming.orm.session(api_ticket).flush()
         self.set_api_token(api_ticket)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cd1be45d/ForgeTracker/forgetracker/tests/functional/test_rest.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tests/functional/test_rest.py b/ForgeTracker/forgetracker/tests/functional/test_rest.py
index df0a422..b687efb 100644
--- a/ForgeTracker/forgetracker/tests/functional/test_rest.py
+++ b/ForgeTracker/forgetracker/tests/functional/test_rest.py
@@ -96,6 +96,10 @@ class TestRestUpdateTicket(TestTrackerApiBase):
         assert tickets.json['milestones'][0]['name'] == '1.0'
         assert tickets.json['milestones'][1]['name'] == '2.0'
 
+    def test_ticket_index_noauth(self):
+        tickets = self.api_get('/rest/p/test/bugs', user='*anonymous')
+        assert 'TicketMonitoringEmail' not in tickets.json['tracker_config']['options']
+
     def test_update_ticket(self):
         args = dict(self.ticket_args, summary='test update ticket', labels='',
                     assigned_to=self.ticket_args['assigned_to_id'] or '')

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/cd1be45d/ForgeTracker/forgetracker/tracker_main.py
----------------------------------------------------------------------
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index bc66e6c..28bd4aa 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -1583,7 +1583,12 @@ class RootRestController(BaseController):
                                         limit=int(limit), page=int(page))
         results['tickets'] = [dict(ticket_num=t.ticket_num, summary=t.summary)
                               for t in results['tickets']]
-        results['tracker_config'] = c.app.config
+        results['tracker_config'] = c.app.config.__json__()
+        if not has_access(c.app, 'admin', c.user):
+            try:
+                del results['tracker_config']['options']['TicketMonitoringEmail']
+            except KeyError:
+                pass
         results['milestones'] = c.app.milestones
         results['saved_bins'] = c.app.bins
         results.pop('q', None)


[4/9] git commit: [#6540] bump GoogleCodeWikiImporter and TracWikiImporter versions for their rate-limiting additions

Posted by br...@apache.org.
[#6540] bump GoogleCodeWikiImporter and TracWikiImporter versions for their rate-limiting additions


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/b6776afe
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/b6776afe
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/b6776afe

Branch: refs/heads/db/5966
Commit: b6776afe3b6cdba86a7a75648044279b40476fdc
Parents: 3c0a7e0
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Thu Sep 12 19:56:33 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 12 19:56:41 2013 +0000

----------------------------------------------------------------------
 requirements-sf.txt | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/b6776afe/requirements-sf.txt
----------------------------------------------------------------------
diff --git a/requirements-sf.txt b/requirements-sf.txt
index ecad4a2..071e528 100644
--- a/requirements-sf.txt
+++ b/requirements-sf.txt
@@ -6,7 +6,7 @@ kombu==1.0.4
 coverage==3.5a1-20110413
 ForgeHg==0.1.16
 ForgePastebin==0.2.7
-GoogleCodeWikiImporter==0.3.3
+GoogleCodeWikiImporter==0.4.3
 mechanize==0.2.4
 mercurial==1.4.3
 MySQL-python==1.2.3c1
@@ -20,7 +20,7 @@ wsgipreload==1.2
 pyzmq==2.1.7
 html2text==3.200.3dev-20121112
 PyMollom==0.1
-TracWikiImporter==0.2.2
+TracWikiImporter==0.3.2
 
 # use version built from https://github.com/johnsca/GitPython/commits/tv/6000
 # for unmerged fixes for [#5411], [#6000], and [#6078]


[8/9] git commit: [#5966] start adding docs for our scripts

Posted by br...@apache.org.
[#5966] start adding docs for our scripts


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/c825e57c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/c825e57c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/c825e57c

Branch: refs/heads/db/5966
Commit: c825e57ca4d325d58565fcff96cd4b7576656880
Parents: 26586c3
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Thu Sep 12 18:44:50 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 13 17:55:46 2013 +0000

----------------------------------------------------------------------
 Allura/docs/administration.rst | 66 +++++++++++++++++++++++++++++++++++++
 Allura/docs/index.rst          |  1 +
 2 files changed, 67 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c825e57c/Allura/docs/administration.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/administration.rst b/Allura/docs/administration.rst
new file mode 100644
index 0000000..b58339f
--- /dev/null
+++ b/Allura/docs/administration.rst
@@ -0,0 +1,66 @@
+..     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.
+
+Administration
+=================
+
+Commands, Scripts, and Tasks
+----------------------------
+
+Allura has many `paster` commands and `paster` scripts that can be run from the
+server commandline to administrate Allura.  There are also tasks that can be
+run through the `taskd` system.  New tasks can be submitted via the web at
+/nf/admin/task_manager  Some paster commands and scripts have been set up
+so that they are runnable as tasks too, giving you the convenience of starting
+them through the web and letting `taskd` execute them, rather than from a server
+shell.
+
+Commands can be discovered and run via the `paster` command when you are in the
+'Allura' directory that has your .ini file.  For example::
+
+    (env-allura) Allura$ paster help
+    ... all commands listed here ...
+
+    (env-allura) Allura$ paster create-neighborhood --help
+    ... specific command help ...
+
+    (env-allura) Allura$ paster create-neighborhood development.ini myneighborhood myuser ...
+
+
+Scripts are in the `scripts/` directory and run via `paster script`.  An extra
+`--` is required to separate script arguments from paster arguments.  Example::
+
+    (env-allura) Allura$ paster script development.ini ../scripts/create-allura-sitemap.py -- --help
+    ... help output ...
+
+    (env-allura) Allura$ paster script development.ini ../scripts/create-allura-sitemap.py -- -u 100
+
+
+TODO: explain how to run tasks, explain important scripts, commands and tasks here
+
+
+Client Scripts
+--------------
+
+Allura includes some client scripts that use Allura APIs and do not have to be run
+from an Allura server.  They do require various python packages to be installed
+and possibly a local Allura codebase set up.
+
+One such script is `wiki-copy.py` which reads the wiki pages from one Allura wiki
+instance and uploads them to another Allura wiki instance.  It can be run as::
+
+$ python scripts/wiki-copy.py --help

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c825e57c/Allura/docs/index.rst
----------------------------------------------------------------------
diff --git a/Allura/docs/index.rst b/Allura/docs/index.rst
index 877b98d..ebbd0bf 100644
--- a/Allura/docs/index.rst
+++ b/Allura/docs/index.rst
@@ -35,6 +35,7 @@ Getting Started
    :maxdepth: 2
 
    installation
+   administration
    platform_tour
    scm_host
    migration


[6/9] git commit: [#5966] add wiki-copy.py script

Posted by br...@apache.org.
[#5966] add wiki-copy.py script


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/c2d05580
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/c2d05580
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/c2d05580

Branch: refs/heads/db/5966
Commit: c2d05580f77f0fc2116bb86d161b93400919f3e2
Parents: b6776af
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Wed Sep 11 19:54:24 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 13 17:55:45 2013 +0000

----------------------------------------------------------------------
 scripts/wiki-copy.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/c2d05580/scripts/wiki-copy.py
----------------------------------------------------------------------
diff --git a/scripts/wiki-copy.py b/scripts/wiki-copy.py
new file mode 100644
index 0000000..bca726d
--- /dev/null
+++ b/scripts/wiki-copy.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python
+
+#       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 re
+import os
+import sys
+import urllib
+import urlparse
+from optparse import OptionParser
+import json
+
+from ConfigParser import ConfigParser, NoOptionError
+import webbrowser
+import oauth2 as oauth
+
+
+def main():
+    cp = ConfigParser()
+    cp.read('/var/local/allura/Allura/production.ini')
+
+    op = OptionParser(usage='usage: %prog [options]')
+    op.add_option('-u', '--base-url', action='store', dest='base_url',
+            default=cp.get('app:main', 'base_url'), help='URL of API to upload to [default: %default]')
+    op.add_option('-D', '--debug', action='store_true', dest='debug', default=False)
+    op.add_option('-f', '--from-wiki', action='store', dest='from_wiki',
+            help='URL of wiki to copy from like http://fromserver.com/rest/p/test/wiki/')
+    op.add_option('-t', '--to-wiki', action='store', dest='to_wiki',
+            help='URL of wiki to copy to like http://toserver.com/rest/p/test/wiki/')
+    (options, args) = op.parse_args(sys.argv[1:])
+
+    oauth_client = make_oauth_client(options.base_url)
+
+    wiki_data = urllib.urlopen(options.from_wiki).read()
+    wiki_json = json.loads(wiki_data)['pages']
+    for p in wiki_json:
+        from_url = options.from_wiki+urllib.quote(p)
+        to_url = options.to_wiki+urllib.quote(p)
+        try:
+            page_data = urllib.urlopen(from_url).read()
+            page_json = json.loads(page_data)
+            if options.debug:
+                print page_json['text']
+                break
+            resp = oauth_client.request(to_url, 'POST', body=urllib.urlencode(dict(text=page_json['text'].encode('utf-8'))))
+            if resp[0]['status'] == '200':
+                print "Posted {0} to {1}".format(page_json['title'], to_url)
+            else:
+                print "Error posting {0} to {1}: {2} (project may not exist)".format(page_json['title'], to_url, resp[0]['status'])
+                break
+        except:
+            print "Error processing " + p
+            raise
+
+def make_oauth_client(base_url):
+    """
+    Build an oauth.Client with which callers can query Allura.
+
+    Based on Allura, sfpy, and sfx push scripts
+    """
+
+    cp = ConfigParser()
+    cp.read(os.path.join(os.environ['HOME'], '.convertrc'))
+
+    # https://sourceforge.net/p/forge/documentation/API%20-%20Beta/
+    REQUEST_TOKEN_URL = base_url+'/rest/oauth/request_token'
+    AUTHORIZE_URL =     base_url+'/rest/oauth/authorize'
+    ACCESS_TOKEN_URL =  base_url+'/rest/oauth/access_token'
+    oauth_key = option(cp, base_url, 'oauth_key', 'Forge API OAuth Key (%s/auth/oauth/): ' % base_url)
+    oauth_secret = option(cp, base_url, 'oauth_secret', 'Forge API Oauth Secret: ')
+    consumer = oauth.Consumer(oauth_key, oauth_secret)
+
+    try:
+        oauth_token = cp.get(base_url, 'oauth_token')
+        oauth_token_secret = cp.get(base_url, 'oauth_token_secret')
+    except NoOptionError:
+        client = oauth.Client(consumer)
+        resp, content = client.request(REQUEST_TOKEN_URL, 'GET')
+        assert resp['status'] == '200', resp
+
+        request_token = dict(urlparse.parse_qsl(content))
+        pin_url = "%s?oauth_token=%s" % (AUTHORIZE_URL, request_token['oauth_token'])
+        if getattr(webbrowser.get(), 'name', '') == 'links':
+            # sandboxes
+            print("Go to %s" % pin_url)
+        else:
+            webbrowser.open(pin_url)
+        oauth_verifier = raw_input('What is the PIN? ')
+
+        token = oauth.Token(request_token['oauth_token'], request_token['oauth_token_secret'])
+        token.set_verifier(oauth_verifier)
+        client = oauth.Client(consumer, token)
+        resp, content = client.request(ACCESS_TOKEN_URL, "GET")
+        access_token = dict(urlparse.parse_qsl(content))
+        oauth_token = access_token['oauth_token']
+        oauth_token_secret = access_token['oauth_token_secret']
+
+        cp.set(base_url, 'oauth_token', oauth_token)
+        cp.set(base_url, 'oauth_token_secret', oauth_token_secret)
+
+    # save oauth token for later use
+    cp.write(open(os.path.join(os.environ['HOME'], '.convertrc'), 'w'))
+
+    access_token = oauth.Token(oauth_token, oauth_token_secret)
+    oauth_client = oauth.Client(consumer, access_token)
+    return oauth_client
+
+def option(cp, section, key, prompt=None):
+    """ shared (copy/paste) between Allura & sfpy """
+    if not cp.has_section(section):
+        cp.add_section(section)
+    if cp.has_option(section, key):
+        value = cp.get(section, key)
+    else:
+        value = raw_input(prompt or ('%s: ' % key))
+        cp.set(section, key, value)
+    return value
+
+
+if __name__ == '__main__':
+    main()


[7/9] git commit: [#5966] remove SF-internal references; PEP-8 cleanup; determine base_url automatically

Posted by br...@apache.org.
[#5966] remove SF-internal references; PEP-8 cleanup; determine base_url automatically


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/26586c34
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/26586c34
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/26586c34

Branch: refs/heads/db/5966
Commit: 26586c348755c4b4844a15346dc57560535e04ce
Parents: c2d0558
Author: Dave Brondsema <db...@slashdotmedia.com>
Authored: Wed Sep 11 22:12:59 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Fri Sep 13 17:55:45 2013 +0000

----------------------------------------------------------------------
 scripts/wiki-copy.py | 33 ++++++++++++++-------------------
 1 file changed, 14 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/26586c34/scripts/wiki-copy.py
----------------------------------------------------------------------
diff --git a/scripts/wiki-copy.py b/scripts/wiki-copy.py
index bca726d..35e0077 100644
--- a/scripts/wiki-copy.py
+++ b/scripts/wiki-copy.py
@@ -17,7 +17,6 @@
 #       specific language governing permissions and limitations
 #       under the License.
 
-import re
 import os
 import sys
 import urllib
@@ -31,20 +30,16 @@ import oauth2 as oauth
 
 
 def main():
-    cp = ConfigParser()
-    cp.read('/var/local/allura/Allura/production.ini')
-
     op = OptionParser(usage='usage: %prog [options]')
-    op.add_option('-u', '--base-url', action='store', dest='base_url',
-            default=cp.get('app:main', 'base_url'), help='URL of API to upload to [default: %default]')
-    op.add_option('-D', '--debug', action='store_true', dest='debug', default=False)
     op.add_option('-f', '--from-wiki', action='store', dest='from_wiki',
-            help='URL of wiki to copy from like http://fromserver.com/rest/p/test/wiki/')
+                  help='URL of wiki API to copy from like http://fromserver.com/rest/p/test/wiki/')
     op.add_option('-t', '--to-wiki', action='store', dest='to_wiki',
-            help='URL of wiki to copy to like http://toserver.com/rest/p/test/wiki/')
+                  help='URL of wiki API to copy to like http://toserver.com/rest/p/test/wiki/')
+    op.add_option('-D', '--debug', action='store_true', dest='debug', default=False)
     (options, args) = op.parse_args(sys.argv[1:])
 
-    oauth_client = make_oauth_client(options.base_url)
+    base_url = options.to_wiki.split('/rest/')[0]
+    oauth_client = make_oauth_client(base_url)
 
     wiki_data = urllib.urlopen(options.from_wiki).read()
     wiki_json = json.loads(wiki_data)['pages']
@@ -67,20 +62,18 @@ def main():
             print "Error processing " + p
             raise
 
+
 def make_oauth_client(base_url):
     """
     Build an oauth.Client with which callers can query Allura.
-
-    Based on Allura, sfpy, and sfx push scripts
     """
-
+    config_file = os.path.join(os.environ['HOME'], '.allurarc')
     cp = ConfigParser()
-    cp.read(os.path.join(os.environ['HOME'], '.convertrc'))
+    cp.read(config_file)
 
-    # https://sourceforge.net/p/forge/documentation/API%20-%20Beta/
     REQUEST_TOKEN_URL = base_url+'/rest/oauth/request_token'
-    AUTHORIZE_URL =     base_url+'/rest/oauth/authorize'
-    ACCESS_TOKEN_URL =  base_url+'/rest/oauth/access_token'
+    AUTHORIZE_URL = base_url+'/rest/oauth/authorize'
+    ACCESS_TOKEN_URL = base_url+'/rest/oauth/access_token'
     oauth_key = option(cp, base_url, 'oauth_key', 'Forge API OAuth Key (%s/auth/oauth/): ' % base_url)
     oauth_secret = option(cp, base_url, 'oauth_secret', 'Forge API Oauth Secret: ')
     consumer = oauth.Consumer(oauth_key, oauth_secret)
@@ -114,14 +107,16 @@ def make_oauth_client(base_url):
         cp.set(base_url, 'oauth_token_secret', oauth_token_secret)
 
     # save oauth token for later use
-    cp.write(open(os.path.join(os.environ['HOME'], '.convertrc'), 'w'))
+    cp.write(open(config_file, 'w'))
+    print 'Saving oauth tokens in {} for later re-use'.format(config_file)
+    print
 
     access_token = oauth.Token(oauth_token, oauth_token_secret)
     oauth_client = oauth.Client(consumer, access_token)
     return oauth_client
 
+
 def option(cp, section, key, prompt=None):
-    """ shared (copy/paste) between Allura & sfpy """
     if not cp.has_section(section):
         cp.add_section(section)
     if cp.has_option(section, key):


[5/9] git commit: [#6540] Added rate limiting on tool imports

Posted by br...@apache.org.
[#6540] Added rate limiting on tool imports

Signed-off-by: Cory Johns <cj...@slashdotmedia.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-allura/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-allura/commit/3c0a7e0f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/3c0a7e0f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/3c0a7e0f

Branch: refs/heads/db/5966
Commit: 3c0a7e0f5fd179096d48239e3db8c014960a2181
Parents: cd1be45
Author: Cory Johns <cj...@slashdotmedia.com>
Authored: Mon Sep 9 22:35:20 2013 +0000
Committer: Dave Brondsema <db...@slashdotmedia.com>
Committed: Thu Sep 12 19:56:41 2013 +0000

----------------------------------------------------------------------
 ForgeImporters/forgeimporters/base.py           | 44 +++++++++++++++++++-
 ForgeImporters/forgeimporters/github/code.py    | 19 +++++----
 .../forgeimporters/github/tests/test_code.py    | 20 +++++++++
 ForgeImporters/forgeimporters/google/code.py    | 17 ++++----
 .../forgeimporters/google/tests/test_code.py    | 18 ++++++++
 ForgeImporters/forgeimporters/google/tracker.py | 19 +++++----
 .../forgeimporters/tests/google/test_tracker.py | 20 ++++++++-
 .../forgeimporters/trac/tests/test_tickets.py   | 18 ++++++++
 ForgeImporters/forgeimporters/trac/tickets.py   | 19 +++++----
 9 files changed, 160 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3c0a7e0f/ForgeImporters/forgeimporters/base.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/base.py b/ForgeImporters/forgeimporters/base.py
index ffb1650..d7479b3 100644
--- a/ForgeImporters/forgeimporters/base.py
+++ b/ForgeImporters/forgeimporters/base.py
@@ -68,14 +68,16 @@ class ToolImportForm(schema.Schema):
 
 
 class ImportErrorHandler(object):
-    def __init__(self, importer, project_name):
+    def __init__(self, importer, project_name, project):
         self.importer = importer
         self.project_name = project_name
+        self.project = project
 
     def __enter__(self):
         pass
 
     def __exit__(self, exc_type, exc_val, exc_tb):
+        self.importer.clear_pending(self.project)
         if exc_type:
             g.post_event('import_tool_task_failed',
                 error=str(exc_val),
@@ -89,7 +91,7 @@ class ImportErrorHandler(object):
 @task(notifications_disabled=True)
 def import_tool(importer_name, project_name=None, mount_point=None, mount_label=None, **kw):
     importer = ToolImporter.by_name(importer_name)
-    with ImportErrorHandler(importer, project_name):
+    with ImportErrorHandler(importer, project_name, c.project):
         importer.import_tool(c.project, c.user, project_name=project_name,
                 mount_point=mount_point, mount_label=mount_label, **kw)
 
@@ -326,6 +328,44 @@ class ToolImporter(object):
                 importers[ep.name] = importer()
         return importers
 
+    @property
+    def classname(self):
+        return self.__class__.__name__
+
+    def enforce_limit(self, project):
+        """
+        Enforce rate limiting of tool imports on a given project.
+
+        Returns False if limit is met / exceeded.  Otherwise, increments the
+        count of pending / in-progress imports and returns True.
+        """
+        limit = config.get('tool_import.rate_limit', 1)
+        pending_key = 'tool_data.%s.pending' % self.classname
+        modified_project = M.Project.query.find_and_modify(
+                query={
+                        '_id': project._id,
+                        '$or': [
+                                {pending_key: None},
+                                {pending_key: {'$lt': limit}},
+                            ],
+                    },
+                update={'$inc': {pending_key: 1}},
+                new=True,
+            )
+        return modified_project is not None
+
+    def clear_pending(self, project):
+        """
+        Decrement the pending counter for this importer on the given project,
+        to indicate that an import is complete.
+        """
+        pending_key = 'tool_data.%s.pending' % self.classname
+        M.Project.query.find_and_modify(
+                query={'_id': project._id},
+                update={'$inc': {pending_key: -1}},
+                new=True,
+            )
+
     def import_tool(self, project, user, project_name=None,
             mount_point=None, mount_label=None, **kw):
         """

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3c0a7e0f/ForgeImporters/forgeimporters/github/code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/code.py b/ForgeImporters/forgeimporters/github/code.py
index 92e5626..aa5fe7d 100644
--- a/ForgeImporters/forgeimporters/github/code.py
+++ b/ForgeImporters/forgeimporters/github/code.py
@@ -45,7 +45,7 @@ from forgeimporters.github import GitHubProjectExtractor
 @task(notifications_disabled=True)
 def import_tool(**kw):
     importer = GitHubRepoImporter()
-    with ImportErrorHandler(importer, kw.get('project_name')):
+    with ImportErrorHandler(importer, kw.get('project_name'), c.project):
         importer.import_tool(c.project, c.user, **kw)
 
 
@@ -73,13 +73,16 @@ class GitHubRepoImportController(BaseController):
     @require_post()
     @validate(GitHubRepoImportForm(ForgeGitApp), error_handler=index)
     def create(self, gh_project_name, gh_user_name, mount_point, mount_label, **kw):
-        import_tool.post(
-                project_name=gh_project_name,
-                user_name=gh_user_name,
-                mount_point=mount_point,
-                mount_label=mount_label)
-        flash('Repo import has begun. Your new repo will be available '
-                'when the import is complete.')
+        if GitHubRepoImporter().enforce_limit(c.project):
+            import_tool.post(
+                    project_name=gh_project_name,
+                    user_name=gh_user_name,
+                    mount_point=mount_point,
+                    mount_label=mount_label)
+            flash('Repo import has begun. Your new repo will be available '
+                    'when the import is complete.')
+        else:
+            flash('There are too many imports pending at this time.  Please wait and try again.', 'error')
         redirect(c.project.url() + 'admin/')
 
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3c0a7e0f/ForgeImporters/forgeimporters/github/tests/test_code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/github/tests/test_code.py b/ForgeImporters/forgeimporters/github/tests/test_code.py
index 09afa87..729d8c5 100644
--- a/ForgeImporters/forgeimporters/github/tests/test_code.py
+++ b/ForgeImporters/forgeimporters/github/tests/test_code.py
@@ -17,9 +17,11 @@
 
 from unittest import TestCase
 from mock import Mock, patch
+from ming.odm import ThreadLocalORMSession
 
 from allura.tests import TestController
 from allura.tests.decorators import with_tool
+from allura import model as M
 from forgeimporters.github.code import GitHubRepoImporter
 
 
@@ -76,3 +78,21 @@ class TestGitHubImportController(TestController, TestCase):
         self.assertEqual(u'mylabel', import_tool.post.call_args[1]['mount_label'])
         self.assertEqual(u'poop', import_tool.post.call_args[1]['project_name'])
         self.assertEqual(u'spooky', import_tool.post.call_args[1]['user_name'])
+
+    @with_git
+    @patch('forgeimporters.github.code.import_tool')
+    def test_create_limit(self, import_tool):
+        project = M.Project.query.get(shortname=test_project_with_repo)
+        project.set_tool_data('GitHubRepoImporter', pending=1)
+        ThreadLocalORMSession.flush_all()
+        params = dict(
+                gh_user_name='spooky',
+                gh_project_name='poop',
+                mount_label='mylabel',
+                mount_point='mymount',
+                )
+        r = self.app.post('/p/{}/admin/ext/import/github-repo/create'.format(test_project_with_repo),
+                params,
+                status=302).follow()
+        self.assertIn('Please wait and try again', r)
+        self.assertEqual(import_tool.post.call_count, 0)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3c0a7e0f/ForgeImporters/forgeimporters/google/code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/code.py b/ForgeImporters/forgeimporters/google/code.py
index 134ecb6..f8e42b3 100644
--- a/ForgeImporters/forgeimporters/google/code.py
+++ b/ForgeImporters/forgeimporters/google/code.py
@@ -88,7 +88,7 @@ def get_repo_class(type_):
 @task(notifications_disabled=True)
 def import_tool(**kw):
     importer = GoogleRepoImporter()
-    with ImportErrorHandler(importer, kw.get('project_name')):
+    with ImportErrorHandler(importer, kw.get('project_name'), c.project):
         importer.import_tool(c.project, c.user, **kw)
 
 
@@ -140,12 +140,15 @@ class GoogleRepoImportController(BaseController):
     @require_post()
     @validate(GoogleRepoImportForm(), error_handler=index)
     def create(self, gc_project_name, mount_point, mount_label, **kw):
-        import_tool.post(
-                project_name=gc_project_name,
-                mount_point=mount_point,
-                mount_label=mount_label)
-        flash('Repo import has begun. Your new repo will be available '
-                'when the import is complete.')
+        if GoogleRepoImporter().enforce_limit(c.project):
+            import_tool.post(
+                    project_name=gc_project_name,
+                    mount_point=mount_point,
+                    mount_label=mount_label)
+            flash('Repo import has begun. Your new repo will be available '
+                    'when the import is complete.')
+        else:
+            flash('There are too many imports pending at this time.  Please wait and try again.', 'error')
         redirect(c.project.url() + 'admin/')
 
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3c0a7e0f/ForgeImporters/forgeimporters/google/tests/test_code.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/tests/test_code.py b/ForgeImporters/forgeimporters/google/tests/test_code.py
index 806a004..96dd687 100644
--- a/ForgeImporters/forgeimporters/google/tests/test_code.py
+++ b/ForgeImporters/forgeimporters/google/tests/test_code.py
@@ -17,9 +17,11 @@
 
 from unittest import TestCase
 from mock import Mock, patch
+from ming.odm import ThreadLocalORMSession
 
 from allura.tests import TestController
 from allura.tests.decorators import with_tool
+from allura import model as M
 
 
 # important to be distinct from 'test' which ForgeSVN uses, so that the tests can run in parallel and not clobber each other
@@ -113,3 +115,19 @@ class TestGoogleRepoImportController(TestController, TestCase):
         self.assertEqual(u'mymount', import_tool.post.call_args[1]['mount_point'])
         self.assertEqual(u'mylabel', import_tool.post.call_args[1]['mount_label'])
         self.assertEqual(u'poop', import_tool.post.call_args[1]['project_name'])
+
+    @with_svn
+    @patch('forgeimporters.google.code.import_tool')
+    def test_create_limit(self, import_tool):
+        project = M.Project.query.get(shortname=test_project_with_repo)
+        project.set_tool_data('GoogleRepoImporter', pending=1)
+        ThreadLocalORMSession.flush_all()
+        params = dict(gc_project_name='poop',
+                mount_label='mylabel',
+                mount_point='mymount',
+                )
+        r = self.app.post('/p/{}/admin/src/_importer/create'.format(test_project_with_repo),
+                params,
+                status=302).follow()
+        self.assertIn('Please wait and try again', r)
+        self.assertEqual(import_tool.post.call_count, 0)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3c0a7e0f/ForgeImporters/forgeimporters/google/tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/google/tracker.py b/ForgeImporters/forgeimporters/google/tracker.py
index 9c259dd..d52945e 100644
--- a/ForgeImporters/forgeimporters/google/tracker.py
+++ b/ForgeImporters/forgeimporters/google/tracker.py
@@ -54,7 +54,7 @@ from forgeimporters.base import (
 @task(notifications_disabled=True)
 def import_tool(**kw):
     importer = GoogleCodeTrackerImporter()
-    with ImportErrorHandler(importer, kw.get('project_name')):
+    with ImportErrorHandler(importer, kw.get('project_name'), c.project):
         importer.import_tool(c.project, c.user, **kw)
 
 
@@ -81,13 +81,16 @@ class GoogleCodeTrackerImportController(BaseController):
     @require_post()
     @validate(GoogleCodeTrackerImportForm(ForgeTrackerApp), error_handler=index)
     def create(self, gc_project_name, mount_point, mount_label, **kw):
-        import_tool.post(
-                project_name=gc_project_name,
-                mount_point=mount_point,
-                mount_label=mount_label,
-                )
-        flash('Ticket import has begun. Your new tracker will be available '
-                'when the import is complete.')
+        if GoogleCodeTrackerImporter().enforce_limit(c.project):
+            import_tool.post(
+                    project_name=gc_project_name,
+                    mount_point=mount_point,
+                    mount_label=mount_label,
+                    )
+            flash('Ticket import has begun. Your new tracker will be available '
+                    'when the import is complete.')
+        else:
+            flash('There are too many imports pending at this time.  Please wait and try again.', 'error')
         redirect(c.project.url() + 'admin/')
 
 

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3c0a7e0f/ForgeImporters/forgeimporters/tests/google/test_tracker.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/tests/google/test_tracker.py b/ForgeImporters/forgeimporters/tests/google/test_tracker.py
index d795bab..98216f9 100644
--- a/ForgeImporters/forgeimporters/tests/google/test_tracker.py
+++ b/ForgeImporters/forgeimporters/tests/google/test_tracker.py
@@ -17,13 +17,16 @@
 
 from datetime import datetime
 from unittest import TestCase
+
 import mock
 from mock import patch
+from ming.odm import ThreadLocalORMSession
 
 from allura.tests import TestController
 from allura.tests.decorators import with_tracker
 
-from ...google import tracker
+from allura import model as M
+from forgeimporters.google import tracker
 
 
 class TestTrackerImporter(TestCase):
@@ -299,3 +302,18 @@ class TestGoogleCodeTrackerImportController(TestController, TestCase):
         self.assertEqual(u'mymount', import_tool.post.call_args[1]['mount_point'])
         self.assertEqual(u'mylabel', import_tool.post.call_args[1]['mount_label'])
         self.assertEqual(u'test', import_tool.post.call_args[1]['project_name'])
+
+    @with_tracker
+    @patch('forgeimporters.google.tracker.import_tool')
+    def test_create_limit(self, import_tool):
+        project = M.Project.query.get(shortname='test')
+        project.set_tool_data('GoogleCodeTrackerImporter', pending=1)
+        ThreadLocalORMSession.flush_all()
+        params = dict(gc_project_name='test',
+                mount_label='mylabel',
+                mount_point='mymount',
+                )
+        r = self.app.post('/p/test/admin/bugs/_importer/create', params,
+                status=302).follow()
+        self.assertIn('Please wait and try again', r)
+        self.assertEqual(import_tool.post.call_count, 0)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3c0a7e0f/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/tests/test_tickets.py b/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
index 92e7854..b3d2fcb 100644
--- a/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
+++ b/ForgeImporters/forgeimporters/trac/tests/test_tickets.py
@@ -18,12 +18,14 @@
 import json
 from unittest import TestCase
 from mock import Mock, patch
+from ming.orm import ThreadLocalORMSession
 
 from tg import config
 
 from allura.tests import TestController
 from allura.tests.decorators import with_tracker
 
+from allura import model as M
 from forgeimporters.trac.tickets import (
     TracTicketImporter,
     TracTicketImportController,
@@ -132,3 +134,19 @@ class TestTracTicketImportController(TestController, TestCase):
         self.assertEqual(u'mylabel', import_tool.post.call_args[1]['mount_label'])
         self.assertEqual('{"orig_user": "new_user"}', import_tool.post.call_args[1]['user_map'])
         self.assertEqual(u'http://example.com/trac/url', import_tool.post.call_args[1]['trac_url'])
+
+    @with_tracker
+    @patch('forgeimporters.trac.tickets.import_tool')
+    def test_create_limit(self, import_tool):
+        project = M.Project.query.get(shortname='test')
+        project.set_tool_data('TracTicketImporter', pending=1)
+        ThreadLocalORMSession.flush_all()
+        params = dict(trac_url='http://example.com/trac/url',
+                mount_label='mylabel',
+                mount_point='mymount',
+                )
+        r = self.app.post('/p/test/admin/bugs/_importer/create', params,
+                upload_files=[('user_map', 'myfile', '{"orig_user": "new_user"}')],
+                status=302).follow()
+        self.assertIn('Please wait and try again', r)
+        self.assertEqual(import_tool.post.call_count, 0)

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/3c0a7e0f/ForgeImporters/forgeimporters/trac/tickets.py
----------------------------------------------------------------------
diff --git a/ForgeImporters/forgeimporters/trac/tickets.py b/ForgeImporters/forgeimporters/trac/tickets.py
index 9e4f493..cafd403 100644
--- a/ForgeImporters/forgeimporters/trac/tickets.py
+++ b/ForgeImporters/forgeimporters/trac/tickets.py
@@ -61,7 +61,7 @@ from forgetracker.scripts.import_tracker import import_tracker
 @task(notifications_disabled=True)
 def import_tool(**kw):
     importer = TracTicketImporter()
-    with ImportErrorHandler(importer, kw.get('trac_url')):
+    with ImportErrorHandler(importer, kw.get('trac_url'), c.project):
         importer.import_tool(c.project, c.user, **kw)
 
 
@@ -89,13 +89,16 @@ class TracTicketImportController(BaseController):
     @require_post()
     @validate(TracTicketImportForm(ForgeTrackerApp), error_handler=index)
     def create(self, trac_url, mount_point, mount_label, user_map=None, **kw):
-        import_tool.post(
-                mount_point=mount_point,
-                mount_label=mount_label,
-                trac_url=trac_url,
-                user_map=user_map)
-        flash('Ticket import has begun. Your new tracker will be available '
-                'when the import is complete.')
+        if TracTicketImporter().enforce_limit(c.project):
+            import_tool.post(
+                    mount_point=mount_point,
+                    mount_label=mount_label,
+                    trac_url=trac_url,
+                    user_map=user_map)
+            flash('Ticket import has begun. Your new tracker will be available '
+                    'when the import is complete.')
+        else:
+            flash('There are too many imports pending at this time.  Please wait and try again.', 'error')
         redirect(c.project.url() + 'admin/')