1# -*- coding: utf-8 -*-
2# Copyright (C) 2021 Greenbone Networks GmbH
3#
4# SPDX-License-Identifier: GPL-3.0-or-later
5#
6# This program is free software: you can redistribute it and/or modify
7# it under the terms of the GNU General Public License as published by
8# the Free Software Foundation, either version 3 of the License, or
9# (at your option) any later version.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License
17# along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19from enum import Enum
20
21from typing import Any, Optional
22
23from gvm.errors import InvalidArgument, InvalidArgumentType, RequiredArgument
24from gvm.utils import add_filter, to_bool
25from gvm.xml import XmlCommand
26
27
28class TicketStatus(Enum):
29    """Enum for ticket status"""
30
31    OPEN = 'Open'
32    FIXED = 'Fixed'
33    CLOSED = 'Closed'
34
35    @classmethod
36    def from_string(
37        cls,
38        ticket_status: Optional[str],
39    ) -> Optional["TicketStatus"]:
40        """Convert a ticket status string into a TicketStatus instance"""
41        if not ticket_status:
42            return None
43
44        try:
45            return cls[ticket_status.upper()]
46        except KeyError:
47            raise InvalidArgument(
48                argument='ticket_status',
49                function=cls.from_string.__name__,
50            ) from None
51
52
53class TicketsMixin:
54    def clone_ticket(self, ticket_id: str) -> Any:
55        """Clone an existing ticket
56
57        Arguments:
58            ticket_id: UUID of an existing ticket to clone from
59
60        Returns:
61            The response. See :py:meth:`send_command` for details.
62        """
63        if not ticket_id:
64            raise RequiredArgument(
65                function=self.clone_ticket.__name__, argument='ticket_id'
66            )
67
68        cmd = XmlCommand("create_ticket")
69
70        _copy = cmd.add_element("copy", ticket_id)
71
72        return self._send_xml_command(cmd)
73
74    def create_ticket(
75        self,
76        *,
77        result_id: str,
78        assigned_to_user_id: str,
79        note: str,
80        comment: Optional[str] = None,
81    ) -> Any:
82        """Create a new ticket
83
84        Arguments:
85            result_id: UUID of the result the ticket applies to
86            assigned_to_user_id: UUID of a user the ticket should be assigned to
87            note: A note about opening the ticket
88            comment: Comment for the ticket
89
90        Returns:
91            The response. See :py:meth:`send_command` for details.
92        """
93        if not result_id:
94            raise RequiredArgument(
95                function=self.create_ticket.__name__, argument='result_id'
96            )
97
98        if not assigned_to_user_id:
99            raise RequiredArgument(
100                function=self.create_ticket.__name__,
101                argument='assigned_to_user_id',
102            )
103
104        if not note:
105            raise RequiredArgument(
106                function=self.create_ticket.__name__, argument='note'
107            )
108
109        cmd = XmlCommand("create_ticket")
110
111        _result = cmd.add_element("result")
112        _result.set_attribute("id", result_id)
113
114        _assigned = cmd.add_element("assigned_to")
115        _user = _assigned.add_element("user")
116        _user.set_attribute("id", assigned_to_user_id)
117
118        _note = cmd.add_element("open_note", note)
119
120        if comment:
121            cmd.add_element("comment", comment)
122
123        return self._send_xml_command(cmd)
124
125    def delete_ticket(
126        self, ticket_id: str, *, ultimate: Optional[bool] = False
127    ):
128        """Deletes an existing ticket
129
130        Arguments:
131            ticket_id: UUID of the ticket to be deleted.
132            ultimate: Whether to remove entirely, or to the trashcan.
133        """
134        if not ticket_id:
135            raise RequiredArgument(
136                function=self.delete_ticket.__name__, argument='ticket_id'
137            )
138
139        cmd = XmlCommand("delete_ticket")
140        cmd.set_attribute("ticket_id", ticket_id)
141        cmd.set_attribute("ultimate", to_bool(ultimate))
142
143        return self._send_xml_command(cmd)
144
145    def get_tickets(
146        self,
147        *,
148        trash: Optional[bool] = None,
149        filter_string: Optional[str] = None,
150        filter_id: Optional[str] = None,
151    ) -> Any:
152        """Request a list of tickets
153
154        Arguments:
155            filter_string: Filter term to use for the query
156            filter_id: UUID of an existing filter to use for the query
157            trash: True to request the tickets in the trashcan
158
159        Returns:
160            The response. See :py:meth:`send_command` for details.
161        """
162        cmd = XmlCommand("get_tickets")
163
164        add_filter(cmd, filter_string, filter_id)
165
166        if trash is not None:
167            cmd.set_attribute("trash", to_bool(trash))
168
169        return self._send_xml_command(cmd)
170
171    def get_ticket(self, ticket_id: str) -> Any:
172        """Request a single ticket
173
174        Arguments:
175            ticket_id: UUID of an existing ticket
176
177        Returns:
178            The response. See :py:meth:`send_command` for details.
179        """
180        if not ticket_id:
181            raise RequiredArgument(
182                function=self.get_ticket.__name__, argument='ticket_id'
183            )
184
185        cmd = XmlCommand("get_tickets")
186        cmd.set_attribute("ticket_id", ticket_id)
187        return self._send_xml_command(cmd)
188
189    def modify_ticket(
190        self,
191        ticket_id: str,
192        *,
193        status: Optional[TicketStatus] = None,
194        note: Optional[str] = None,
195        assigned_to_user_id: Optional[str] = None,
196        comment: Optional[str] = None,
197    ) -> Any:
198        """Modify a single ticket
199
200        Arguments:
201            ticket_id: UUID of an existing ticket
202            status: New status for the ticket
203            note: Note for the status change. Required if status is set.
204            assigned_to_user_id: UUID of the user the ticket should be assigned
205                to
206            comment: Comment for the ticket
207
208        Returns:
209            The response. See :py:meth:`send_command` for details.
210        """
211        if not ticket_id:
212            raise RequiredArgument(
213                function=self.modify_ticket.__name__, argument='ticket_id'
214            )
215
216        if status and not note:
217            raise RequiredArgument(
218                function=self.modify_ticket.__name__, argument='note'
219            )
220
221        if note and not status:
222            raise RequiredArgument(
223                function=self.modify_ticket.__name__, argument='status'
224            )
225
226        cmd = XmlCommand("modify_ticket")
227        cmd.set_attribute("ticket_id", ticket_id)
228
229        if assigned_to_user_id:
230            _assigned = cmd.add_element("assigned_to")
231            _user = _assigned.add_element("user")
232            _user.set_attribute("id", assigned_to_user_id)
233
234        if status:
235            if not isinstance(status, TicketStatus):
236                raise InvalidArgumentType(
237                    function=self.modify_ticket.__name__,
238                    argument='status',
239                    arg_type=TicketStatus.__name__,
240                )
241
242            cmd.add_element('status', status.value)
243            cmd.add_element(f'{status.name.lower()}_note', note)
244
245        if comment:
246            cmd.add_element("comment", comment)
247
248        return self._send_xml_command(cmd)
249