pycontribs / jira

Python Jira library. Development chat available on https://matrix.to/#/#pycontribs:matrix.org
https://jira.readthedocs.io
BSD 2-Clause "Simplified" License
1.91k stars 856 forks source link

add_worklog & Worklog.delete do not support `notify` param #1717

Open mjurbanski-reef opened 10 months ago

mjurbanski-reef commented 10 months ago

Problem trying to solve

I have written a bot for synchronizing my timetracking system to JIRA. It generates a lot of entries and sometimes even needs to remove old ones.

This generates a lot of notifications.

Possible solution(s)

REST API for worklogs allows for notifications to be disabled, but that parameter is simply not exposed and arbitrary parameters are not allowed.

Alternatives

Notifications can be disabled globally in JIRA by admin, but I want only disabled if they are generated by my bot using jira package.

Additional Context

this is very similar to https://github.com/pycontribs/jira/issues/937 which is about attachments instead of worklogs

mjurbanski-reef commented 10 months ago

workaround for now:

import jira
import jira.client
from jira import JIRA, Worklog
from jira.resources import Resource
from jira.utils import json_loads

class _JIRA_Patched(JIRA):
    """
    Patched JIRA client to work around JIRA package missing features:
    * https://github.com/pycontribs/jira/issues/1717 - worklog generating notifications

    Code of this class is copied&modified from mentioned above `jira` package, licensed under BSD-2.
    Full license text can be found here: https://github.com/pycontribs/jira/blob/main/LICENSE
    """

    class PatchedWorklog(Worklog):
        def delete(  # type: ignore[override]
            self,
            adjustEstimate: str | None = None,
            newEstimate=None,
            increaseBy=None,
            notify: bool | None = None,
        ):
            params = {}
            if adjustEstimate is not None:
                params["adjustEstimate"] = adjustEstimate
            if newEstimate is not None:
                params["newEstimate"] = newEstimate
            if increaseBy is not None:
                params["increaseBy"] = increaseBy
            if notify is not None:
                params["notifyUsers"] = "true" if notify else "false"

            Resource.delete(self, params)

    jira.client.Worklog = PatchedWorklog  # monkey patch so all methods are patched

    @jira.client.translate_resource_args
    def add_worklog(
        self,
        issue: str | int,
        timeSpent: (str | None) = None,
        timeSpentSeconds: (str | None) = None,
        adjustEstimate: (str | None) = None,
        newEstimate: (str | None) = None,
        reduceBy: (str | None) = None,
        comment: (str | None) = None,
        started: (datetime.datetime | None) = None,
        user: (str | None) = None,
        notify: bool | None = None,
    ) -> Worklog:
        params = {}
        if adjustEstimate is not None:
            params["adjustEstimate"] = adjustEstimate
        if newEstimate is not None:
            params["newEstimate"] = newEstimate
        if reduceBy is not None:
            params["reduceBy"] = reduceBy
        if notify is not None:
            params["notifyUsers"] = "true" if notify else "false"

        data: dict[str, Any] = {}
        if timeSpent is not None:
            data["timeSpent"] = timeSpent
        if timeSpentSeconds is not None:
            data["timeSpentSeconds"] = timeSpentSeconds
        if comment is not None:
            data["comment"] = comment
        elif user:
            # we log user inside comment as it doesn't always work
            data["comment"] = user

        if started is not None:
            # based on REST Browser it needs: "2014-06-03T08:21:01.273+0000"
            if started.tzinfo is None:
                data["started"] = started.strftime("%Y-%m-%dT%H:%M:%S.000+0000")
            else:
                data["started"] = started.strftime("%Y-%m-%dT%H:%M:%S.000%z")
        if user is not None:
            data["author"] = {
                "name": user,
                "self": self.JIRA_BASE_URL + "/rest/api/latest/user?username=" + user,
                "displayName": user,
                "active": False,
            }
            data["updateAuthor"] = data["author"]
        # report bug to Atlassian: author and updateAuthor parameters are ignored.
        url = self._get_url(f"issue/{issue}/worklog")
        r = self._session.post(url, params=params, data=json.dumps(data))

        return self.PatchedWorklog(self._options, self._session, json_loads(r))