From 88d11aba8509d4fa8a403ad5e40b284d413b1a2d Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 5 Nov 2014 08:49:56 +0100 Subject: [PATCH] Adding API support for project modules configurations --- settings/common.py | 8 +++++ taiga/github_hook/services.py | 27 +++++++++-------- taiga/projects/api.py | 14 +++++++++ taiga/projects/permissions.py | 1 + taiga/projects/serializers.py | 3 +- taiga/projects/services/__init__.py | 2 ++ taiga/projects/services/modules_config.py | 36 ++++++++++++++++++++++ tests/integration/test_github_hook.py | 37 ++++++++++++++++++++++- 8 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 taiga/projects/services/modules_config.py diff --git a/settings/common.py b/settings/common.py index 57cc50dd..5d62635d 100644 --- a/settings/common.py +++ b/settings/common.py @@ -338,6 +338,14 @@ FEEDBACK_EMAIL = "support@taiga.io" # collapsed during that interval CHANGE_NOTIFICATIONS_MIN_INTERVAL = 0 #seconds + +# List of functions called for filling correctly the ProjectModulesConfig associated to a project +# This functions should receive a Project parameter and return a dict with the desired configuration +PROJECT_MODULES_CONFIGURATORS = { + "github": "taiga.github_hook.services.get_config_or_default", +} + + # NOTE: DON'T INSERT MORE SETTINGS AFTER THIS LINE TEST_RUNNER="django.test.runner.DiscoverRunner" diff --git a/taiga/github_hook/services.py b/taiga/github_hook/services.py index 96191709..99b5df33 100644 --- a/taiga/github_hook/services.py +++ b/taiga/github_hook/services.py @@ -16,23 +16,24 @@ import uuid -from taiga.projects.models import ProjectModulesConfig +from django.core.urlresolvers import reverse + from taiga.users.models import User +from taiga.base.utils.urls import get_absolute_url -def set_default_config(project): - if hasattr(project, "modules_config"): - if project.modules_config.config is None: - project.modules_config.config = {"github": {"secret": uuid.uuid4().hex }} - else: - project.modules_config.config["github"] = {"secret": uuid.uuid4().hex } +def get_config_or_default(project): + config = project.modules_config.config + if config and "github" in config: + g_config = project.modules_config.config["github"] else: - project.modules_config = ProjectModulesConfig(project=project, config={ - "github": { - "secret": uuid.uuid4().hex - } - }) - project.modules_config.save() + g_config = {"secret": uuid.uuid4().hex } + + url = reverse("github-hook-list") + url = get_absolute_url(url) + url = "%s?project=%s"%(url, project.id) + g_config["webhooks_url"] = url + return g_config def get_github_user(user_id): diff --git a/taiga/projects/api.py b/taiga/projects/api.py index c51673c0..f291a994 100644 --- a/taiga/projects/api.py +++ b/taiga/projects/api.py @@ -60,6 +60,20 @@ class ProjectViewSet(ModelCrudViewSet): qs = models.Project.objects.all() return attach_votescount_to_queryset(qs, as_field="stars_count") + @detail_route(methods=["GET", "PATCH"]) + def modules(self, request, pk=None): + project = self.get_object() + self.check_permissions(request, 'modules', project) + modules_config = services.get_modules_config(project) + + if request.method == "GET": + return Response(modules_config.config) + + else: + modules_config.config.update(request.DATA) + modules_config.save() + return Response(status=status.HTTP_204_NO_CONTENT) + @detail_route(methods=['get']) def stats(self, request, pk=None): project = self.get_object() diff --git a/taiga/projects/permissions.py b/taiga/projects/permissions.py index d1364f8e..89c9476a 100644 --- a/taiga/projects/permissions.py +++ b/taiga/projects/permissions.py @@ -24,6 +24,7 @@ class ProjectPermission(TaigaResourcePermission): create_perms = IsAuthenticated() update_perms = IsProjectOwner() destroy_perms = IsProjectOwner() + modules_perms = IsProjectOwner() list_perms = AllowAny() stats_perms = AllowAny() star_perms = IsAuthenticated() diff --git a/taiga/projects/serializers.py b/taiga/projects/serializers.py index 60a63636..613fbc02 100644 --- a/taiga/projects/serializers.py +++ b/taiga/projects/serializers.py @@ -182,13 +182,14 @@ class ProjectSerializer(ModelSerializer): def validate_total_milestones(self, attrs, source): """ Check that total_milestones is not null, it's an optional parameter but - not nullable in the model. + not nullable in the model. """ value = attrs[source] if value is None: raise serializers.ValidationError("Total milestones must be major or equal to zero") return attrs + class ProjectDetailSerializer(ProjectSerializer): roles = serializers.SerializerMethodField("get_roles") memberships = serializers.SerializerMethodField("get_memberships") diff --git a/taiga/projects/services/__init__.py b/taiga/projects/services/__init__.py index cd394f9a..35341f8d 100644 --- a/taiga/projects/services/__init__.py +++ b/taiga/projects/services/__init__.py @@ -38,3 +38,5 @@ from .invitations import send_invitation from .invitations import find_invited_user from .tags_colors import update_project_tags_colors_handler + +from .modules_config import get_modules_config diff --git a/taiga/projects/services/modules_config.py b/taiga/projects/services/modules_config.py new file mode 100644 index 00000000..4b1cbae8 --- /dev/null +++ b/taiga/projects/services/modules_config.py @@ -0,0 +1,36 @@ +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino +# Copyright (C) 2014 David Barragán +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# 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 Affero 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 . + +import importlib + +from .. import models +from django.conf import settings + + +def get_modules_config(project): + modules_config, created = models.ProjectModulesConfig.objects.get_or_create(project=project) + + if created: + modules_config.config = {} + + for key, configurator_function_name in settings.PROJECT_MODULES_CONFIGURATORS.items(): + mod_name, func_name = configurator_function_name.rsplit('.',1) + mod = importlib.import_module(mod_name) + configurator = getattr(mod, func_name) + modules_config.config[key] = configurator(project) + + modules_config.save() + return modules_config diff --git a/tests/integration/test_github_hook.py b/tests/integration/test_github_hook.py index 9c65b735..d218c6eb 100644 --- a/tests/integration/test_github_hook.py +++ b/tests/integration/test_github_hook.py @@ -16,7 +16,7 @@ from taiga.projects.models import Membership from taiga.projects.history.services import get_history_queryset_by_model_instance, take_snapshot from taiga.projects.notifications.choices import NotifyLevel from taiga.projects.notifications.models import NotifyPolicy - +from taiga.projects import services from .. import factories as f pytestmark = pytest.mark.django_db @@ -345,3 +345,38 @@ def test_issues_event_bad_comment(client): assert Issue.objects.count() == 1 assert len(mail.outbox) == 0 + + +def test_api_get_project_modules(client): + project = f.create_project() + + url = reverse("projects-modules", args=(project.id,)) + + client.login(project.owner) + response = client.get(url) + assert response.status_code == 200 + content = json.loads(response.content.decode("utf-8")) + assert "github" in content + assert content["github"]["secret"] != "" + assert content["github"]["webhooks_url"] != "" + + +def test_api_patch_project_modules(client): + project = f.create_project() + + url = reverse("projects-modules", args=(project.id,)) + + client.login(project.owner) + data = { + "github": { + "secret": "test_secret", + "url": "test_url", + } + } + response = client.patch(url, json.dumps(data), content_type="application/json") + assert response.status_code == 204 + + config = services.get_modules_config(project).config + assert "github" in config + assert config["github"]["secret"] == "test_secret" + assert config["github"]["webhooks_url"] != "test_url"