This is an automated email from the ASF dual-hosted git repository.
beto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 998624b1a5 feat: allow setting db UUID (#20412)
998624b1a5 is described below
commit 998624b1a5a498343bd7f37b5ca80402ba08e305
Author: Beto Dealmeida <[email protected]>
AuthorDate: Thu Jun 16 15:53:59 2022 -0700
feat: allow setting db UUID (#20412)
* WIP
* feat: allow passing UUID when creating a DB
* Test
* Fix field
---
superset/databases/schemas.py | 1 +
tests/unit_tests/conftest.py | 20 +++++++++
tests/unit_tests/databases/api_test.py | 53 +++++++++++++++++++++++
tests/unit_tests/importexport/api_test.py | 70 +++++++++++++------------------
4 files changed, 104 insertions(+), 40 deletions(-)
diff --git a/superset/databases/schemas.py b/superset/databases/schemas.py
index cdce5578cd..bd30ad4ec5 100644
--- a/superset/databases/schemas.py
+++ b/superset/databases/schemas.py
@@ -397,6 +397,7 @@ class DatabasePostSchema(Schema,
DatabaseParametersSchemaMixin):
)
is_managed_externally = fields.Boolean(allow_none=True, default=False)
external_url = fields.String(allow_none=True)
+ uuid = fields.String(required=False)
class DatabasePutSchema(Schema, DatabaseParametersSchemaMixin):
diff --git a/tests/unit_tests/conftest.py b/tests/unit_tests/conftest.py
index 86fb0127b8..4560617d4b 100644
--- a/tests/unit_tests/conftest.py
+++ b/tests/unit_tests/conftest.py
@@ -25,6 +25,7 @@ from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.session import Session
+from superset import security_manager
from superset.app import SupersetApp
from superset.extensions import appbuilder
from superset.initialization import SupersetAppInitializer
@@ -69,6 +70,8 @@ def app() -> Iterator[SupersetApp]:
app.config.from_object("superset.config")
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite://"
+ app.config["WTF_CSRF_ENABLED"] = False
+ app.config["PREVENT_UNSAFE_DB_CONNECTIONS"] = False
app.config["TESTING"] = True
# ``superset.extensions.appbuilder`` is a singleton, and won't rebuild the
@@ -101,3 +104,20 @@ def app_context(app: SupersetApp) -> Iterator[None]:
"""
with app.app_context():
yield
+
+
[email protected]
+def full_api_access(mocker: MockFixture) -> Iterator[None]:
+ """
+ Allow full access to the API.
+
+ TODO (betodealmeida): we should replace this with user-fixtures, eg,
``admin`` or
+ ``gamma``, so that we have granular access to the APIs.
+ """
+ mocker.patch(
+ "flask_appbuilder.security.decorators.verify_jwt_in_request",
+ return_value=True,
+ )
+ mocker.patch.object(security_manager, "has_access", return_value=True)
+
+ yield
diff --git a/tests/unit_tests/databases/api_test.py
b/tests/unit_tests/databases/api_test.py
new file mode 100644
index 0000000000..f121b799fd
--- /dev/null
+++ b/tests/unit_tests/databases/api_test.py
@@ -0,0 +1,53 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+# pylint: disable=unused-argument, import-outside-toplevel
+
+from typing import Any
+from uuid import UUID
+
+from pytest_mock import MockFixture
+from sqlalchemy.orm.session import Session
+
+
+def test_post_with_uuid(
+ mocker: MockFixture,
+ app_context: None,
+ session: Session,
+ client: Any,
+ full_api_access: None,
+) -> None:
+ """
+ Test that we can set the database UUID when creating it.
+ """
+ from superset.models.core import Database
+
+ # create table for databases
+ Database.metadata.create_all(session.get_bind()) # pylint:
disable=no-member
+
+ response = client.post(
+ "/api/v1/database/",
+ json={
+ "database_name": "my_db",
+ "sqlalchemy_uri": "sqlite://",
+ "uuid": "7c1b7880-a59d-47cd-8bf1-f1eb8d2863cb",
+ },
+ )
+ assert response.status_code == 201
+
+ database = session.query(Database).one()
+ assert database.uuid == UUID("7c1b7880-a59d-47cd-8bf1-f1eb8d2863cb")
diff --git a/tests/unit_tests/importexport/api_test.py
b/tests/unit_tests/importexport/api_test.py
index e5dee975d8..9c8c740255 100644
--- a/tests/unit_tests/importexport/api_test.py
+++ b/tests/unit_tests/importexport/api_test.py
@@ -27,18 +27,16 @@ from pytest_mock import MockFixture
from superset import security_manager
-def test_export_assets(mocker: MockFixture, client: Any) -> None:
+def test_export_assets(
+ mocker: MockFixture,
+ client: Any,
+ full_api_access: None,
+) -> None:
"""
Test exporting assets.
"""
from superset.commands.importers.v1.utils import get_contents_from_bundle
- # grant access
- mocker.patch(
- "flask_appbuilder.security.decorators.verify_jwt_in_request",
return_value=True
- )
- mocker.patch.object(security_manager, "has_access", return_value=True)
-
mocked_contents = [
(
"metadata.yaml",
@@ -62,16 +60,14 @@ def test_export_assets(mocker: MockFixture, client: Any) ->
None:
assert contents == dict(mocked_contents)
-def test_import_assets(mocker: MockFixture, client: Any) -> None:
+def test_import_assets(
+ mocker: MockFixture,
+ client: Any,
+ full_api_access: None,
+) -> None:
"""
Test importing assets.
"""
- # grant access
- mocker.patch(
- "flask_appbuilder.security.decorators.verify_jwt_in_request",
return_value=True
- )
- mocker.patch.object(security_manager, "has_access", return_value=True)
-
mocked_contents = {
"metadata.yaml": (
"version: 1.0.0\ntype: assets\ntimestamp:
'2022-01-01T00:00:00+00:00'\n"
@@ -105,16 +101,14 @@ def test_import_assets(mocker: MockFixture, client: Any)
-> None:
ImportAssetsCommand.assert_called_with(mocked_contents,
passwords=passwords)
-def test_import_assets_not_zip(mocker: MockFixture, client: Any) -> None:
+def test_import_assets_not_zip(
+ mocker: MockFixture,
+ client: Any,
+ full_api_access: None,
+) -> None:
"""
Test error message when the upload is not a ZIP file.
"""
- # grant access
- mocker.patch(
- "flask_appbuilder.security.decorators.verify_jwt_in_request",
return_value=True
- )
- mocker.patch.object(security_manager, "has_access", return_value=True)
-
buf = BytesIO(b"definitely_not_a_zip_file")
form_data = {
"bundle": (buf, "broken.txt"),
@@ -145,14 +139,14 @@ def test_import_assets_not_zip(mocker: MockFixture,
client: Any) -> None:
}
-def test_import_assets_no_form_data(mocker: MockFixture, client: Any) -> None:
+def test_import_assets_no_form_data(
+ mocker: MockFixture,
+ client: Any,
+ full_api_access: None,
+) -> None:
"""
Test error message when the upload has no form data.
"""
- # grant access
- mocker.patch(
- "flask_appbuilder.security.decorators.verify_jwt_in_request",
return_value=True
- )
mocker.patch.object(security_manager, "has_access", return_value=True)
response = client.post("/api/v1/assets/import/", data="some_content")
@@ -179,16 +173,14 @@ def test_import_assets_no_form_data(mocker: MockFixture,
client: Any) -> None:
}
-def test_import_assets_incorrect_form_data(mocker: MockFixture, client: Any)
-> None:
+def test_import_assets_incorrect_form_data(
+ mocker: MockFixture,
+ client: Any,
+ full_api_access: None,
+) -> None:
"""
Test error message when the upload form data has the wrong key.
"""
- # grant access
- mocker.patch(
- "flask_appbuilder.security.decorators.verify_jwt_in_request",
return_value=True
- )
- mocker.patch.object(security_manager, "has_access", return_value=True)
-
buf = BytesIO(b"definitely_not_a_zip_file")
form_data = {
"wrong": (buf, "broken.txt"),
@@ -200,16 +192,14 @@ def test_import_assets_incorrect_form_data(mocker:
MockFixture, client: Any) ->
assert response.json == {"message": "Arguments are not correct"}
-def test_import_assets_no_contents(mocker: MockFixture, client: Any) -> None:
+def test_import_assets_no_contents(
+ mocker: MockFixture,
+ client: Any,
+ full_api_access: None,
+) -> None:
"""
Test error message when the ZIP bundle has no contents.
"""
- # grant access
- mocker.patch(
- "flask_appbuilder.security.decorators.verify_jwt_in_request",
return_value=True
- )
- mocker.patch.object(security_manager, "has_access", return_value=True)
-
mocked_contents = {
"README.txt": "Something is wrong",
}