Skip to content

Commit

Permalink
add new endpoints for filtering and updating tasks
Browse files Browse the repository at this point in the history
Signed-off-by: Conrado Costa <[email protected]>
  • Loading branch information
costaconrado committed Oct 24, 2023
1 parent 90b5a68 commit 96ad07d
Show file tree
Hide file tree
Showing 13 changed files with 10,261 additions and 480 deletions.
390 changes: 227 additions & 163 deletions apps/taskman/api.py

Large diffs are not rendered by default.

85 changes: 85 additions & 0 deletions apps/taskman/jira_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,89 @@
"""
from rest_framework import serializers

from apps.osim.workflow import WorkflowModel

from .models import WORKFLOW_TO_JIRA_STATUS, JiraResolution, JiraStatus, Task


class TaskSerializer(serializers.ModelSerializer):
state = serializers.ChoiceField(
choices=WorkflowModel.OSIMState.choices, write_only=True, required=False
)

class Meta:
model = Task
fields = (
"uuid",
"state",
"jira_key",
"jira_group_key",
"team_id",
"owner",
"flaw",
)
read_only_fields = ("jira_key",)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# flaw is required during creation
if self.context.get("request") and self.context["request"].method == "POST":
self.fields["flaw"].required = True
else:
self.fields["flaw"].required = False

def create(self, validated_data):
state = validated_data.pop("state", WorkflowModel.OSIMState.NEW)
task = Task(**validated_data)

jira_status, jira_resolution = WORKFLOW_TO_JIRA_STATUS.get(
state, (JiraStatus.NEW, JiraResolution.NONE)
)

task.status = jira_status
task.resolution = jira_resolution

task.save()
return task

def update(self, instance, validated_data):
state = validated_data.pop("state", instance.workflow_state)

jira_status, jira_resolution = WORKFLOW_TO_JIRA_STATUS.get(
state, (JiraStatus.NEW, JiraResolution.NONE)
)

for attr, value in validated_data.items():
setattr(instance, attr, value)

instance.status = jira_status
instance.resolution = jira_resolution

instance.save()
return instance

def to_representation(self, instance):
# Map JiraStatus and JiraResolution back to OSIMState in the response
return {
"uuid": instance.uuid,
"state": instance.workflow_state,
"jira_key": instance.jira_key,
"jira_group_key": instance.jira_group_key,
"team_id": instance.team_id,
"owner": instance.owner,
"flaw": instance.flaw_id,
}

def validate_state(self, value):
current_state = self.instance.workflow_state if self.instance else None
if value == WorkflowModel.OSIMState.REJECTED and (
not current_state or current_state != WorkflowModel.OSIMState.REJECTED
):
raise serializers.ValidationError(
"Rejecting a task requires reasoning, use proper endpoint."
)
return value


class JiraIssueTypeSerializer(serializers.Serializer):
"""
Expand Down Expand Up @@ -46,6 +129,8 @@ class JiraIssueSerializer(serializers.Serializer):


class JiraIssueQueryResultSerializer(serializers.Serializer):
startAt = serializers.IntegerField()
maxResults = serializers.IntegerField()
total = serializers.IntegerField()
issues = JiraIssueSerializer(many=True)

Expand Down
97 changes: 97 additions & 0 deletions apps/taskman/query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
"""
Task Manager API endpoints
"""
import logging

from apps.taskman.models import JiraCustomFields

from .constants import JIRA_TASKMAN_PROJECT_KEY

logger = logging.getLogger(__name__)


class JiraTaskQueryBuilder:
"""
Query builder for updating or creating tasks
"""

def __init__(self, flaw):
"""
init stuff
"""
self._flaw = flaw
self._project = JIRA_TASKMAN_PROJECT_KEY
self.query = {}

def generate(self, is_creation=False):
"""
Generate and return a dict accepted by Jira API to
create or update a Jira Issue given a flaw and its task.
Removes not accepted fields on if is_creation is set
"""
self.generate_base()
self.generate_description()
self.generate_labels()
self.generate_summary()
self.generate_owner()
self.generate_group()

if not is_creation:
self.generate_team()

return self.query

def generate_base(self):
"""Create a base dict to assemble a Jira query"""
self.query = {
"fields": {
"issuetype": {"name": "Story"},
"project": {"key": self._project},
}
}

def generate_owner(self):
"""Add the assignee of the task to query"""
if not hasattr(self._flaw, "task"):
return
if self._flaw.task.owner:
self.query["fields"]["assignee"] = {"name": self._flaw.task.owner}

def generate_team(self):
"""Add the team of the task to query"""
if not hasattr(self._flaw, "task"):
return
if self._flaw.task.team_id:
self.query["fields"][JiraCustomFields.TEAM] = self._flaw.task.team_id

def generate_description(self):
"""Add descrition of the task to query"""
self.query["fields"]["description"] = self._flaw.description

def generate_labels(self):
"""Add labels of the task to query"""
labels = [f"flawuuid:{str(self._flaw.uuid)}", f"impact:{self._flaw.impact}"]

if self._flaw.is_major_incident_temp():
labels.append("major_incident")

self.query["fields"]["labels"] = [
*labels,
f"flawuuid:{str(self._flaw.uuid)}",
f"impact:{self._flaw.impact}",
]

def generate_summary(self):
"""Add summary of the task to query"""
if not self._flaw.cve_id or self._flaw.cve_id in self._flaw.title:
summary = self._flaw.title
else:
summary = f"{self._flaw.cve_id} {self._flaw.title}"

self.query["fields"]["summary"] = summary

def generate_group(self):
if not hasattr(self._flaw, "task") or not self._flaw.task.jira_group_key:
return
self.query["fields"][JiraCustomFields.EPIC_KEY] = self._flaw.task.jira_group_key
Loading

0 comments on commit 96ad07d

Please sign in to comment.