Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: return parameters only for DB with default driver #14803

Merged
merged 2 commits into from May 25, 2021

Conversation

betodealmeida
Copy link
Member

@betodealmeida betodealmeida commented May 25, 2021

SUMMARY

This PR improves the /api/v1/database/available/ endpoint:

  1. For each DB engine spec, also show the list of installed drivers (eg, mysqldb and mysqlconnector for MySQL, in my system).
  2. For each DB, show the default driver if available (eg, mysqldb for MySQL).
  3. Only show the parameters if the default driver is installed (eg, if mysqldb is not installed but mysqlconector is, don't show the parameters for MySQL).

The reason for (3) is that if the user doesn't have the default driver installed we can't enable SSL for them. For example, for MySQL the engine spec can only enable it for the default mysqldb driver (see).

If the user wants to use the non-default driver they need to use the old method via the SQLAlchemy URI. In that case we don't show the parameters for the engine spec, so that the modal should only allow the DB to be configured via the URI.

(1) and (2) will not be used by the modal, but are useful for debugging and troubleshooting.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A

TESTING INSTRUCTIONS

Updated unit tests.

Also tested manually. On my machine, with:

PREFERRED_DATABASES = ["PostgreSQL", "Presto"]

The response in /api/v1/database/available/ is:

{
  "databases": [
    {
      "available_drivers": [
        "mysqlconnector", 
        "mysqldb"
      ], 
      "default_driver": "mysqldb", 
      "engine": "mysql", 
      "name": "MySQL", 
      "parameters": {
        "properties": {
          "database": {
            "description": "Database name", 
            "type": "string"
          }, 
          "encryption": {
            "description": "Use an encrypted connection to the database", 
            "type": "boolean"
          }, 
          "host": {
            "description": "Hostname or IP address", 
            "type": "string"
          }, 
          "password": {
            "description": "Password", 
            "nullable": true, 
            "type": "string"
          }, 
          "port": {
            "description": "Database port", 
            "format": "int32", 
            "type": "integer"
          }, 
          "query": {
            "additionalProperties": {}, 
            "description": "Additional parameters", 
            "type": "object"
          }, 
          "username": {
            "description": "Username", 
            "nullable": true, 
            "type": "string"
          }
        }, 
        "required": [
          "database", 
          "host", 
          "port", 
          "username"
        ], 
        "type": "object"
      }, 
      "preferred": false, 
      "sqlalchemy_uri_placeholder": "mysql://user:password@host:port/dbname[?key=value&key=value...]"
    }, 
    {
      "available_drivers": [
        "psycopg2"
      ], 
      "default_driver": "psycopg2", 
      "engine": "postgresql", 
      "name": "PostgreSQL", 
      "parameters": {
        "properties": {
          "database": {
            "description": "Database name", 
            "type": "string"
          }, 
          "encryption": {
            "description": "Use an encrypted connection to the database", 
            "type": "boolean"
          }, 
          "host": {
            "description": "Hostname or IP address", 
            "type": "string"
          }, 
          "password": {
            "description": "Password", 
            "nullable": true, 
            "type": "string"
          }, 
          "port": {
            "description": "Database port", 
            "format": "int32", 
            "type": "integer"
          }, 
          "query": {
            "additionalProperties": {}, 
            "description": "Additional parameters", 
            "type": "object"
          }, 
          "username": {
            "description": "Username", 
            "nullable": true, 
            "type": "string"
          }
        }, 
        "required": [
          "database", 
          "host", 
          "port", 
          "username"
        ], 
        "type": "object"
      }, 
      "preferred": true, 
      "sqlalchemy_uri_placeholder": "postgresql://user:password@host:port/dbname[?key=value&key=value...]"
    }, 
    {
      "available_drivers": [
        "pysqlite"
      ], 
      "engine": "sqlite", 
      "name": "SQLite", 
      "preferred": false
    }, 
    {
      "available_drivers": [
        "vertica_python"
      ], 
      "engine": "vertica", 
      "name": "Vertica", 
      "preferred": false
    }, 
    {
      "available_drivers": [
        "rest"
      ], 
      "engine": "presto", 
      "name": "Presto", 
      "preferred": true
    }, 
    {
      "available_drivers": [
        "rest"
      ], 
      "engine": "druid", 
      "name": "Apache Druid", 
      "preferred": false
    }, 
    {
      "available_drivers": [
        "bigquery"
      ], 
      "default_driver": "bigquery", 
      "engine": "bigquery", 
      "name": "Google BigQuery", 
      "parameters": {
        "properties": {
          "credentials_info": {
            "description": "Contents of BigQuery JSON credentials.", 
            "type": "string", 
            "x-encrypted-extra": true
          }
        }, 
        "type": "object"
      }, 
      "preferred": false, 
      "sqlalchemy_uri_placeholder": "bigquery://{project_id}"
    }, 
    {
      "available_drivers": [
        "turbodbc"
      ], 
      "engine": "mssql", 
      "name": "Microsoft SQL", 
      "preferred": false
    }, 
    {
      "available_drivers": [
        "apsw"
      ], 
      "engine": "gsheets", 
      "name": "Google Sheets", 
      "preferred": false
    }
  ]
}

ADDITIONAL INFORMATION

  • Has associated issue:
  • Changes UI
  • Includes DB Migration (follow approval process in SIP-59)
    • Migration is atomic, supports rollback & is backwards-compatible
    • Confirm DB migration upgrade and downgrade tested
    • Runtime estimates and downtime expectations provided
  • Introduces new feature or API
  • Removes existing feature or API

@betodealmeida betodealmeida changed the title WIP feat: return parameters only for DB with default driver May 25, 2021
@pull-request-size pull-request-size bot added size/L and removed size/M labels May 25, 2021
@betodealmeida betodealmeida force-pushed the ch15642 branch 3 times, most recently from e77bc84 to 08b2762 Compare May 25, 2021 04:59
@betodealmeida betodealmeida marked this pull request as ready for review May 25, 2021 05:09
available_databases.sort(
key=lambda payload: preferred_databases.index(payload["engine"])
if payload["engine"] in preferred_databases
else len(preferred_databases)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was sorting the preferred DBs correctly, but doing a non-deterministic sort on the non-preferred. 🤦

@eschutho
Copy link
Member

The reason for (3) is that if the user doesn't have the default driver installed we can't enable SSL for them. For example, for MySQL the engine spec can only enable it for the default mysqldb driver (see).

Is SSL the only difference between the default and non default driver? Could we show them the form but not give them the option to turn on ssl (like we do for admins who enforce ssl but the opposite)?

@betodealmeida
Copy link
Member Author

The reason for (3) is that if the user doesn't have the default driver installed we can't enable SSL for them. For example, for MySQL the engine spec can only enable it for the default mysqldb driver (see).

Is SSL the only difference between the default and non default driver? Could we show them the form but not give them the option to turn on ssl (like we do for admins who enforce ssl but the opposite)?

Right, but there could potentially be other parameters that are driver-dependent — maybe a MySQL driver uses client certificates instead of username/password — so I think we should only show the form if the default driver is installed.

Also, if we show the form when the default driver is not installed, and the user has more than one non-default drivers installed, this raises the problem of which one we should use. Eg, the default driver for MySQL is mysqldb, but the user could have oursql and mysqlconnector installed instead. If we show them the form for MySQL, we would have to choose one of the drivers for them (normally we would use the default driver).

I think users in cases like this probably know better, and so they can just the SQLAlchemy URI to configure the DB with a non-default driver.

@codecov
Copy link

codecov bot commented May 25, 2021

Codecov Report

Merging #14803 (81e7419) into master (c728947) will decrease coverage by 0.03%.
The diff coverage is 50.00%.

❗ Current head 81e7419 differs from pull request most recent head dbab3dd. Consider uploading reports for the commit dbab3dd to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##           master   #14803      +/-   ##
==========================================
- Coverage   77.38%   77.35%   -0.04%     
==========================================
  Files         962      962              
  Lines       49174    49192      +18     
  Branches     6184     6184              
==========================================
- Hits        38055    38051       -4     
- Misses      10917    10939      +22     
  Partials      202      202              
Flag Coverage Δ
mysql 81.59% <50.00%> (-0.04%) ⬇️
postgres 81.61% <50.00%> (-0.04%) ⬇️
python 81.66% <50.00%> (-0.08%) ⬇️
sqlite ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
superset/config.py 91.09% <ø> (ø)
superset/db_engine_specs/__init__.py 54.23% <21.05%> (-11.68%) ⬇️
superset/db_engine_specs/bigquery.py 90.15% <50.00%> (ø)
superset/databases/api.py 92.47% <100.00%> (+0.08%) ⬆️
superset/db_engine_specs/base.py 87.50% <100.00%> (-0.41%) ⬇️
superset/db_engine_specs/cockroachdb.py 100.00% <100.00%> (ø)
superset/db_engine_specs/mysql.py 94.28% <100.00%> (ø)
superset/db_engine_specs/postgres.py 96.96% <100.00%> (ø)
superset/db_engine_specs/sqlite.py 90.62% <0.00%> (-6.25%) ⬇️
superset/utils/celery.py 86.20% <0.00%> (-3.45%) ⬇️
... and 4 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c728947...dbab3dd. Read the comment docs.

Copy link
Member

@hughhhh hughhhh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@betodealmeida betodealmeida merged commit 8b1a117 into apache:master May 25, 2021
cccs-RyanS pushed a commit to CybercentreCanada/superset that referenced this pull request Dec 17, 2021
QAlexBall pushed a commit to QAlexBall/superset that referenced this pull request Dec 29, 2021
cccs-rc pushed a commit to CybercentreCanada/superset that referenced this pull request Mar 6, 2024
@mistercrunch mistercrunch added 🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels 🚢 1.3.0 labels Mar 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels size/L 🚢 1.3.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants