We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Minor Salesforce runner fix
<!--

We use GitHub only for bug reports 🐛

Anything else should be posted to https://discuss.redash.io 👫

🚨For support, help & questions use https://discuss.redash.io/c/support
💡For feature requests & ideas use https://discuss.redash.io/c/feature-requests

**Found a security vulnerability?** Please email security@redash.io to report any security vulnerabilities. We will acknowledge receipt of your vulnerability and strive to send you regular updates about our progress. If you're curious about the status of your disclosure please feel free to email us again. If you want to encrypt your disclosure email, you can use this PGP key.

-->

### Issue Summary

A Security Token isn't required in all SFDC environments - depending on configuration. See [here](https://help.salesforce.com/articleView?id=000331668&type=1&mode=1) for more information.

### Steps to Reproduce

1. Add Salesforce as a data source where a token isn't required (and cannot be generated)
2. Cannot proceed without required field

### Technical details:

https://github.com/getredash/redash/blob/be56035bd6d9856361edc6b23d30a38c8f2d2be2/redash/query_runner/salesforce.py#L81

Just remove `token` from the `required` list. Seemed like it'd be faster to create an issue than submit a PR for such a small change

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `redash/query_runner/salesforce.py`
Content:
```
1 import logging
2 import re
3 from collections import OrderedDict
4 
5 from redash.query_runner import (
6     TYPE_BOOLEAN,
7     TYPE_DATE,
8     TYPE_DATETIME,
9     TYPE_FLOAT,
10     TYPE_INTEGER,
11     TYPE_STRING,
12     BaseQueryRunner,
13     register,
14 )
15 from redash.utils import json_dumps
16 
17 logger = logging.getLogger(__name__)
18 
19 try:
20     from simple_salesforce import Salesforce as SimpleSalesforce
21     from simple_salesforce import SalesforceError
22     from simple_salesforce.api import DEFAULT_API_VERSION
23 
24     enabled = True
25 except ImportError:
26     enabled = False
27 
28 # See https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/field_types.htm
29 TYPES_MAP = dict(
30     id=TYPE_STRING,
31     string=TYPE_STRING,
32     currency=TYPE_FLOAT,
33     reference=TYPE_STRING,
34     double=TYPE_FLOAT,
35     picklist=TYPE_STRING,
36     date=TYPE_DATE,
37     url=TYPE_STRING,
38     phone=TYPE_STRING,
39     textarea=TYPE_STRING,
40     int=TYPE_INTEGER,
41     datetime=TYPE_DATETIME,
42     boolean=TYPE_BOOLEAN,
43     percent=TYPE_FLOAT,
44     multipicklist=TYPE_STRING,
45     masterrecord=TYPE_STRING,
46     location=TYPE_STRING,
47     JunctionIdList=TYPE_STRING,
48     encryptedstring=TYPE_STRING,
49     email=TYPE_STRING,
50     DataCategoryGroupReference=TYPE_STRING,
51     combobox=TYPE_STRING,
52     calculated=TYPE_STRING,
53     anyType=TYPE_STRING,
54     address=TYPE_STRING,
55 )
56 
57 # Query Runner for Salesforce SOQL Queries
58 # For example queries, see:
59 # https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_examples.htm
60 
61 
62 class Salesforce(BaseQueryRunner):
63     should_annotate_query = False
64 
65     @classmethod
66     def enabled(cls):
67         return enabled
68 
69     @classmethod
70     def configuration_schema(cls):
71         return {
72             "type": "object",
73             "properties": {
74                 "username": {"type": "string"},
75                 "password": {"type": "string"},
76                 "token": {"type": "string", "title": "Security Token"},
77                 "sandbox": {"type": "boolean"},
78                 "api_version": {
79                     "type": "string",
80                     "title": "Salesforce API Version",
81                     "default": DEFAULT_API_VERSION,
82                 },
83             },
84             "required": ["username", "password", "token"],
85             "secret": ["password", "token"],
86         }
87 
88     def test_connection(self):
89         response = self._get_sf().describe()
90         if response is None:
91             raise Exception("Failed describing objects.")
92         pass
93 
94     def _get_sf(self):
95         sf = SimpleSalesforce(
96             username=self.configuration["username"],
97             password=self.configuration["password"],
98             security_token=self.configuration["token"],
99             sandbox=self.configuration.get("sandbox", False),
100             version=self.configuration.get("api_version", DEFAULT_API_VERSION),
101             client_id="Redash",
102         )
103         return sf
104 
105     def _clean_value(self, value):
106         if isinstance(value, OrderedDict) and "records" in value:
107             value = value["records"]
108             for row in value:
109                 row.pop("attributes", None)
110         return value
111 
112     def _get_value(self, dct, dots):
113         for key in dots.split("."):
114             if dct is not None and key in dct:
115                 dct = dct.get(key)
116             else:
117                 dct = None
118         return dct
119 
120     def _get_column_name(self, key, parents=[]):
121         return ".".join(parents + [key])
122 
123     def _build_columns(self, sf, child, parents=[]):
124         child_type = child["attributes"]["type"]
125         child_desc = sf.__getattr__(child_type).describe()
126         child_type_map = dict((f["name"], f["type"]) for f in child_desc["fields"])
127         columns = []
128         for key in child.keys():
129             if key != "attributes":
130                 if isinstance(child[key], OrderedDict) and "attributes" in child[key]:
131                     columns.extend(self._build_columns(sf, child[key], parents + [key]))
132                 else:
133                     column_name = self._get_column_name(key, parents)
134                     key_type = child_type_map.get(key, "string")
135                     column_type = TYPES_MAP.get(key_type, TYPE_STRING)
136                     columns.append((column_name, column_type))
137         return columns
138 
139     def _build_rows(self, columns, records):
140         rows = []
141         for record in records:
142             record.pop("attributes", None)
143             row = dict()
144             for column in columns:
145                 key = column[0]
146                 value = self._get_value(record, key)
147                 row[key] = self._clean_value(value)
148             rows.append(row)
149         return rows
150 
151     def run_query(self, query, user):
152         logger.debug("Salesforce is about to execute query: %s", query)
153         query = re.sub(r"/\*(.|\n)*?\*/", "", query).strip()
154         try:
155             columns = []
156             rows = []
157             sf = self._get_sf()
158             response = sf.query_all(query)
159             records = response["records"]
160             if response["totalSize"] > 0 and len(records) == 0:
161                 columns = self.fetch_columns([("Count", TYPE_INTEGER)])
162                 rows = [{"Count": response["totalSize"]}]
163             elif len(records) > 0:
164                 cols = self._build_columns(sf, records[0])
165                 rows = self._build_rows(cols, records)
166                 columns = self.fetch_columns(cols)
167             error = None
168             data = {"columns": columns, "rows": rows}
169             json_data = json_dumps(data)
170         except SalesforceError as err:
171             error = err.content
172             json_data = None
173         return json_data, error
174 
175     def get_schema(self, get_stats=False):
176         sf = self._get_sf()
177         response = sf.describe()
178         if response is None:
179             raise Exception("Failed describing objects.")
180 
181         schema = {}
182         for sobject in response["sobjects"]:
183             table_name = sobject["name"]
184             if sobject["queryable"] is True and table_name not in schema:
185                 desc = sf.__getattr__(sobject["name"]).describe()
186                 fields = desc["fields"]
187                 schema[table_name] = {
188                     "name": table_name,
189                     "columns": [f["name"] for f in fields],
190                 }
191         return list(schema.values())
192 
193 
194 register(Salesforce)
195
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/redash/query_runner/salesforce.py b/redash/query_runner/salesforce.py
--- a/redash/query_runner/salesforce.py
+++ b/redash/query_runner/salesforce.py
@@ -81,7 +81,7 @@
                     "default": DEFAULT_API_VERSION,
                 },
             },
-            "required": ["username", "password", "token"],
+            "required": ["username", "password"],
             "secret": ["password", "token"],
         }

{"golden_diff": "diff --git a/redash/query_runner/salesforce.py b/redash/query_runner/salesforce.py\n--- a/redash/query_runner/salesforce.py\n+++ b/redash/query_runner/salesforce.py\n@@ -81,7 +81,7 @@\n                     \"default\": DEFAULT_API_VERSION,\n                 },\n             },\n-            \"required\": [\"username\", \"password\", \"token\"],\n+            \"required\": [\"username\", \"password\"],\n             \"secret\": [\"password\", \"token\"],\n         }\n", "issue": "Minor Salesforce runner fix\n<!--\r\n\r\nWe use GitHub only for bug reports \ud83d\udc1b\r\n\r\nAnything else should be posted to https://discuss.redash.io \ud83d\udc6b\r\n\r\n\ud83d\udea8For support, help & questions use https://discuss.redash.io/c/support\r\n\ud83d\udca1For feature requests & ideas use https://discuss.redash.io/c/feature-requests\r\n\r\n**Found a security vulnerability?** Please email security@redash.io to report any security vulnerabilities. We will acknowledge receipt of your vulnerability and strive to send you regular updates about our progress. If you're curious about the status of your disclosure please feel free to email us again. If you want to encrypt your disclosure email, you can use this PGP key.\r\n\r\n-->\r\n\r\n### Issue Summary\r\n\r\nA Security Token isn't required in all SFDC environments - depending on configuration. See [here](https://help.salesforce.com/articleView?id=000331668&type=1&mode=1) for more information.\r\n\r\n### Steps to Reproduce\r\n\r\n1. Add Salesforce as a data source where a token isn't required (and cannot be generated)\r\n2. Cannot proceed without required field\r\n\r\n### Technical details:\r\n\r\nhttps://github.com/getredash/redash/blob/be56035bd6d9856361edc6b23d30a38c8f2d2be2/redash/query_runner/salesforce.py#L81\r\n\r\nJust remove `token` from the `required` list. Seemed like it'd be faster to create an issue than submit a PR for such a small change\n", "before_files": [{"content": "import logging\nimport re\nfrom collections import OrderedDict\n\nfrom redash.query_runner import (\n    TYPE_BOOLEAN,\n    TYPE_DATE,\n    TYPE_DATETIME,\n    TYPE_FLOAT,\n    TYPE_INTEGER,\n    TYPE_STRING,\n    BaseQueryRunner,\n    register,\n)\nfrom redash.utils import json_dumps\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    from simple_salesforce import Salesforce as SimpleSalesforce\n    from simple_salesforce import SalesforceError\n    from simple_salesforce.api import DEFAULT_API_VERSION\n\n    enabled = True\nexcept ImportError:\n    enabled = False\n\n# See https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/field_types.htm\nTYPES_MAP = dict(\n    id=TYPE_STRING,\n    string=TYPE_STRING,\n    currency=TYPE_FLOAT,\n    reference=TYPE_STRING,\n    double=TYPE_FLOAT,\n    picklist=TYPE_STRING,\n    date=TYPE_DATE,\n    url=TYPE_STRING,\n    phone=TYPE_STRING,\n    textarea=TYPE_STRING,\n    int=TYPE_INTEGER,\n    datetime=TYPE_DATETIME,\n    boolean=TYPE_BOOLEAN,\n    percent=TYPE_FLOAT,\n    multipicklist=TYPE_STRING,\n    masterrecord=TYPE_STRING,\n    location=TYPE_STRING,\n    JunctionIdList=TYPE_STRING,\n    encryptedstring=TYPE_STRING,\n    email=TYPE_STRING,\n    DataCategoryGroupReference=TYPE_STRING,\n    combobox=TYPE_STRING,\n    calculated=TYPE_STRING,\n    anyType=TYPE_STRING,\n    address=TYPE_STRING,\n)\n\n# Query Runner for Salesforce SOQL Queries\n# For example queries, see:\n# https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_examples.htm\n\n\nclass Salesforce(BaseQueryRunner):\n    should_annotate_query = False\n\n    @classmethod\n    def enabled(cls):\n        return enabled\n\n    @classmethod\n    def configuration_schema(cls):\n        return {\n            \"type\": \"object\",\n            \"properties\": {\n                \"username\": {\"type\": \"string\"},\n                \"password\": {\"type\": \"string\"},\n                \"token\": {\"type\": \"string\", \"title\": \"Security Token\"},\n                \"sandbox\": {\"type\": \"boolean\"},\n                \"api_version\": {\n                    \"type\": \"string\",\n                    \"title\": \"Salesforce API Version\",\n                    \"default\": DEFAULT_API_VERSION,\n                },\n            },\n            \"required\": [\"username\", \"password\", \"token\"],\n            \"secret\": [\"password\", \"token\"],\n        }\n\n    def test_connection(self):\n        response = self._get_sf().describe()\n        if response is None:\n            raise Exception(\"Failed describing objects.\")\n        pass\n\n    def _get_sf(self):\n        sf = SimpleSalesforce(\n            username=self.configuration[\"username\"],\n            password=self.configuration[\"password\"],\n            security_token=self.configuration[\"token\"],\n            sandbox=self.configuration.get(\"sandbox\", False),\n            version=self.configuration.get(\"api_version\", DEFAULT_API_VERSION),\n            client_id=\"Redash\",\n        )\n        return sf\n\n    def _clean_value(self, value):\n        if isinstance(value, OrderedDict) and \"records\" in value:\n            value = value[\"records\"]\n            for row in value:\n                row.pop(\"attributes\", None)\n        return value\n\n    def _get_value(self, dct, dots):\n        for key in dots.split(\".\"):\n            if dct is not None and key in dct:\n                dct = dct.get(key)\n            else:\n                dct = None\n        return dct\n\n    def _get_column_name(self, key, parents=[]):\n        return \".\".join(parents + [key])\n\n    def _build_columns(self, sf, child, parents=[]):\n        child_type = child[\"attributes\"][\"type\"]\n        child_desc = sf.__getattr__(child_type).describe()\n        child_type_map = dict((f[\"name\"], f[\"type\"]) for f in child_desc[\"fields\"])\n        columns = []\n        for key in child.keys():\n            if key != \"attributes\":\n                if isinstance(child[key], OrderedDict) and \"attributes\" in child[key]:\n                    columns.extend(self._build_columns(sf, child[key], parents + [key]))\n                else:\n                    column_name = self._get_column_name(key, parents)\n                    key_type = child_type_map.get(key, \"string\")\n                    column_type = TYPES_MAP.get(key_type, TYPE_STRING)\n                    columns.append((column_name, column_type))\n        return columns\n\n    def _build_rows(self, columns, records):\n        rows = []\n        for record in records:\n            record.pop(\"attributes\", None)\n            row = dict()\n            for column in columns:\n                key = column[0]\n                value = self._get_value(record, key)\n                row[key] = self._clean_value(value)\n            rows.append(row)\n        return rows\n\n    def run_query(self, query, user):\n        logger.debug(\"Salesforce is about to execute query: %s\", query)\n        query = re.sub(r\"/\\*(.|\\n)*?\\*/\", \"\", query).strip()\n        try:\n            columns = []\n            rows = []\n            sf = self._get_sf()\n            response = sf.query_all(query)\n            records = response[\"records\"]\n            if response[\"totalSize\"] > 0 and len(records) == 0:\n                columns = self.fetch_columns([(\"Count\", TYPE_INTEGER)])\n                rows = [{\"Count\": response[\"totalSize\"]}]\n            elif len(records) > 0:\n                cols = self._build_columns(sf, records[0])\n                rows = self._build_rows(cols, records)\n                columns = self.fetch_columns(cols)\n            error = None\n            data = {\"columns\": columns, \"rows\": rows}\n            json_data = json_dumps(data)\n        except SalesforceError as err:\n            error = err.content\n            json_data = None\n        return json_data, error\n\n    def get_schema(self, get_stats=False):\n        sf = self._get_sf()\n        response = sf.describe()\n        if response is None:\n            raise Exception(\"Failed describing objects.\")\n\n        schema = {}\n        for sobject in response[\"sobjects\"]:\n            table_name = sobject[\"name\"]\n            if sobject[\"queryable\"] is True and table_name not in schema:\n                desc = sf.__getattr__(sobject[\"name\"]).describe()\n                fields = desc[\"fields\"]\n                schema[table_name] = {\n                    \"name\": table_name,\n                    \"columns\": [f[\"name\"] for f in fields],\n                }\n        return list(schema.values())\n\n\nregister(Salesforce)\n", "path": "redash/query_runner/salesforce.py"}], "after_files": [{"content": "import logging\nimport re\nfrom collections import OrderedDict\n\nfrom redash.query_runner import (\n    TYPE_BOOLEAN,\n    TYPE_DATE,\n    TYPE_DATETIME,\n    TYPE_FLOAT,\n    TYPE_INTEGER,\n    TYPE_STRING,\n    BaseQueryRunner,\n    register,\n)\nfrom redash.utils import json_dumps\n\nlogger = logging.getLogger(__name__)\n\ntry:\n    from simple_salesforce import Salesforce as SimpleSalesforce\n    from simple_salesforce import SalesforceError\n    from simple_salesforce.api import DEFAULT_API_VERSION\n\n    enabled = True\nexcept ImportError:\n    enabled = False\n\n# See https://developer.salesforce.com/docs/atlas.en-us.api.meta/api/field_types.htm\nTYPES_MAP = dict(\n    id=TYPE_STRING,\n    string=TYPE_STRING,\n    currency=TYPE_FLOAT,\n    reference=TYPE_STRING,\n    double=TYPE_FLOAT,\n    picklist=TYPE_STRING,\n    date=TYPE_DATE,\n    url=TYPE_STRING,\n    phone=TYPE_STRING,\n    textarea=TYPE_STRING,\n    int=TYPE_INTEGER,\n    datetime=TYPE_DATETIME,\n    boolean=TYPE_BOOLEAN,\n    percent=TYPE_FLOAT,\n    multipicklist=TYPE_STRING,\n    masterrecord=TYPE_STRING,\n    location=TYPE_STRING,\n    JunctionIdList=TYPE_STRING,\n    encryptedstring=TYPE_STRING,\n    email=TYPE_STRING,\n    DataCategoryGroupReference=TYPE_STRING,\n    combobox=TYPE_STRING,\n    calculated=TYPE_STRING,\n    anyType=TYPE_STRING,\n    address=TYPE_STRING,\n)\n\n# Query Runner for Salesforce SOQL Queries\n# For example queries, see:\n# https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_examples.htm\n\n\nclass Salesforce(BaseQueryRunner):\n    should_annotate_query = False\n\n    @classmethod\n    def enabled(cls):\n        return enabled\n\n    @classmethod\n    def configuration_schema(cls):\n        return {\n            \"type\": \"object\",\n            \"properties\": {\n                \"username\": {\"type\": \"string\"},\n                \"password\": {\"type\": \"string\"},\n                \"token\": {\"type\": \"string\", \"title\": \"Security Token\"},\n                \"sandbox\": {\"type\": \"boolean\"},\n                \"api_version\": {\n                    \"type\": \"string\",\n                    \"title\": \"Salesforce API Version\",\n                    \"default\": DEFAULT_API_VERSION,\n                },\n            },\n            \"required\": [\"username\", \"password\"],\n            \"secret\": [\"password\", \"token\"],\n        }\n\n    def test_connection(self):\n        response = self._get_sf().describe()\n        if response is None:\n            raise Exception(\"Failed describing objects.\")\n        pass\n\n    def _get_sf(self):\n        sf = SimpleSalesforce(\n            username=self.configuration[\"username\"],\n            password=self.configuration[\"password\"],\n            security_token=self.configuration[\"token\"],\n            sandbox=self.configuration.get(\"sandbox\", False),\n            version=self.configuration.get(\"api_version\", DEFAULT_API_VERSION),\n            client_id=\"Redash\",\n        )\n        return sf\n\n    def _clean_value(self, value):\n        if isinstance(value, OrderedDict) and \"records\" in value:\n            value = value[\"records\"]\n            for row in value:\n                row.pop(\"attributes\", None)\n        return value\n\n    def _get_value(self, dct, dots):\n        for key in dots.split(\".\"):\n            if dct is not None and key in dct:\n                dct = dct.get(key)\n            else:\n                dct = None\n        return dct\n\n    def _get_column_name(self, key, parents=[]):\n        return \".\".join(parents + [key])\n\n    def _build_columns(self, sf, child, parents=[]):\n        child_type = child[\"attributes\"][\"type\"]\n        child_desc = sf.__getattr__(child_type).describe()\n        child_type_map = dict((f[\"name\"], f[\"type\"]) for f in child_desc[\"fields\"])\n        columns = []\n        for key in child.keys():\n            if key != \"attributes\":\n                if isinstance(child[key], OrderedDict) and \"attributes\" in child[key]:\n                    columns.extend(self._build_columns(sf, child[key], parents + [key]))\n                else:\n                    column_name = self._get_column_name(key, parents)\n                    key_type = child_type_map.get(key, \"string\")\n                    column_type = TYPES_MAP.get(key_type, TYPE_STRING)\n                    columns.append((column_name, column_type))\n        return columns\n\n    def _build_rows(self, columns, records):\n        rows = []\n        for record in records:\n            record.pop(\"attributes\", None)\n            row = dict()\n            for column in columns:\n                key = column[0]\n                value = self._get_value(record, key)\n                row[key] = self._clean_value(value)\n            rows.append(row)\n        return rows\n\n    def run_query(self, query, user):\n        logger.debug(\"Salesforce is about to execute query: %s\", query)\n        query = re.sub(r\"/\\*(.|\\n)*?\\*/\", \"\", query).strip()\n        try:\n            columns = []\n            rows = []\n            sf = self._get_sf()\n            response = sf.query_all(query)\n            records = response[\"records\"]\n            if response[\"totalSize\"] > 0 and len(records) == 0:\n                columns = self.fetch_columns([(\"Count\", TYPE_INTEGER)])\n                rows = [{\"Count\": response[\"totalSize\"]}]\n            elif len(records) > 0:\n                cols = self._build_columns(sf, records[0])\n                rows = self._build_rows(cols, records)\n                columns = self.fetch_columns(cols)\n            error = None\n            data = {\"columns\": columns, \"rows\": rows}\n            json_data = json_dumps(data)\n        except SalesforceError as err:\n            error = err.content\n            json_data = None\n        return json_data, error\n\n    def get_schema(self, get_stats=False):\n        sf = self._get_sf()\n        response = sf.describe()\n        if response is None:\n            raise Exception(\"Failed describing objects.\")\n\n        schema = {}\n        for sobject in response[\"sobjects\"]:\n            table_name = sobject[\"name\"]\n            if sobject[\"queryable\"] is True and table_name not in schema:\n                desc = sf.__getattr__(sobject[\"name\"]).describe()\n                fields = desc[\"fields\"]\n                schema[table_name] = {\n                    \"name\": table_name,\n                    \"columns\": [f[\"name\"] for f in fields],\n                }\n        return list(schema.values())\n\n\nregister(Salesforce)\n", "path": "redash/query_runner/salesforce.py"}]}