Browse Source

[slack] Add `One Click Install` API, add unit test and surface it on gethue.com (#2166)

Harsh Gupta 4 years ago
parent
commit
29e5fe512b

+ 24 - 1
desktop/core/src/desktop/lib/botserver/api.py

@@ -17,12 +17,16 @@
 
 
 import logging
 import logging
 import json
 import json
+import yaml
 import sys
 import sys
+import os
+from urllib.parse import quote_plus
 
 
 from desktop.lib.botserver.slack_client import slack_client
 from desktop.lib.botserver.slack_client import slack_client
 from desktop.lib.exceptions_renderable import PopupException
 from desktop.lib.exceptions_renderable import PopupException
 from desktop.decorators import api_error_handler
 from desktop.decorators import api_error_handler
-from desktop.lib.django_util import JsonResponse
+from desktop.lib.django_util import JsonResponse, login_notrequired
+from desktop.settings import BASE_DIR
 
 
 if sys.version_info[0] > 2:
 if sys.version_info[0] > 2:
   from django.utils.translation import gettext as _
   from django.utils.translation import gettext as _
@@ -57,6 +61,25 @@ def send_message(request):
     'ok': slack_response.get('ok'),
     'ok': slack_response.get('ok'),
   })
   })
 
 
+@login_notrequired
+@api_error_handler
+def generate_slack_install_link(request):
+  hostname = request.GET.get('hostname')
+  hostname_without_port = hostname.split(':')[0] if ':' in hostname else hostname
+
+  install_link = 'https://api.slack.com/apps?new_app=1&manifest_yaml='
+
+  with open(os.path.join(BASE_DIR, 'tools/slack/manifest.yml')) as manifest:
+    data = yaml.safe_load(manifest)
+
+    data['features']['unfurl_domains'] = [hostname_without_port]
+    data['settings']['event_subscriptions']['request_url'] = 'https://' + hostname + '/desktop/slack/events/'
+
+    changed_data = yaml.safe_dump(data)
+    install_link += quote_plus(changed_data)
+
+  return JsonResponse({'link': install_link})
+
 def _send_message(channel_info, message=None, block_element=None):
 def _send_message(channel_info, message=None, block_element=None):
   try:
   try:
     return slack_client.chat_postMessage(channel=channel_info, text=message, blocks=block_element)
     return slack_client.chat_postMessage(channel=channel_info, text=message, blocks=block_element)

+ 21 - 1
desktop/core/src/desktop/lib/botserver/api_tests.py

@@ -39,6 +39,8 @@ class TestApi(object):
     self.client = make_logged_in_client(username="api_user", recreate=True, is_superuser=False, is_admin=True)
     self.client = make_logged_in_client(username="api_user", recreate=True, is_superuser=False, is_admin=True)
     self.user = User.objects.get(username="api_user")
     self.user = User.objects.get(username="api_user")
 
 
+    self.hostname = 'testserver.gethue.com'
+
   @classmethod
   @classmethod
   def setUpClass(cls):
   def setUpClass(cls):
     if not conf.SLACK.IS_ENABLED.get():
     if not conf.SLACK.IS_ENABLED.get():
@@ -80,4 +82,22 @@ class TestApi(object):
 
 
       assert_equal(200, response.status_code)
       assert_equal(200, response.status_code)
       chat_postMessage.assert_called_with(channel='channel-1', text='@api_user: message with link', blocks=None)
       chat_postMessage.assert_called_with(channel='channel-1', text='@api_user: message with link', blocks=None)
-      assert_true(data.get('ok'))
+      assert_true(data.get('ok'))
+  
+  def test_generate_slack_install_link(self):
+    response = self.client.get(reverse('botserver.api.slack_install_link') + '/?hostname=' + self.hostname)
+    data = json.loads(response.content)
+
+    assert_equal(200, response.status_code)
+    assert_equal(
+      data.get('link'),
+      ('https://api.slack.com/apps?new_app=1&manifest_yaml=_metadata%3A%0A++major_version%3A+1%0A++minor_version%3A+1%0Adisplay'
+      '_information%3A%0A++background_color%3A+%27%23000000%27%0A++description%3A+Share+queries%2C+ask+where+is+the+data%2C+how+to+query+it..'
+      '+questions.%0A++name%3A+SQL+Assistant%0Afeatures%3A%0A++app_home%3A%0A++++home_tab_enabled%3A+false%0A++++messages_tab_enabled%3A+false'
+      '%0A++++messages_tab_read_only_enabled%3A+true%0A++bot_user%3A%0A++++always_online%3A+true%0A++++display_name%3A+Hue+Bot%0A++unfurl_domains'
+      '%3A%0A++-+testserver.gethue.com%0Aoauth_config%3A%0A++scopes%3A%0A++++bot%3A%0A++++-+app_mentions%3Aread%0A++++-+channels%3Ahistory%0A++++-'
+      '+channels%3Aread%0A++++-+chat%3Awrite%0A++++-+files%3Awrite%0A++++-+links%3Aread%0A++++-+links%3Awrite%0A++++-+users%3Aread%0A++++-+users%3Ar'
+      'ead.email%0Asettings%3A%0A++event_subscriptions%3A%0A++++bot_events%3A%0A++++-+app_mention%0A++++-+link_shared%0A++++-+message.channels%0A++++'
+      'request_url%3A+https%3A%2F%2Ftestserver.gethue.com%2Fdesktop%2Fslack%2Fevents%2F%0A++is_hosted%3A+false%0A++org_deploy_enabled%3A+false%0A++so'
+      'cket_mode_enabled%3A+false%0A')
+    )

+ 2 - 0
desktop/core/src/desktop/lib/botserver/urls.py

@@ -29,4 +29,6 @@ urlpatterns = [
 
 
   re_path(r'^api/channels/get/?$', api.get_channels, name='botserver.api.get_channels'),
   re_path(r'^api/channels/get/?$', api.get_channels, name='botserver.api.get_channels'),
   re_path(r'^api/message/send/?$', api.send_message, name='botserver.api.send_message'),
   re_path(r'^api/message/send/?$', api.send_message, name='botserver.api.send_message'),
+
+  re_path(r'^api/install/?$', api.generate_slack_install_link, name='botserver.api.slack_install_link'),
 ]
 ]

BIN
docs/gethue/static/img/hue_slack_integration.png


+ 56 - 34
docs/gethue/themes/stack-hue-theme/layouts/partials/home.en.html

@@ -11,11 +11,7 @@
           <h3>
           <h3>
             Hue is an open source SQL Assistant for Databases & Data Warehouses
             Hue is an open source SQL Assistant for Databases & Data Warehouses
           </h3>
           </h3>
-          <a
-            class="btn btn--primary btn--lg type--uppercase"
-            href="http://demo.gethue.com"
-            target="_blank"
-          >
+          <a class="btn btn--primary btn--lg type--uppercase" href="http://demo.gethue.com" target="_blank">
             <span class="btn__text">
             <span class="btn__text">
               Try Hue Now
               Try Hue Now
             </span>
             </span>
@@ -67,14 +63,8 @@
             <br />
             <br />
             <div class="modal-container">
             <div class="modal-container">
               <div class="modal-content bg-dark" data-width="60%" data-height="60%">
               <div class="modal-content bg-dark" data-width="60%" data-height="60%">
-                <iframe
-                  title="vimeo-player"
-                  src="https://player.vimeo.com/video/275808384"
-                  width="640"
-                  height="360"
-                  frameborder="0"
-                  allowfullscreen
-                ></iframe>
+                <iframe title="vimeo-player" src="https://player.vimeo.com/video/275808384" width="640" height="360"
+                  frameborder="0" allowfullscreen></iframe>
               </div>
               </div>
               <!--end of modal-content-->
               <!--end of modal-content-->
             </div>
             </div>
@@ -92,11 +82,8 @@
     <div class="container">
     <div class="container">
       <div class="row justify-content-around">
       <div class="row justify-content-around">
         <div class="col-md-6">
         <div class="col-md-6">
-          <img
-            alt="Image"
-            class="border--round box-shadow-wide"
-            src="https://cdn.gethue.com/uploads/2017/07/hue_4_assistant_2.gif"
-          />
+          <img alt="Image" class="border--round box-shadow-wide"
+            src="https://cdn.gethue.com/uploads/2017/07/hue_4_assistant_2.gif" />
         </div>
         </div>
         <div class="col-md-6 col-lg-5">
         <div class="col-md-6 col-lg-5">
           <div class="switchable__text">
           <div class="switchable__text">
@@ -106,11 +93,8 @@
               <a href="{{ .Site.Params.docsHost }}/user/querying/">Querying Experience</a>
               <a href="{{ .Site.Params.docsHost }}/user/querying/">Querying Experience</a>
               with the most intelligent autocompletes, query sharing, result charting and download
               with the most intelligent autocompletes, query sharing, result charting and download
               for any database. Enable more of your employees to level-up and perform self service analytics like
               for any database. Enable more of your employees to level-up and perform self service analytics like
-              <a
-                href="/self-service-bi-doing-a-customer-360-by-querying-and-joining-salesforce-marketing-and-log-datasets/"
-                target="_blank"
-                >Customer 360s</a
-              >
+              <a href="/self-service-bi-doing-a-customer-360-by-querying-and-joining-salesforce-marketing-and-log-datasets/"
+                target="_blank">Customer 360s</a>
               .
               .
             </p>
             </p>
             <a href="{{ .Site.Params.docsHost }}/user/concept">Learn More »</a>
             <a href="{{ .Site.Params.docsHost }}/user/concept">Learn More »</a>
@@ -131,7 +115,8 @@
             <p class="lead">
             <p class="lead">
               Pick one of the multiple interpreters for
               Pick one of the multiple interpreters for
               <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/#apache-hive">Apache Hive</a>,
               <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/#apache-hive">Apache Hive</a>,
-              <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/#apache-impala">Apache Impala</a>,
+              <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/#apache-impala">Apache
+                Impala</a>,
               <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/#presto">Presto</a>
               <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/#presto">Presto</a>
               and all the others too: MySQL,
               and all the others too: MySQL,
               <a href="/blog/tutorial-query-live-data-stream-with-flink-sql/">Apache Flink SQL</a>,
               <a href="/blog/tutorial-query-live-data-stream-with-flink-sql/">Apache Flink SQL</a>,
@@ -139,12 +124,12 @@
               <a href="/blog/querying-spark-sql-with-spark-thrift-server-and-hue-editor/">SparkSQL</a>,
               <a href="/blog/querying-spark-sql-with-spark-thrift-server-and-hue-editor/">SparkSQL</a>,
               <a href="/sql-querying-apache-hbase-with-apache-phoenix/">Apache Phoenix</a>,
               <a href="/sql-querying-apache-hbase-with-apache-phoenix/">Apache Phoenix</a>,
               <a href="/blog/tutorial-query-live-data-stream-with-kafka-sql/">ksqlDB</a>,
               <a href="/blog/tutorial-query-live-data-stream-with-kafka-sql/">ksqlDB</a>,
-              <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/#elastic-search">Elastic Search</a>,
-              <a href="/quick-task-how-to-query-apache-druid-analytic-database/">Apache Druid</a>, PostgreSQL, Redshift, BigQuery...
+              <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/#elastic-search">Elastic
+                Search</a>,
+              <a href="/quick-task-how-to-query-apache-druid-analytic-database/">Apache Druid</a>, PostgreSQL, Redshift,
+              BigQuery...
             </p>
             </p>
-            <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/"
-              >Learn More »</a
-            >
+            <a href="{{ .Site.Params.docsHost }}/administrator/configuration/connectors/">Learn More »</a>
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
@@ -154,11 +139,8 @@
     <div class="container">
     <div class="container">
       <div class="row justify-content-around">
       <div class="row justify-content-around">
         <div class="col-md-6">
         <div class="col-md-6">
-          <img
-            alt="Image"
-            class="border--round box-shadow-wide"
-            src="https://cdn.gethue.com/uploads/2018/04/blog_top_search_.png"
-          />
+          <img alt="Image" class="border--round box-shadow-wide"
+            src="https://cdn.gethue.com/uploads/2018/04/blog_top_search_.png" />
         </div>
         </div>
         <div class="col-md-6 col-lg-5">
         <div class="col-md-6 col-lg-5">
           <div class="switchable__text">
           <div class="switchable__text">
@@ -174,4 +156,44 @@
       </div>
       </div>
     </div>
     </div>
   </section>
   </section>
+  <section class="switchable feature-large switchable--switch bg--secondary">
+    <div class="container">
+      <div class="row justify-content-around">
+        <div class="col-md-6">
+          <img alt="Image" class="border--round box-shadow-wide" src="img/hue_slack_integration.png" />
+        </div>
+        <div class="col-md-6 col-lg-5">
+          <div class="switchable__text">
+            <h2>Install the Slack App!</h2>
+            <p class="lead">
+              Assist users with their SQL queries and leverage different
+              <a href="/blog/2021-04-09-collaborate-on-your-sql-queries-and-results-directly-within-slack/">features</a>
+              such as rich preview for links, sharing from Editor etc. directly in Slack.
+              <br /> Give your Hue hostname below and
+              <a href="/blog/2021-05-18-installing-hue-slack-app-in-three-simple-steps/">install</a>
+              it in your workspace within seconds...
+            </p>
+            <form autocomplete="off" onsubmit="return check(event)">
+              <input type="text" id="hostname" placeholder="Your hue hostname (e.g. hue.gethue.com:8000)"
+                required></input>
+              <button type="submit" class="btn btn--primary btn--lg type--uppercase">Create SQL Assistant</button>
+            </form>
+            <br /><a href="{{ .Site.Params.docsHost }}/user/concept/#slack">Learn More »</a>
+          </div>
+        </div>
+      </div>
+    </div>
+  </section>
 </div>
 </div>
+<script>
+  function check(event) {
+    event.preventDefault();
+
+    const url = 'https://demo.gethue.com/desktop/slack/api/install/?hostname=' + document.getElementById('hostname').value;
+    fetch(url)
+      .then(response => response.json())
+      .then(response => {
+        window.location.replace(response['link']);
+      });
+  }
+</script>