diff --git a/attract/cli.py b/attract/cli.py index f45a1e0..1edb6a1 100644 --- a/attract/cli.py +++ b/attract/cli.py @@ -12,7 +12,8 @@ log = logging.getLogger(__name__) @manager.command @manager.option('-r', '--replace', dest='replace', action='store_true', default=False) -def setup_for_attract(project_url, replace=False): +@manager.option('-s', '--svn', dest='svn_url', nargs='?') +def setup_for_attract(project_url, replace=False, svn_url=None): """Adds Attract node types to the project. Use --replace to replace pre-existing Attract node types @@ -20,4 +21,4 @@ def setup_for_attract(project_url, replace=False): """ authentication.force_cli_user() - attract.setup.setup_for_attract(project_url, replace=replace) + attract.setup.setup_for_attract(project_url, replace=replace, svn_url=svn_url) diff --git a/attract/modules.py b/attract/modules.py index fb369f7..1a4a07d 100644 --- a/attract/modules.py +++ b/attract/modules.py @@ -16,26 +16,6 @@ def index(): return render_template('attract/index.html') -@blueprint.route('/subversion/kick') -def subversion_kick(): - from . import subversion - - # TODO: each project should have its own SVN server. - svn_server_url = 'svn://localhost/agent327' - log.info('Re-examining SVN server %s', svn_server_url) - client = subversion.obtain(svn_server_url) - - # TODO: last_seen_revision should be stored, probably at the project level. - last_seen_revision = 0 - observer = subversion.CommitLogObserver(client, last_seen_revision=last_seen_revision) - observer.fetch_and_observe() - - return jsonify({ - 'previous_last_seen_revision': last_seen_revision, - 'last_seen_revision': observer.last_seen_revision, - }) - - def error_project_not_setup_for_attract(): return render_template('attract/errors/project_not_setup.html') @@ -90,3 +70,30 @@ def attract_project_view(extra_project_projections=None): return wrapper return decorator + + +@blueprint.route('//subversion/kick') +@attract_project_view() +def subversion_kick(project): + from . import subversion + + try: + pprops = project.extension_props.attract + except AttributeError: + log.warning("subversion_kick(): Project url=%s doesn't have Attract extension properties.", + project.url) + return error_project_not_setup_for_attract() + + svn_server_url = pprops.svn_url # 'svn://localhost/agent327' + log.info('Re-examining SVN server %s', svn_server_url) + client = subversion.obtain(svn_server_url) + + # TODO: last_seen_revision should be stored, probably at the project level. + last_seen_revision = 0 + observer = subversion.CommitLogObserver(client, last_seen_revision=last_seen_revision) + observer.fetch_and_observe() + + return jsonify({ + 'previous_last_seen_revision': last_seen_revision, + 'last_seen_revision': observer.last_seen_revision, + }) diff --git a/attract/setup.py b/attract/setup.py index 92f6d6f..9dcc6a6 100644 --- a/attract/setup.py +++ b/attract/setup.py @@ -12,6 +12,8 @@ from bson import ObjectId from eve.methods.put import put_internal from flask import current_app +from . import EXTENSION_NAME + log = logging.getLogger(__name__) @@ -53,7 +55,7 @@ def _update_project(project): raise RuntimeError("Can't update project %s, issues: %s", project_id, result) -def setup_for_attract(project_url, replace=False): +def setup_for_attract(project_url, replace=False, svn_url=None): """Adds Attract node types to the project. Use --replace to replace pre-existing Attract node types @@ -105,6 +107,15 @@ def setup_for_attract(project_url, replace=False): project['node_types'].append(node_type) + # Set default extension properties. Be careful not to overwrite any properties that + # are already there. + eprops = project.setdefault('extension_props', {}) + attract_props = eprops.setdefault(EXTENSION_NAME, {}) + + if svn_url: + log.info('Setting SVN URL to %s', svn_url) + attract_props['svn_url'] = svn_url + _update_project(project) log.info('Project %s was updated for Attract.', project_url) diff --git a/tests/abstract_attract_test.py b/tests/abstract_attract_test.py index f8b48b7..0af98fe 100644 --- a/tests/abstract_attract_test.py +++ b/tests/abstract_attract_test.py @@ -3,6 +3,8 @@ from pillar.tests import PillarTestServer, AbstractPillarTest from attract import AttractExtension from attract.setup import setup_for_attract +MOCK_SVN_URL = 'svn://biserver/mocked' + class AttractTestServer(PillarTestServer): def __init__(self, *args, **kwargs): @@ -23,6 +25,8 @@ class AbstractAttractTest(AbstractPillarTest): proj_id, project = AbstractPillarTest.ensure_project_exists(self, project_overrides) with self.app.test_request_context(): - attract_project = setup_for_attract(project['url'], replace=True) + attract_project = setup_for_attract(project['url'], + replace=True, + svn_url=MOCK_SVN_URL) return proj_id, attract_project diff --git a/tests/test_setup_for_attract.py b/tests/test_setup_for_attract.py new file mode 100644 index 0000000..374402e --- /dev/null +++ b/tests/test_setup_for_attract.py @@ -0,0 +1,17 @@ +from abstract_attract_test import AbstractAttractTest, MOCK_SVN_URL + + +class TaskWorkflowTest(AbstractAttractTest): + def setUp(self, **kwargs): + AbstractAttractTest.setUp(self, **kwargs) + self.project_id, _ = self.ensure_project_exists() + + def test_custom_properties(self): + """Projects should get their properties dict.""" + + with self.app.test_request_context(): + proj_coll = self.app.data.driver.db['projects'] + project = proj_coll.find_one({'_id': self.project_id}) + aprops = project['extension_props']['attract'] + self.assertIsInstance(aprops, dict) + self.assertEqual(MOCK_SVN_URL, aprops['svn_url'])