Browse Source

[slack] Refactor link-unfurl and update UTs

Harshg999 4 years ago
parent
commit
1833c2de7f

+ 146 - 21
desktop/core/src/desktop/lib/botserver/views.py

@@ -17,12 +17,16 @@
 
 import logging
 import json
+from urllib.parse import urlsplit
 from pprint import pprint
 
 from desktop import conf
-from django.http import HttpResponse
+from desktop.conf import ENABLE_GIST_PREVIEW
 from desktop.lib.django_util import login_notrequired, JsonResponse
 from desktop.lib.exceptions_renderable import PopupException
+from desktop.models import Document2, _get_gist_document
+
+from django.http import HttpResponse
 from django.utils.translation import ugettext as _
 from django.views.decorators.csrf import csrf_exempt
 
@@ -36,6 +40,7 @@ if conf.SLACK.IS_ENABLED.get():
   from slack_sdk import WebClient
   slack_client = WebClient(token=SLACK_BOT_USER_TOKEN)
 
+
 @login_notrequired
 @csrf_exempt
 def slack_events(request):
@@ -45,7 +50,7 @@ def slack_events(request):
     if slack_message['token'] != SLACK_VERIFICATION_TOKEN:
       return HttpResponse(status=403)
 
-      # challenge verification
+    # challenge verification
     if slack_message['type'] == 'url_verification':
       response_dict = {"challenge": slack_message['challenge']}
       return JsonResponse(response_dict, status=200)
@@ -53,32 +58,152 @@ def slack_events(request):
     if 'event' in slack_message:
       event_message = slack_message['event']
       parse_events(event_message)
-  except ValueError as e:
-    raise PopupException(_("Response content is not valid JSON"), detail=e)
-  
+  except ValueError as err:
+    raise PopupException(_("Response content is not valid JSON"), detail=err)
+
   return HttpResponse(status=200)
 
 
-def parse_events(event_message):
-  user_id = event_message.get('user')
-  text = event_message.get('text')
-  channel = event_message.get('channel')
+def parse_events(event):
+  """
+  Parses the event according to its 'type'.
 
-  # ignore bot's own message since that will cause an infinite loop of messages if we respond
-  if event_message.get('bot_id'):
+  """
+  channel_id = event.get('channel')
+  if event.get('type') == 'message':
+    handle_on_message(channel_id, event.get('bot_id'), event.get('text'), event.get('user'))
+
+  if event.get('type') == 'link_shared':
+    handle_on_link_shared(channel_id, event.get('message_ts'), event.get('links'))
+
+
+def handle_on_message(channel_id, bot_id, text, user_id):
+  # ignore bot's own message since that will cause an infinite loop of messages if we respond.
+  if bot_id:
     return HttpResponse(status=200)
   
-  if slack_client is not None:
-    if 'hello hue' in text.lower():
-      response = say_hi_user(channel, user_id)
+  if slack_client:
+    if text and 'hello hue' in text.lower():
+      response = say_hi_user(channel_id, user_id)
+
+      if not response['ok']:
+        raise PopupException(_("Error posting message"), detail=response["error"])
+
+
+def handle_on_link_shared(channel_id, message_ts, links):
+  for item in links:
+    path = urlsplit(item['url'])[2]
+    queryid_or_uuid = urlsplit(item['url'])[3]  # if /hue/editor/ then query_id else if /hue/gist then uuid
+
+    if path == '/hue/editor':
+      query_id = queryid_or_uuid.split('=')[1]
+      doc2 = Document2.objects.get(id=query_id)
+      doc2_data = json.loads(doc2.data)
+
+      statement = doc2_data['snippets'][0]['statement_raw']
+      dialect = doc2_data['dialect'].capitalize()
+      database = doc2_data['snippets'][0]['database'].capitalize()
+      
+      payload = make_query_history_payload(item['url'], statement, dialect, database)
+      response = slack_client.chat_unfurl(channel=channel_id, ts=message_ts, unfurls=payload)
       if response['ok']:
-        return HttpResponse(status=200)
-      else:
-        raise PopupException(response["error"])
+        raise PopupException(_("Cannot unfurl query history link"), detail=response["error"])
 
-  
-def say_hi_user(channel, user_id):
-  """Bot sends Hi<username> message in a specific channel"""
+    if path == '/hue/gist' and ENABLE_GIST_PREVIEW.get():
+      gist_uuid = queryid_or_uuid.split('=')[1]
+      gist_doc = _get_gist_document(uuid=gist_uuid)
+      gist_doc_data = json.loads(gist_doc.data)
+
+      statement = gist_doc_data['statement_raw']
+      created_by = gist_doc.owner.get_full_name() or gist_doc.owner.username
+      dialect = gist_doc.extra.capitalize()
+      
+      payload = make_gist_payload(item['url'], statement, dialect, created_by)
+      response = slack_client.chat_unfurl(channel=channel_id, ts=message_ts, unfurls=payload)
+      if not response['ok']:
+        raise PopupException(_("Cannot unfurl gist link"), detail=response["error"])
+
+def say_hi_user(channel_id, user_id):
+  """
+  Sends Hi<user_id> message in a specific channel.
 
+  """
   bot_message = 'Hi <@{}> :wave:'.format(user_id)
-  return slack_client.api_call(api_method='chat.postMessage', json={'channel': channel, 'text': bot_message})
+  return slack_client.api_call(api_method='chat.postMessage', json={'channel': channel_id, 'text': bot_message})
+
+
+def make_gist_payload(url, statement, dialect, created_by):
+  gist_payload = {
+    url: {
+      "color": "#025BA6",
+      "blocks": [
+        {
+          "type": "section",
+          "text": {
+            "type": "mrkdwn",
+            "text": "\n*<{}|Hue - SQL Gist>*".format(url)
+          }
+        },
+        {
+          "type": "section",
+          "text": {
+            "type": "mrkdwn",
+            "text": statement if len(statement) < 150 else (statement[:150] + '...')
+          }
+        },
+        {
+          "type": "section",
+          "fields": [
+            {
+              "type": "mrkdwn",
+              "text": "*Dialect:*\n{}".format(dialect)
+            },
+            {
+              "type": "mrkdwn",
+              "text": "*Created By:*\n{}".format(created_by)
+            }
+          ]
+        }
+      ]
+    }
+  }
+  return gist_payload
+
+
+def make_query_history_payload(url, statement, dialect, database):
+  payload = {
+    url: {
+      "color": "#025BA6",
+      "blocks": [
+        {
+          "type": "section",
+          "text": {
+            "type": "mrkdwn",
+            "text": "\n*<{}|Hue - SQL Editor>*".format(url)
+          }
+        },
+        {
+          "type": "section",
+          "text": {
+            "type": "mrkdwn",
+            "text": statement if len(statement) < 150 else (statement[:150] + '...')
+          }
+        },
+        {
+          "type": "section",
+          "fields": [
+            {
+              "type": "mrkdwn",
+              "text": "*Dialect:*\n{}".format(dialect)
+            },
+            {
+              "type": "mrkdwn",
+              "text": "*Database:*\n{}".format(database)
+            }
+          ]
+        }
+      ]
+    }
+  }
+  return payload
+

+ 18 - 2
desktop/core/src/desktop/lib/botserver/views_tests.py

@@ -20,7 +20,7 @@ import logging
 import unittest
 import sys
 
-from nose.tools import assert_equal, assert_true
+from nose.tools import assert_equal, assert_true, assert_false
 from nose.plugins.skip import SkipTest
 from django.test import TestCase, Client
 from desktop.lib.botserver.views import *
@@ -47,4 +47,20 @@ class TestBotServer(unittest.TestCase):
       }
       response = say_hi_user("channel", "user_id")
       api_call.assert_called_with(api_method='chat.postMessage', json={'channel': 'channel', 'text': 'Hi <@user_id> :wave:'})
-      assert_true(response['ok'])
+      assert_true(response['ok'])
+  
+  def test_handle_on_message(self):
+    with patch('desktop.lib.botserver.views.say_hi_user') as say_hi_user:
+      
+      response = handle_on_message("channel", "bot_id", "text", "user_id")
+      assert_equal(response.status_code, 200)
+      assert_false(say_hi_user.called)
+      
+      handle_on_message("channel", None, None, "user_id")
+      assert_false(say_hi_user.called)
+
+      handle_on_message("channel", None, "text", "user_id")
+      assert_false(say_hi_user.called)
+
+      handle_on_message("channel", None, "hello hue test", "user_id")
+      assert_true(say_hi_user.called)