Allow deletion of repositories
authorSybren A. Stüvel <sybren@stuvel.eu>
Fri, 3 Nov 2017 17:00:16 +0000 (18:00 +0100)
committerSybren A. Stüvel <sybren@stuvel.eu>
Fri, 3 Nov 2017 17:00:16 +0000 (18:00 +0100)
src/templates/svnman/project_settings/settings.pug
svnman/__init__.py
svnman/routes.py

index a7d2a70..2d7349f 100644 (file)
@@ -2,5 +2,29 @@
 
 | {% block svnman_container %}
 #node-edit-form
-       p nothing here yet.
+       #node-edit-form
+               p This project has a Subversion repository
+               p To do a checkout, use:
+               p
+                       code svn checkout {{ svn_url }} my_repo
+               p
+                       button.btn.btn-danger(onclick='deleteRepo()') Delete Subversion repository
+
 | {% endblock svnman_container %}
+
+| {% block footer_scripts %}
+script.
+       function deleteRepo() {
+               $.ajax({
+                       url: '{{ url_for( "svnman.delete_repo", project_url=project.url, repo_id=repo_id) }}',
+                       method: 'POST',
+               })
+               .done(function() {
+                       window.location.reload();
+               })
+               .fail(function(err) {
+                       var err_elt = xhrErrorResponseElement(err, 'Error deleting your repository: ');
+                       toastr.error(err_elt);
+               });
+       }
+| {% endblock %}
index 78c037c..e865797 100644 (file)
@@ -1,7 +1,7 @@
 import logging
 import os.path
+from urllib.parse import urljoin
 
-import bson
 import flask
 from werkzeug.local import LocalProxy
 
@@ -9,6 +9,7 @@ import pillarsdk
 from pillar.extension import PillarExtension
 from pillar.auth import current_user
 from pillar.api.projects import utils as proj_utils
+from pillar import current_app
 
 EXTENSION_NAME = 'svnman'
 
@@ -47,7 +48,7 @@ class SVNManExtension(PillarExtension):
         from . import cli
 
         return {
-            'SVNMAN_API_URL': 'http://configure-SVNMAN_API_URL/api/',
+            'SVNMAN_URL': 'http://SVNMAN_URL/',
             'SVNMAN_API_USERNAME': 'SVNMAN_API_USERNAME',
             'SVNMAN_API_PASSWORD': 'SVNMAN_API_PASSWORD',
         }
@@ -81,7 +82,7 @@ class SVNManExtension(PillarExtension):
         from . import remote
 
         self.remote = remote.API(
-            remote_url=app.config['SVNMAN_API_URL'],
+            remote_url=urljoin(app.config['SVNMAN_URL'], 'api/'),
             username=app.config['SVNMAN_API_USERNAME'],
             password=app.config['SVNMAN_API_PASSWORD'],
         )
@@ -115,7 +116,20 @@ class SVNManExtension(PillarExtension):
 
         from .routes import project_settings
 
-        return project_settings(project, **template_args)
+        remote_url = current_app.config['SVNMAN_URL']
+
+        if self.is_svnman_project(project):
+            repo_id = project.extension_props[EXTENSION_NAME].repo_id
+            svn_url = urljoin(remote_url, f'/repo/{repo_id}')
+        else:
+            svn_url = ''
+            repo_id = ''
+
+        return project_settings(project,
+                                svn_url=svn_url,
+                                repo_id=repo_id,
+                                remote_url=remote_url,
+                                **template_args)
 
     def is_svnman_project(self, project: pillarsdk.Project) -> bool:
         """Checks whether the project is correctly set up for SVNman."""
@@ -197,6 +211,29 @@ class SVNManExtension(PillarExtension):
 
         return repo_info.repo_id
 
+    def delete_repo(self, project_url: str, repo_id: str):
+        """Deletes an SVN repository and detaches it from the project."""
+
+        from . import remote, exceptions
+
+        proj = proj_utils.get_project(project_url)
+        project_id = proj['_id']
+        eprops = proj.setdefault('extension_props', {}).setdefault(EXTENSION_NAME, {})
+
+        proj_repo_id = eprops.get('repo_id')
+        if proj_repo_id != repo_id:
+            self._log.warning('project %s is linked to repo %r, not to %r, refusing to delete',
+                              project_id, proj_repo_id, repo_id)
+            raise ValueError()
+
+        self.remote.delete_repo(repo_id)
+        self._log.info('deleted Subversion repository %s', repo_id)
+
+        # Update the project to remove the repository ID.
+        eprops.pop('repo_id', None)
+        eprops.pop('access', None)
+        proj_utils.put_project(proj)
+
 
 def _get_current_svnman() -> SVNManExtension:
     """Returns the SVNMan extension of the current application."""
index b02659a..8983007 100644 (file)
@@ -54,11 +54,38 @@ def create_repo(project_url: str):
 
     from . import exceptions
 
+    # TODO(sybren): check project access
+
     try:
         current_svnman.create_repo(project_url, f'{current_user.full_name} <{current_user.email}>')
     except (OSError, IOError):
         log.exception('unable to reach SVNman API')
-        resp = jsonify(_message='unable to reach SVNman API')
+        resp = jsonify(_message='unable to reach SVNman API server')
+        resp.status_code = 500
+        return resp
+    except exceptions.RemoteError as ex:
+        log.error('API sent us an error: %s', ex)
+        resp = jsonify(_message=str(ex))
+        resp.status_code = 500
+        return resp
+    return '', 204
+
+
+@blueprint.route('/<project_url>/delete-repo/<repo_id>', methods=['POST'])
+@require_login(require_cap='svn-use')
+def delete_repo(project_url: str, repo_id: str):
+    log.info('going to delete repository %s for project url=%r on behalf of user %s (%s)',
+             repo_id, project_url, current_user.user_id, current_user.email)
+
+    from . import exceptions
+
+    # TODO(sybren): check project access
+
+    try:
+        current_svnman.delete_repo(project_url, repo_id)
+    except (OSError, IOError):
+        log.exception('unable to reach SVNman API')
+        resp = jsonify(_message='unable to reach SVNman API server')
         resp.status_code = 500
         return resp
     except exceptions.RemoteError as ex: