Adding API support for project modules configurations
parent
0fd7142802
commit
88d11aba85
|
@ -338,6 +338,14 @@ FEEDBACK_EMAIL = "support@taiga.io"
|
||||||
# collapsed during that interval
|
# collapsed during that interval
|
||||||
CHANGE_NOTIFICATIONS_MIN_INTERVAL = 0 #seconds
|
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
|
# NOTE: DON'T INSERT MORE SETTINGS AFTER THIS LINE
|
||||||
TEST_RUNNER="django.test.runner.DiscoverRunner"
|
TEST_RUNNER="django.test.runner.DiscoverRunner"
|
||||||
|
|
||||||
|
|
|
@ -16,23 +16,24 @@
|
||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from taiga.projects.models import ProjectModulesConfig
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
from taiga.users.models import User
|
from taiga.users.models import User
|
||||||
|
from taiga.base.utils.urls import get_absolute_url
|
||||||
|
|
||||||
|
|
||||||
def set_default_config(project):
|
def get_config_or_default(project):
|
||||||
if hasattr(project, "modules_config"):
|
config = project.modules_config.config
|
||||||
if project.modules_config.config is None:
|
if config and "github" in config:
|
||||||
project.modules_config.config = {"github": {"secret": uuid.uuid4().hex }}
|
g_config = project.modules_config.config["github"]
|
||||||
else:
|
|
||||||
project.modules_config.config["github"] = {"secret": uuid.uuid4().hex }
|
|
||||||
else:
|
else:
|
||||||
project.modules_config = ProjectModulesConfig(project=project, config={
|
g_config = {"secret": uuid.uuid4().hex }
|
||||||
"github": {
|
|
||||||
"secret": uuid.uuid4().hex
|
url = reverse("github-hook-list")
|
||||||
}
|
url = get_absolute_url(url)
|
||||||
})
|
url = "%s?project=%s"%(url, project.id)
|
||||||
project.modules_config.save()
|
g_config["webhooks_url"] = url
|
||||||
|
return g_config
|
||||||
|
|
||||||
|
|
||||||
def get_github_user(user_id):
|
def get_github_user(user_id):
|
||||||
|
|
|
@ -60,6 +60,20 @@ class ProjectViewSet(ModelCrudViewSet):
|
||||||
qs = models.Project.objects.all()
|
qs = models.Project.objects.all()
|
||||||
return attach_votescount_to_queryset(qs, as_field="stars_count")
|
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'])
|
@detail_route(methods=['get'])
|
||||||
def stats(self, request, pk=None):
|
def stats(self, request, pk=None):
|
||||||
project = self.get_object()
|
project = self.get_object()
|
||||||
|
|
|
@ -24,6 +24,7 @@ class ProjectPermission(TaigaResourcePermission):
|
||||||
create_perms = IsAuthenticated()
|
create_perms = IsAuthenticated()
|
||||||
update_perms = IsProjectOwner()
|
update_perms = IsProjectOwner()
|
||||||
destroy_perms = IsProjectOwner()
|
destroy_perms = IsProjectOwner()
|
||||||
|
modules_perms = IsProjectOwner()
|
||||||
list_perms = AllowAny()
|
list_perms = AllowAny()
|
||||||
stats_perms = AllowAny()
|
stats_perms = AllowAny()
|
||||||
star_perms = IsAuthenticated()
|
star_perms = IsAuthenticated()
|
||||||
|
|
|
@ -182,13 +182,14 @@ class ProjectSerializer(ModelSerializer):
|
||||||
def validate_total_milestones(self, attrs, source):
|
def validate_total_milestones(self, attrs, source):
|
||||||
"""
|
"""
|
||||||
Check that total_milestones is not null, it's an optional parameter but
|
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]
|
value = attrs[source]
|
||||||
if value is None:
|
if value is None:
|
||||||
raise serializers.ValidationError("Total milestones must be major or equal to zero")
|
raise serializers.ValidationError("Total milestones must be major or equal to zero")
|
||||||
return attrs
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
class ProjectDetailSerializer(ProjectSerializer):
|
class ProjectDetailSerializer(ProjectSerializer):
|
||||||
roles = serializers.SerializerMethodField("get_roles")
|
roles = serializers.SerializerMethodField("get_roles")
|
||||||
memberships = serializers.SerializerMethodField("get_memberships")
|
memberships = serializers.SerializerMethodField("get_memberships")
|
||||||
|
|
|
@ -38,3 +38,5 @@ from .invitations import send_invitation
|
||||||
from .invitations import find_invited_user
|
from .invitations import find_invited_user
|
||||||
|
|
||||||
from .tags_colors import update_project_tags_colors_handler
|
from .tags_colors import update_project_tags_colors_handler
|
||||||
|
|
||||||
|
from .modules_config import get_modules_config
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
|
||||||
|
# Copyright (C) 2014 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014 David Barragán <bameda@dbarragan.com>
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
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
|
|
@ -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.history.services import get_history_queryset_by_model_instance, take_snapshot
|
||||||
from taiga.projects.notifications.choices import NotifyLevel
|
from taiga.projects.notifications.choices import NotifyLevel
|
||||||
from taiga.projects.notifications.models import NotifyPolicy
|
from taiga.projects.notifications.models import NotifyPolicy
|
||||||
|
from taiga.projects import services
|
||||||
from .. import factories as f
|
from .. import factories as f
|
||||||
|
|
||||||
pytestmark = pytest.mark.django_db
|
pytestmark = pytest.mark.django_db
|
||||||
|
@ -345,3 +345,38 @@ def test_issues_event_bad_comment(client):
|
||||||
|
|
||||||
assert Issue.objects.count() == 1
|
assert Issue.objects.count() == 1
|
||||||
assert len(mail.outbox) == 0
|
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"
|
||||||
|
|
Loading…
Reference in New Issue