Repository: softlayer/softlayer-python
Pull request #1024 (2018-08-22 23:17:49)

#1019 - Ability to set ticket priority
Addresses #1019 
### New Features
- Create ticket with priority
- List tickets with priority column
- Ticket details with priority information
- Ticket features 100% unit tests


Patch:
diff --git a/SoftLayer/CLI/ticket/__init__.py b/SoftLayer/CLI/ticket/__init__.py
index e5f711d9..11fe2a87 100644
--- a/SoftLayer/CLI/ticket/__init__.py
+++ b/SoftLayer/CLI/ticket/__init__.py
@@ -7,6 +7,15 @@
 
 TEMPLATE_MSG = "***** SoftLayer Ticket Content ******"
 
+# https://softlayer.github.io/reference/services/SoftLayer_Ticket_Priority/getPriorities/
+PRIORITY_MAP = [
+    'No Priority',
+    'Severity 1 - Critical Impact / Service Down',
+    'Severity 2 - Significant Business Impact',
+    'Severity 3 - Minor Business Impact',
+    'Severity 4 - Minimal Business Impact'
+]
+
 
 def get_ticket_results(mgr, ticket_id, update_count=1):
     """Get output about a ticket.
@@ -24,6 +33,7 @@ def get_ticket_results(mgr, ticket_id, update_count=1):
 
     table.add_row(['id', ticket['id']])
     table.add_row(['title', ticket['title']])
+    table.add_row(['priority', PRIORITY_MAP[ticket.get('priority', 0)]])
     if ticket.get('assignedUser'):
         user = ticket['assignedUser']
         table.add_row([
diff --git a/SoftLayer/CLI/ticket/create.py b/SoftLayer/CLI/ticket/create.py
index 71ebd26e..75c34b79 100644
--- a/SoftLayer/CLI/ticket/create.py
+++ b/SoftLayer/CLI/ticket/create.py
@@ -11,43 +11,38 @@
 
 @click.command()
 @click.option('--title', required=True, help="The title of the ticket")
-@click.option('--subject-id',
-              type=int,
-              required=True,
+@click.option('--subject-id', type=int, required=True,
               help="""The subject id to use for the ticket,
  issue 'slcli ticket subjects' to get the list""")
 @click.option('--body', help="The ticket body")
-@click.option('--hardware',
-              'hardware_identifier',
+@click.option('--hardware', 'hardware_identifier',
               help="The identifier for hardware to attach")
-@click.option('--virtual',
-              'virtual_identifier',
+@click.option('--virtual', 'virtual_identifier',
               help="The identifier for a virtual server to attach")
+@click.option('--priority', 'priority', type=click.Choice(['1', '2', '3', '4']), default=None,
+              help="""Ticket priority, from 1 (Critical) to 4 (Minimal Impact).
+              Only settable with Advanced and Premium support. See https://www.ibm.com/cloud/support""")
 @environment.pass_env
-def cli(env, title, subject_id, body, hardware_identifier, virtual_identifier):
+def cli(env, title, subject_id, body, hardware_identifier, virtual_identifier, priority):
     """Create a support ticket."""
     ticket_mgr = SoftLayer.TicketManager(env.client)
 
     if body is None:
         body = click.edit('\n\n' + ticket.TEMPLATE_MSG)
-
     created_ticket = ticket_mgr.create_ticket(
         title=title,
         body=body,
-        subject=subject_id)
+        subject=subject_id,
+        priority=priority)
 
     if hardware_identifier:
         hardware_mgr = SoftLayer.HardwareManager(env.client)
-        hardware_id = helpers.resolve_id(hardware_mgr.resolve_ids,
-                                         hardware_identifier,
-                                         'hardware')
+        hardware_id = helpers.resolve_id(hardware_mgr.resolve_ids, hardware_identifier, 'hardware')
         ticket_mgr.attach_hardware(created_ticket['id'], hardware_id)
 
     if virtual_identifier:
         vs_mgr = SoftLayer.VSManager(env.client)
-        vs_id = helpers.resolve_id(vs_mgr.resolve_ids,
-                                   virtual_identifier,
-                                   'VS')
+        vs_id = helpers.resolve_id(vs_mgr.resolve_ids, virtual_identifier, 'VS')
         ticket_mgr.attach_virtual_server(created_ticket['id'], vs_id)
 
     env.fout(ticket.get_ticket_results(ticket_mgr, created_ticket['id']))
diff --git a/SoftLayer/CLI/ticket/list.py b/SoftLayer/CLI/ticket/list.py
index d7c72794..64c8b7dd 100644
--- a/SoftLayer/CLI/ticket/list.py
+++ b/SoftLayer/CLI/ticket/list.py
@@ -9,25 +9,21 @@
 
 
 @click.command()
-@click.option('--open / --closed', 'is_open',
-              help="Display only open or closed tickets",
-              default=True)
+@click.option('--open / --closed', 'is_open', default=True,
+              help="Display only open or closed tickets")
 @environment.pass_env
 def cli(env, is_open):
     """List tickets."""
     ticket_mgr = SoftLayer.TicketManager(env.client)
+    table = formatting.Table([
+        'id', 'assigned_user', 'title', 'last_edited', 'status', 'updates', 'priority'
+    ])
 
-    tickets = ticket_mgr.list_tickets(open_status=is_open,
-                                      closed_status=not is_open)
-
-    table = formatting.Table(['id', 'assigned_user', 'title',
-                              'last_edited', 'status'])
-
+    tickets = ticket_mgr.list_tickets(open_status=is_open, closed_status=not is_open)
     for ticket in tickets:
         user = formatting.blank()
         if ticket.get('assignedUser'):
-            user = "%s %s" % (ticket['assignedUser']['firstName'],
-                              ticket['assignedUser']['lastName'])
+            user = "%s %s" % (ticket['assignedUser']['firstName'], ticket['assignedUser']['lastName'])
 
         table.add_row([
             ticket['id'],
@@ -35,6 +31,8 @@ def cli(env, is_open):
             click.wrap_text(ticket['title']),
             ticket['lastEditDate'],
             ticket['status']['name'],
+            ticket.get('updateCount', 0),
+            ticket.get('priority', 0)
         ])
 
     env.fout(table)
diff --git a/SoftLayer/managers/ticket.py b/SoftLayer/managers/ticket.py
index 93b91509..0155f0d5 100644
--- a/SoftLayer/managers/ticket.py
+++ b/SoftLayer/managers/ticket.py
@@ -28,8 +28,8 @@ def list_tickets(self, open_status=True, closed_status=True):
         :param boolean open_status: include open tickets
         :param boolean closed_status: include closed tickets
         """
-        mask = ('id, title, assignedUser[firstName, lastName],'
-                'createDate,lastEditDate,accountId,status')
+        mask = """mask[id, title, assignedUser[firstName, lastName], priority,
+                  createDate, lastEditDate, accountId, status, updateCount]"""
 
         call = 'getTickets'
         if not all([open_status, closed_status]):
@@ -53,18 +53,18 @@ def get_ticket(self, ticket_id):
         :returns: dict -- information about the specified ticket
 
         """
-        mask = ('id, title, assignedUser[firstName, lastName],status,'
-                'createDate,lastEditDate,updates[entry,editor],updateCount')
+        mask = """mask[id, title, assignedUser[firstName, lastName],status,
+                  createDate,lastEditDate,updates[entry,editor],updateCount, priority]"""
         return self.ticket.getObject(id=ticket_id, mask=mask)
 
-    def create_ticket(self, title=None, body=None, subject=None):
+    def create_ticket(self, title=None, body=None, subject=None, priority=None):
         """Create a new ticket.
 
         :param string title: title for the new ticket
         :param string body: body for the new ticket
         :param integer subject: id of the subject to be assigned to the ticket
+        :param integer priority: Value from 1 (highest) to 4 (lowest)
         """
-
         current_user = self.account.getCurrentUser()
         new_ticket = {
             'subjectId': subject,
@@ -72,6 +72,9 @@ def create_ticket(self, title=None, body=None, subject=None):
             'assignedUserId': current_user['id'],
             'title': title,
         }
+        if priority is not None:
+            new_ticket['priority'] = int(priority)
+
         created_ticket = self.ticket.createStandardTicket(new_ticket, body)
         return created_ticket
 
@@ -83,18 +86,12 @@ def update_ticket(self, ticket_id=None, body=None):
         """
         return self.ticket.addUpdate({'entry': body}, id=ticket_id)
 
-    def upload_attachment(self, ticket_id=None, file_path=None,
-                          file_name=None):
+    def upload_attachment(self, ticket_id=None, file_path=None, file_name=None):
         """Upload an attachment to a ticket.
 
-        :param integer ticket_id: the id of the ticket to
-                                  upload the attachment to
-        :param string file_path:
-                                  The path of the attachment to be uploaded
-        :param string file_name:
-                                  The name of the attachment shown
-                                  in the ticket
-
+        :param integer ticket_id: the id of the ticket to upload the attachment to
+        :param string file_path: The path of the attachment to be uploaded
+        :param string file_name: The name of the attachment shown in the ticket
         :returns: dict -- The uploaded attachment
         """
         file_content = None


Test patch:
diff --git a/tests/CLI/modules/ticket_tests.py b/tests/CLI/modules/ticket_tests.py
index c9d27ba3..817b3e71 100644
--- a/tests/CLI/modules/ticket_tests.py
+++ b/tests/CLI/modules/ticket_tests.py
@@ -8,6 +8,9 @@
 import mock
 
 from SoftLayer.CLI import exceptions
+from SoftLayer.CLI import formatting
+from SoftLayer.CLI import ticket
+from SoftLayer.managers import TicketManager
 from SoftLayer import testing
 
 
@@ -20,10 +23,12 @@ def test_list(self):
             'assigned_user': 'John Smith',
             'id': 102,
             'last_edited': '2013-08-01T14:16:47-07:00',
+            'priority': 0,
             'status': 'Open',
-            'title': 'Cloud Instance Cancellation - 08/01/13'}]
+            'title': 'Cloud Instance Cancellation - 08/01/13',
+            'updates': 0}]
         self.assert_no_fail(result)
-        self.assertEqual(json.loads(result.output), expected)
+        self.assertEqual(expected, json.loads(result.output))
 
     def test_detail(self):
         result = self.run_command(['ticket', 'detail', '1'])
@@ -32,6 +37,7 @@ def test_detail(self):
             'created': '2013-08-01T14:14:04-07:00',
             'edited': '2013-08-01T14:16:47-07:00',
             'id': 100,
+            'priority': 'No Priority',
             'status': 'Closed',
             'title': 'Cloud Instance Cancellation - 08/01/13',
             'update 1': 'a bot says something',
@@ -53,8 +59,23 @@ def test_create(self):
                  'assignedUserId': 12345,
                  'title': 'Test'}, 'ticket body')
 
-        self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket',
-                                args=args)
+        self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket', args=args)
+
+    def test_create_with_priority(self):
+        result = self.run_command(['ticket', 'create', '--title=Test',
+                                   '--subject-id=1000',
+                                   '--body=ticket body',
+                                   '--priority=1'])
+
+        self.assert_no_fail(result)
+
+        args = ({'subjectId': 1000,
+                 'contents': 'ticket body',
+                 'assignedUserId': 12345,
+                 'title': 'Test',
+                 'priority': 1}, 'ticket body')
+
+        self.assert_called_with('SoftLayer_Ticket', 'createStandardTicket', args=args)
 
     def test_create_and_attach(self):
         result = self.run_command(['ticket', 'create', '--title=Test',
@@ -204,3 +225,79 @@ def test_ticket_upload(self):
                                 args=({"filename": "a_file_name",
                                        "data": b"ticket attached data"},),
                                 identifier=1)
+
+    def test_init_ticket_results(self):
+        ticket_mgr = TicketManager(self.client)
+        ticket_table = ticket.get_ticket_results(ticket_mgr, 100)
+        self.assert_called_with('SoftLayer_Ticket', 'getObject', identifier=100)
+        self.assertIsInstance(ticket_table, formatting.KeyValueTable)
+
+        ticket_object = ticket_table.to_python()
+        self.assertEqual('No Priority', ticket_object['priority'])
+        self.assertEqual(100, ticket_object['id'])
+
+    def test_init_ticket_results_asigned_user(self):
+        mock = self.set_mock('SoftLayer_Ticket', 'getObject')
+        mock.return_value = {
+            "id": 100,
+            "title": "Simple Title",
+            "priority": 1,
+            "assignedUser": {
+                "firstName": "Test",
+                "lastName": "User"
+            },
+            "status": {
+                "name": "Closed"
+            },
+            "createDate": "2013-08-01T14:14:04-07:00",
+            "lastEditDate": "2013-08-01T14:16:47-07:00",
+            "updates": [{'entry': 'a bot says something'}]
+        }
+
+        ticket_mgr = TicketManager(self.client)
+        ticket_table = ticket.get_ticket_results(ticket_mgr, 100)
+        self.assert_called_with('SoftLayer_Ticket', 'getObject', identifier=100)
+        self.assertIsInstance(ticket_table, formatting.KeyValueTable)
+
+        ticket_object = ticket_table.to_python()
+        self.assertEqual('Severity 1 - Critical Impact / Service Down', ticket_object['priority'])
+        self.assertEqual('Test User', ticket_object['user'])
+
+    def test_ticket_summary(self):
+        mock = self.set_mock('SoftLayer_Account', 'getObject')
+        mock.return_value = {
+            'openTicketCount': 1,
+            'closedTicketCount': 2,
+            'openBillingTicketCount': 3,
+            'openOtherTicketCount': 4,
+            'openSalesTicketCount': 5,
+            'openSupportTicketCount': 6,
+            'openAccountingTicketCount': 7
+        }
+        expected = [
+            {'Status': 'Open',
+             'count': [
+                {'Type': 'Accounting', 'count': 7},
+                {'Type': 'Billing', 'count': 3},
+                {'Type': 'Sales', 'count': 5},
+                {'Type': 'Support', 'count': 6},
+                {'Type': 'Other', 'count': 4},
+                {'Type': 'Total', 'count': 1}]},
+            {'Status': 'Closed', 'count': 2}
+        ]
+        result = self.run_command(['ticket', 'summary'])
+        self.assert_no_fail(result)
+        self.assert_called_with('SoftLayer_Account', 'getObject')
+        self.assertEqual(expected, json.loads(result.output))
+
+    def test_ticket_update(self):
+        result = self.run_command(['ticket', 'update', '100', '--body=Testing'])
+        self.assert_no_fail(result)
+        self.assert_called_with('SoftLayer_Ticket', 'addUpdate', args=({'entry': 'Testing'},), identifier=100)
+
+    @mock.patch('click.edit')
+    def test_ticket_update_no_body(self, edit_mock):
+        edit_mock.return_value = 'Testing1'
+        result = self.run_command(['ticket', 'update', '100'])
+        self.assert_no_fail(result)
+        self.assert_called_with('SoftLayer_Ticket', 'addUpdate', args=({'entry': 'Testing1'},), identifier=100)