incubator-allura-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From john...@apache.org
Subject [5/5] git commit: [#6692] Added API for starting and checking status of project export
Date Thu, 26 Sep 2013 18:13:45 GMT
[#6692] Added API for starting and checking status of project export

Signed-off-by: Cory Johns <cjohns@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/65157663
Tree: http://git-wip-us.apache.org/repos/asf/incubator-allura/tree/65157663
Diff: http://git-wip-us.apache.org/repos/asf/incubator-allura/diff/65157663

Branch: refs/heads/cj/6692
Commit: 65157663fa42d4f0f867555471a150c9357e774f
Parents: cf718c8
Author: Cory Johns <cjohns@slashdotmedia.com>
Authored: Thu Sep 26 18:12:51 2013 +0000
Committer: Cory Johns <cjohns@slashdotmedia.com>
Committed: Thu Sep 26 18:13:23 2013 +0000

----------------------------------------------------------------------
 Allura/allura/ext/admin/admin_main.py        | 37 +++++++++++++++++++-
 Allura/allura/tasks/export_tasks.py          | 17 ++++++----
 Allura/allura/tests/functional/test_admin.py | 41 +++++++++++++++++++++++
 AlluraTest/alluratest/controller.py          | 14 ++++----
 4 files changed, 96 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/65157663/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 314c3a9..aa13d51 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -20,11 +20,12 @@ from collections import defaultdict
 from datetime import datetime
 from urlparse import urlparse
 import json
+import os
 
 import pkg_resources
 from pylons import tmpl_context as c, app_globals as g
 from pylons import request
-from paste.deploy.converters import asbool
+from paste.deploy.converters import asbool, aslist
 from tg import expose, redirect, flash, validate, config, jsonify
 from tg.decorators import with_trailing_slash, without_trailing_slash
 from webob import exc
@@ -86,6 +87,7 @@ class AdminApp(Application):
     def __init__(self, project, config):
         Application.__init__(self, project, config)
         self.root = ProjectAdminController()
+        self.api_root = ProjectAdminRestController()
         self.admin = AdminAppAdminController(self)
         self.templates = pkg_resources.resource_filename('allura.ext.admin', 'templates')
         self.sitemap = [ SitemapEntry('Admin','.')]
@@ -662,6 +664,39 @@ class ProjectAdminController(BaseController):
         }
 
 
+class ProjectAdminRestController(BaseController):
+
+    def _check_security(self):
+        require_access(c.project, 'admin')
+
+    @expose('json:')
+    @require_post()
+    def export(self, tools=None, **kw):
+        if not tools:
+            raise exc.HTTPBadRequest('Must give at least one tool mount point to export')
+        tools = aslist(tools,',')
+        exportable_tools = AdminApp.exportable_tools_for(c.project)
+        allowed = set(t.options.mount_point for t in exportable_tools)
+        if not set(tools).issubset(allowed):
+            raise exc.HTTPBadRequest('Invalid tool')
+        if c.project.bulk_export_status() == 'busy':
+            raise exc.HTTPServiceUnavailable(
+                'Export for project %s already running' % c.project.shortname)
+        # filename (potentially) includes a timestamp, so we have
+        # to pre-generate to be able to return it to the user
+        filename = c.project.bulk_export_filename()
+        export_tasks.bulk_export.post(tools, filename)
+        return {
+                'status': 'in progress',
+                'filename': filename,
+            }
+
+    @expose('json:')
+    def export_status(self, **kw):
+        status = c.project.bulk_export_status()
+        return {'status': status or 'ready'}
+
+
 class PermissionsController(BaseController):
 
     def _check_security(self):

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/65157663/Allura/allura/tasks/export_tasks.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tasks/export_tasks.py b/Allura/allura/tasks/export_tasks.py
index ecd538c..e1de90d 100644
--- a/Allura/allura/tasks/export_tasks.py
+++ b/Allura/allura/tasks/export_tasks.py
@@ -35,19 +35,20 @@ log = logging.getLogger(__name__)
 
 
 @task
-def bulk_export(tools):
+def bulk_export(tools, filename=None):
     '''
-    Export the current project data.  Send notification to current user
+    Export the current project data.  Send notification to current user.
 
     :param list tools: list of mount_points to export
+    :param str filename: optional filename to use
     '''
     # it's very handy to use c.* within a @task,
     # but let's be explicit and keep it separate from the main code
-    return _bulk_export(c.project, tools, c.user)
+    return _bulk_export(c.project, tools, c.user, filename)
 
 
-def _bulk_export(project, tools, user):
-    export_filename = project.bulk_export_filename()
+def _bulk_export(project, tools, user, filename=None):
+    export_filename = filename or project.bulk_export_filename()
     export_path = create_export_dir(project, export_filename)
     not_exported_tools = []
     for tool in tools or []:
@@ -83,7 +84,11 @@ def _bulk_export(project, tools, user):
         return
     tmpl = g.jinja2_env.get_template('allura:templates/mail/bulk_export.html')
     instructions = tg.config.get('bulk_export_download_instructions', '')
-    instructions = instructions.format(project=project.shortname, filename=export_filename,
c=c)
+    instructions = instructions.format(
+            project=project.shortname,
+            filename=export_filename,
+            c=c,
+        )
     tmpl_context = {
         'instructions': instructions,
         'project': project,

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/65157663/Allura/allura/tests/functional/test_admin.py
----------------------------------------------------------------------
diff --git a/Allura/allura/tests/functional/test_admin.py b/Allura/allura/tests/functional/test_admin.py
index 66cffba..4d3efb5 100644
--- a/Allura/allura/tests/functional/test_admin.py
+++ b/Allura/allura/tests/functional/test_admin.py
@@ -38,6 +38,7 @@ except ImportError:
 
 from allura.tests import TestController
 from allura.tests import decorators as td
+from alluratest.controller import TestRestApiBase
 from allura import model as M
 from allura.app import SitemapEntry
 from allura.lib.plugin import AdminExtension
@@ -965,3 +966,43 @@ class TestExport(TestController):
         r = self.app.get('/admin/export')
         assert_in('<input type="checkbox" id="check-all">', r)
         assert_in('Check All</label>', r)
+
+
+class TestRestExport(TestRestApiBase):
+    @mock.patch('allura.model.project.MonQTask')
+    def test_export_status(self, MonQTask):
+        MonQTask.query.get.return_value = None
+        r = self.api_get('/rest/p/test/admin/export_status')
+        assert_equals(r.json, {'status': 'ready'})
+
+        MonQTask.query.get.return_value = 'something'
+        r = self.api_get('/rest/p/test/admin/export_status')
+        assert_equals(r.json, {'status': 'busy'})
+
+    @mock.patch('allura.model.project.MonQTask')
+    @mock.patch('allura.ext.admin.admin_main.AdminApp.exportable_tools_for')
+    @mock.patch('allura.ext.admin.admin_main.export_tasks.bulk_export')
+    def test_export(self, bulk_export, exportable_tools, MonQTask):
+        MonQTask.query.get.return_value = None
+        exportable_tools.return_value = []
+        r = self.api_post('/rest/p/test/admin/export', tools='tickets, discussion', status=400)
+        assert_equals(bulk_export.post.call_count, 0)
+
+        exportable_tools.return_value = [
+                mock.Mock(options=mock.Mock(mount_point='tickets')),
+                mock.Mock(options=mock.Mock(mount_point='discussion')),
+            ]
+        r = self.api_post('/rest/p/test/admin/export', status=400)
+        assert_equals(bulk_export.post.call_count, 0)
+
+        MonQTask.query.get.return_value = 'something'
+        r = self.api_post('/rest/p/test/admin/export', tools='tickets, discussion', status=503)
+        assert_equals(bulk_export.post.call_count, 0)
+
+        MonQTask.query.get.return_value = None
+        r = self.api_post('/rest/p/test/admin/export', tools='tickets, discussion', status=200)
+        assert_equals(r.json, {
+                'filename': 'test.zip',
+                'status': 'in progress',
+            })
+        bulk_export.post.assert_called_once_with(['tickets', 'discussion'], 'test.zip')

http://git-wip-us.apache.org/repos/asf/incubator-allura/blob/65157663/AlluraTest/alluratest/controller.py
----------------------------------------------------------------------
diff --git a/AlluraTest/alluratest/controller.py b/AlluraTest/alluratest/controller.py
index 577088d..e7b51e8 100644
--- a/AlluraTest/alluratest/controller.py
+++ b/AlluraTest/alluratest/controller.py
@@ -165,9 +165,11 @@ class TestRestApiBase(TestController):
         return self._token_cache[username]
 
     def _api_getpost(self, method, path, api_key=None, api_timestamp=None, api_signature=None,
-                 wrap_args=None, user='test-admin', **params):
+                 wrap_args=None, user='test-admin', status=None, **params):
         if wrap_args:
             params = {wrap_args: params}
+        if status is None:
+            status = [200, 201, 302, 400, 403, 404]
         params = variabledecode.variable_encode(params, add_repetitions=False)
         if api_key: params['api_key'] = api_key
         if api_timestamp: params['api_timestamp'] = api_timestamp
@@ -180,16 +182,16 @@ class TestRestApiBase(TestController):
         response = fn(
             str(path),
             params=params,
-            status=[200, 201, 302, 400, 403, 404])
+            status=status)
         if response.status_int == 302:
             return response.follow()
         else:
             return response
 
     def api_get(self, path, api_key=None, api_timestamp=None, api_signature=None,
-                 wrap_args=None, user='test-admin', **params):
-        return self._api_getpost('GET', path, api_key, api_timestamp, api_signature, wrap_args,
user, **params)
+                 wrap_args=None, user='test-admin', status=None, **params):
+        return self._api_getpost('GET', path, api_key, api_timestamp, api_signature, wrap_args,
user, status, **params)
 
     def api_post(self, path, api_key=None, api_timestamp=None, api_signature=None,
-                 wrap_args=None, user='test-admin', **params):
-        return self._api_getpost('POST', path, api_key, api_timestamp, api_signature, wrap_args,
user, **params)
+                 wrap_args=None, user='test-admin', status=None, **params):
+        return self._api_getpost('POST', path, api_key, api_timestamp, api_signature, wrap_args,
user, status, **params)


Mime
View raw message