Skip to content

Commit b0fbf65

Browse files
committed
Add form validation for the issue_tracker_id value #350
Signed-off-by: tdruez <[email protected]>
1 parent c66b5f1 commit b0fbf65

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed

workflow/admin.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@
1111
from django.contrib import messages
1212
from django.contrib.admin.utils import unquote
1313
from django.core.exceptions import PermissionDenied
14+
from django.core.exceptions import ValidationError
1415
from django.template.defaultfilters import pluralize
1516
from django.utils.translation import gettext as _
1617

1718
from dje.admin import DataspacedAdmin
1819
from dje.admin import dejacode_site
1920
from dje.forms import DataspacedAdminForm
2021
from workflow.inlines import QuestionInline
22+
from workflow.integrations import is_valid_issue_tracker_id
2123
from workflow.models import Priority
2224
from workflow.models import RequestTemplate
2325

@@ -56,6 +58,21 @@ class PriorityAdmin(DataspacedAdmin):
5658
save_as = False
5759

5860

61+
class RequestTemplateAdminForm(DataspacedAdminForm):
62+
def clean_issue_tracker_id(self):
63+
issue_tracker_id = self.cleaned_data.get("issue_tracker_id")
64+
if issue_tracker_id and not is_valid_issue_tracker_id(issue_tracker_id):
65+
raise ValidationError(
66+
[
67+
"Invalid issue tracker URL format. Supported formats include:",
68+
"• GitHub: https://github.com/ORG/REPO_NAME",
69+
"• GitLab: https://gitlab.com/GROUP/PROJECT_NAME",
70+
"• Jira: https://YOUR_DOMAIN.atlassian.net/projects/PROJECTKEY",
71+
]
72+
)
73+
return issue_tracker_id
74+
75+
5976
@admin.register(RequestTemplate, site=dejacode_site)
6077
class RequestTemplateAdmin(DataspacedAdmin):
6178
list_display = (
@@ -75,6 +92,7 @@ class RequestTemplateAdmin(DataspacedAdmin):
7592
"include_applies_to",
7693
"include_product",
7794
)
95+
form = RequestTemplateAdminForm
7896
inlines = (QuestionInline,)
7997
actions = [
8098
"copy_to",

workflow/integrations/__init__.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
# See https://aboutcode.org for more information about AboutCode FOSS projects.
77
#
88

9+
import re
10+
911
from workflow.integrations.base import BaseIntegration
1012
from workflow.integrations.github import GitHubIntegration
1113
from workflow.integrations.gitlab import GitLabIntegration
@@ -16,9 +18,31 @@
1618
"GitHubIntegration",
1719
"GitLabIntegration",
1820
"JiraIntegration",
21+
"is_valid_issue_tracker_id",
22+
"get_class_for_tracker",
23+
"get_class_for_platform",
1924
]
2025

2126

27+
GITHUB_PATTERN = re.compile(r"^https://github\.com/[^/]+/[^/]+/?$")
28+
29+
GITLAB_PATTERN = re.compile(r"^https://gitlab\.com/[^/]+/[^/]+/?$")
30+
31+
JIRA_PATTERN = re.compile(
32+
r"^https://[a-zA-Z0-9.-]+\.atlassian\.net/(?:projects|jira/software/projects)/[A-Z][A-Z0-9]+"
33+
)
34+
35+
ISSUE_TRACKER_PATTERNS = [
36+
GITHUB_PATTERN,
37+
GITLAB_PATTERN,
38+
JIRA_PATTERN,
39+
]
40+
41+
42+
def is_valid_issue_tracker_id(issue_tracker_id):
43+
return any(pattern.match(issue_tracker_id) for pattern in ISSUE_TRACKER_PATTERNS)
44+
45+
2246
def get_class_for_tracker(issue_tracker_id):
2347
if "github.com" in issue_tracker_id:
2448
return GitHubIntegration

workflow/tests/test_integrations.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,54 @@
1414

1515
from dje.models import Dataspace
1616
from dje.tests import create_superuser
17+
from workflow.integrations import JiraIntegration
18+
from workflow.integrations import get_class_for_platform
19+
from workflow.integrations import get_class_for_tracker
20+
from workflow.integrations import is_valid_issue_tracker_id
1721
from workflow.integrations.github import GitHubIntegration
1822
from workflow.integrations.gitlab import GitLabIntegration
1923
from workflow.models import Question
2024
from workflow.models import RequestTemplate
2125

2226

27+
class WorkflowIntegrationsTestCase(TestCase):
28+
def test_is_valid_issue_tracker_id(self):
29+
valid_urls = [
30+
"https://github.com/org/repo",
31+
"https://gitlab.com/group/project",
32+
"https://aboutcode.atlassian.net/projects/PROJ",
33+
"https://aboutcode.atlassian.net/jira/software/projects/PROJ",
34+
]
35+
for url in valid_urls:
36+
with self.subTest(url=url):
37+
self.assertTrue(is_valid_issue_tracker_id(url))
38+
39+
invalid_urls = [
40+
"https://bitbucket.org/team/repo",
41+
"https://github.com/",
42+
"https://gitlab.com/",
43+
"https://atlassian.net/projects/",
44+
"https://example.com",
45+
]
46+
for url in invalid_urls:
47+
with self.subTest(url=url):
48+
self.assertFalse(is_valid_issue_tracker_id(url))
49+
50+
def test_get_class_for_tracker(self):
51+
self.assertIs(get_class_for_tracker("https://github.com/org/repo"), GitHubIntegration)
52+
self.assertIs(get_class_for_tracker("https://gitlab.com/group/project"), GitLabIntegration)
53+
self.assertIs(
54+
get_class_for_tracker("https://aboutcode.atlassian.net/projects/PROJ"), JiraIntegration
55+
)
56+
self.assertIsNone(get_class_for_tracker("https://example.com"))
57+
58+
def test_get_class_for_platform(self):
59+
self.assertIs(get_class_for_platform("github"), GitHubIntegration)
60+
self.assertIs(get_class_for_platform("gitlab"), GitLabIntegration)
61+
self.assertIs(get_class_for_platform("jira"), JiraIntegration)
62+
self.assertIsNone(get_class_for_platform("example"))
63+
64+
2365
class GitHubIntegrationTestCase(TestCase):
2466
def setUp(self):
2567
patcher = mock.patch("workflow.models.Request.handle_integrations", return_value=None)

0 commit comments

Comments
 (0)