Repository: getredash/redash

Issue:
Uptycs Query_runner for Redash
Hi Team, 

I want to add new query_runner for Uptycs in Redash. 
I did some testing with query_runner for Uptycs. However before I share the code, please let me know the process of posting the query_runner.

Thanks,
Vibhor



Discussion:
The process is to create a pull request with the new query runner and whatever other changes are needed.
Are you familiar with Pull Requests and GitHub?
Yes. Will create a pull request and will include the changes for Uptycs. 
Thank you @arikfr !

Resolving patch:
diff --git a/redash/query_runner/uptycs.py b/redash/query_runner/uptycs.py
new file mode 100644
--- /dev/null
+++ b/redash/query_runner/uptycs.py
@@ -0,0 +1,140 @@
+from redash.query_runner import *
+from redash.utils import json_dumps
+
+import json
+import jwt
+import datetime
+import requests
+import logging
+
+logger = logging.getLogger(__name__)
+
+
+class Uptycs(BaseSQLQueryRunner):
+    noop_query = "SELECT 1"
+
+    @classmethod
+    def configuration_schema(cls):
+        return {
+            "type": "object",
+            "properties": {
+                "url": {
+                    "type": "string"
+                },
+                "customer_id": {
+                    "type": "string"
+                },
+                "key": {
+                    "type": "string"
+                },
+                "verify_ssl": {
+                    "type": "boolean",
+                    "default": True,
+                    "title": "Verify SSL Certificates",
+                },
+                "secret": {
+                    "type": "string",
+                },
+            },
+            "order": ['url', 'customer_id', 'key', 'secret'],
+            "required": ["url", "customer_id", "key", "secret"],
+            "secret": ["secret", "key"]
+        }
+
+    @classmethod
+    def annotate_query(cls):
+        return False
+
+    def generate_header(self, key, secret):
+        header = {}
+        utcnow = datetime.datetime.utcnow()
+        date = utcnow.strftime("%a, %d %b %Y %H:%M:%S GMT")
+        auth_var = jwt.encode({'iss': key}, secret, algorithm='HS256')
+        authorization = "Bearer %s" % (auth_var)
+        header['date'] = date
+        header['Authorization'] = authorization
+        return header
+
+    def transformed_to_redash_json(self, data):
+        transformed_columns = []
+        rows = []
+        # convert all type to JSON string
+        # In future we correct data type  mapping later
+        if 'columns' in data:
+            for json_each in data['columns']:
+                name = json_each['name']
+                new_json = {"name": name,
+                            "type": "string",
+                            "friendly_name": name}
+                transformed_columns.append(new_json)
+        # Transfored items into rows.
+        if 'items' in data:
+            rows = data['items']
+
+        redash_json_data = {"columns": transformed_columns,
+                            "rows": rows}
+        return redash_json_data
+
+    def api_call(self, sql):
+        # JWT encoded header
+        header = self.generate_header(self.configuration.get('key'),
+                                      self.configuration.get('secret'))
+
+        # URL form using API key file based on GLOBAL
+        url = ("%s/public/api/customers/%s/query" %
+               (self.configuration.get('url'),
+                self.configuration.get('customer_id')))
+
+        # post data base sql
+        post_data_json = {"query": sql}
+
+        response = requests.post(url, headers=header, json=post_data_json,
+                                 verify=self.configuration.get('verify_ssl',
+                                                               True))
+
+        if response.status_code == 200:
+            response_output = json.loads(response.content)
+        else:
+            error = 'status_code ' + str(response.status_code) + '\n'
+            error = error + "failed to connect"
+            json_data = {}
+            return json_data, error
+        # if we get right status code then call transfored_to_redash
+        json_data = self.transformed_to_redash_json(response_output)
+        error = None
+        # if we got error from Uptycs include error information
+        if 'error' in response_output:
+            error = response_output['error']['message']['brief']
+            error = error + '\n' + response_output['error']['message']['detail']
+        return json_data, error
+
+    def run_query(self, query, user):
+        data, error = self.api_call(query)
+        json_data = json_dumps(data)
+        logger.debug("%s", json_data)
+        return json_data, error
+
+    def get_schema(self, get_stats=False):
+        header = self.generate_header(self.configuration.get('key'),
+                                      self.configuration.get('secret'))
+        url = ("%s/public/api/customers/%s/schema/global" %
+               (self.configuration.get('url'),
+                self.configuration.get('customer_id')))
+        response = requests.get(url, headers=header,
+                                verify=self.configuration.get('verify_ssl',
+                                                              True))
+        redash_json = []
+        schema = json.loads(response.content)
+        for each_def in schema['tables']:
+            table_name = each_def['name']
+            columns = []
+            for col in each_def['columns']:
+                columns.append(col['name'])
+            table_json = {"name": table_name, "columns": columns}
+            redash_json.append(table_json)
+
+        logger.debug("%s", schema.values())
+        return redash_json
+
+
+register(Uptycs)
diff --git a/redash/settings/__init__.py b/redash/settings/__init__.py
--- a/redash/settings/__init__.py
+++ b/redash/settings/__init__.py
@@ -190,6 +190,7 @@ def all_settings():
     'redash.query_runner.druid',
     'redash.query_runner.kylin',
     'redash.query_runner.drill',
+    'redash.query_runner.uptycs',
 ]
 
 enabled_query_runners = array_from_string(os.environ.get("REDASH_ENABLED_QUERY_RUNNERS", ",".join(default_query_runners)))


Test patch: