Skip to content

Commit

Permalink
feat(ssh_tunnel): DELETE SSH Tunnels API (#22153)
Browse files Browse the repository at this point in the history
  • Loading branch information
Antonio-RiveroMartnez authored Nov 17, 2022
1 parent 16d960b commit d2ab4a6
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 0 deletions.
1 change: 1 addition & 0 deletions superset/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ class RouteMethod: # pylint: disable=too-few-public-methods
"validate_sql": "read",
"get_data": "read",
"samples": "read",
"delete_ssh_tunnel": "write",
}

EXTRA_FORM_DATA_APPEND_KEYS = {
Expand Down
61 changes: 61 additions & 0 deletions superset/databases/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@
ValidateSQLRequest,
ValidateSQLResponse,
)
from superset.databases.ssh_tunnel.commands.delete import DeleteSSHTunnelCommand
from superset.databases.ssh_tunnel.commands.exceptions import (
SSHTunnelDeleteFailedError,
SSHTunnelNotFoundError,
)
from superset.databases.utils import get_table_metadata
from superset.db_engine_specs import get_available_engine_specs
from superset.errors import ErrorLevel, SupersetError, SupersetErrorType
Expand Down Expand Up @@ -107,6 +112,7 @@ class DatabaseRestApi(BaseSupersetModelRestApi):
"available",
"validate_parameters",
"validate_sql",
"delete_ssh_tunnel",
}
resource_name = "database"
class_permission_name = "Database"
Expand Down Expand Up @@ -1204,3 +1210,58 @@ def validate_parameters(self) -> FlaskResponse:
command = ValidateDatabaseParametersCommand(payload)
command.run()
return self.response(200, message="OK")

@expose("/<int:pk>/ssh_tunnel/", methods=["DELETE"])
@protect()
@safe
@statsd_metrics
@event_logger.log_this_with_context(
action=lambda self, *args, **kwargs: f"{self.__class__.__name__}"
f".delete_ssh_tunnel",
log_to_statsd=False,
)
def delete_ssh_tunnel(self, pk: int) -> Response:
"""Deletes a SSH Tunnel
---
delete:
description: >-
Deletes a SSH Tunnel.
parameters:
- in: path
schema:
type: integer
name: pk
responses:
200:
description: SSH Tunnel deleted
content:
application/json:
schema:
type: object
properties:
message:
type: string
401:
$ref: '#/components/responses/401'
403:
$ref: '#/components/responses/403'
404:
$ref: '#/components/responses/404'
422:
$ref: '#/components/responses/422'
500:
$ref: '#/components/responses/500'
"""
try:
DeleteSSHTunnelCommand(pk).run()
return self.response(200, message="OK")
except SSHTunnelNotFoundError:
return self.response_404()
except SSHTunnelDeleteFailedError as ex:
logger.error(
"Error deleting SSH Tunnel %s: %s",
self.__class__.__name__,
str(ex),
exc_info=True,
)
return self.response_422(message=str(ex))
73 changes: 73 additions & 0 deletions tests/unit_tests/databases/api_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,76 @@ def test_non_zip_import(client: Any, full_api_access: None) -> None:
}
]
}


def test_delete_ssh_tunnel(
mocker: MockFixture,
app: Any,
session: Session,
client: Any,
full_api_access: None,
) -> None:
"""
Test that we can delete SSH Tunnel
"""
with app.app_context():
from superset.databases.api import DatabaseRestApi
from superset.databases.dao import DatabaseDAO
from superset.databases.ssh_tunnel.models import SSHTunnel
from superset.models.core import Database

DatabaseRestApi.datamodel.session = session

# create table for databases
Database.metadata.create_all(session.get_bind()) # pylint: disable=no-member

# Create our Database
database = Database(
database_name="my_database",
sqlalchemy_uri="gsheets://",
encrypted_extra=json.dumps(
{
"service_account_info": {
"type": "service_account",
"project_id": "black-sanctum-314419",
"private_key_id": "259b0d419a8f840056158763ff54d8b08f7b8173",
"private_key": "SECRET",
"client_email": "google-spreadsheets-demo-servi@black-sanctum-314419.iam.gserviceaccount.com",
"client_id": "SSH_TUNNEL_CREDENTIALS_CLIENT",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/google-spreadsheets-demo-servi%40black-sanctum-314419.iam.gserviceaccount.com",
},
}
),
)
session.add(database)
session.commit()

# mock the lookup so that we don't need to include the driver
mocker.patch("sqlalchemy.engine.URL.get_driver_name", return_value="gsheets")
mocker.patch("superset.utils.log.DBEventLogger.log")

# Create our SSHTunnel
tunnel = SSHTunnel(
database_id=1,
database=database,
)

session.add(tunnel)
session.commit()

# Get our recently created SSHTunnel
response_tunnel = DatabaseDAO.get_ssh_tunnel(1)
assert response_tunnel
assert isinstance(response_tunnel["ssh_tunnel"], SSHTunnel)
assert 1 == response_tunnel["ssh_tunnel"].database_id

# Delete the recently created SSHTunnel
response_delete_tunnel = client.delete("/api/v1/database/1/ssh_tunnel/")
assert response_delete_tunnel.json["message"] == "OK"

response_tunnel = DatabaseDAO.get_ssh_tunnel(1)
assert response_tunnel
assert response_tunnel["ssh_tunnel"] is None

0 comments on commit d2ab4a6

Please sign in to comment.