From 1c0b141e2de700f889fc04d5f77d91bc7305a858 Mon Sep 17 00:00:00 2001 From: Beto Dealmeida Date: Mon, 24 Jan 2022 11:03:01 -0800 Subject: [PATCH] feat: allow assets to be managed externally (#18093) * feat: allow assets to be managed externally * Use server_default --- superset/connectors/base/models.py | 2 + ...97a_add_columns_for_external_management.py | 105 ++++++++++++++++++ superset/models/core.py | 3 + superset/models/dashboard.py | 2 + superset/models/helpers.py | 2 +- superset/models/slice.py | 13 ++- 6 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 superset/migrations/versions/5fd49410a97a_add_columns_for_external_management.py diff --git a/superset/connectors/base/models.py b/superset/connectors/base/models.py index f8a3261805e7..06b44c45c85e 100644 --- a/superset/connectors/base/models.py +++ b/superset/connectors/base/models.py @@ -110,6 +110,8 @@ def name(self) -> str: params = Column(String(1000)) perm = Column(String(1000)) schema_perm = Column(String(1000)) + is_managed_externally = Column(Boolean, nullable=False, default=False) + external_url = Column(Text, nullable=True) sql: Optional[str] = None owners: List[User] diff --git a/superset/migrations/versions/5fd49410a97a_add_columns_for_external_management.py b/superset/migrations/versions/5fd49410a97a_add_columns_for_external_management.py new file mode 100644 index 000000000000..466c8dd5ebb7 --- /dev/null +++ b/superset/migrations/versions/5fd49410a97a_add_columns_for_external_management.py @@ -0,0 +1,105 @@ +# 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. +"""Add columns for external management + +Revision ID: 5fd49410a97a +Revises: c53bae8f08dd +Create Date: 2022-01-19 07:34:20.594786 + +""" + +# revision identifiers, used by Alembic. +revision = "5fd49410a97a" +down_revision = "c53bae8f08dd" + +import sqlalchemy as sa +from alembic import op + + +def upgrade(): + with op.batch_alter_table("dashboards") as batch_op: + batch_op.add_column( + sa.Column( + "is_managed_externally", + sa.Boolean(), + nullable=False, + server_default=sa.false(), + ) + ) + batch_op.add_column(sa.Column("external_url", sa.Text(), nullable=True)) + + with op.batch_alter_table("datasources") as batch_op: + batch_op.add_column( + sa.Column( + "is_managed_externally", + sa.Boolean(), + nullable=False, + server_default=sa.false(), + ) + ) + batch_op.add_column(sa.Column("external_url", sa.Text(), nullable=True)) + + with op.batch_alter_table("dbs") as batch_op: + batch_op.add_column( + sa.Column( + "is_managed_externally", + sa.Boolean(), + nullable=False, + server_default=sa.false(), + ) + ) + batch_op.add_column(sa.Column("external_url", sa.Text(), nullable=True)) + + with op.batch_alter_table("slices") as batch_op: + batch_op.add_column( + sa.Column( + "is_managed_externally", + sa.Boolean(), + nullable=False, + server_default=sa.false(), + ) + ) + batch_op.add_column(sa.Column("external_url", sa.Text(), nullable=True)) + + with op.batch_alter_table("tables") as batch_op: + batch_op.add_column( + sa.Column( + "is_managed_externally", + sa.Boolean(), + nullable=False, + server_default=sa.false(), + ) + ) + batch_op.add_column(sa.Column("external_url", sa.Text(), nullable=True)) + + +def downgrade(): + with op.batch_alter_table("tables") as batch_op: + batch_op.drop_column("external_url") + batch_op.drop_column("is_managed_externally") + with op.batch_alter_table("slices") as batch_op: + batch_op.drop_column("external_url") + batch_op.drop_column("is_managed_externally") + with op.batch_alter_table("dbs") as batch_op: + batch_op.drop_column("external_url") + batch_op.drop_column("is_managed_externally") + with op.batch_alter_table("datasources") as batch_op: + batch_op.drop_column("external_url") + batch_op.drop_column("is_managed_externally") + with op.batch_alter_table("dashboards") as batch_op: + batch_op.drop_column("external_url") + batch_op.drop_column("is_managed_externally") diff --git a/superset/models/core.py b/superset/models/core.py index 4b7adad2d2b2..05970fe774fb 100755 --- a/superset/models/core.py +++ b/superset/models/core.py @@ -151,6 +151,9 @@ class Database( encrypted_extra = Column(encrypted_field_factory.create(Text), nullable=True) impersonate_user = Column(Boolean, default=False) server_cert = Column(encrypted_field_factory.create(Text), nullable=True) + is_managed_externally = Column(Boolean, nullable=False, default=False) + external_url = Column(Text, nullable=True) + export_fields = [ "database_name", "sqlalchemy_uri", diff --git a/superset/models/dashboard.py b/superset/models/dashboard.py index defe720012da..55bf909b9735 100644 --- a/superset/models/dashboard.py +++ b/superset/models/dashboard.py @@ -149,6 +149,8 @@ class Dashboard(Model, AuditMixinNullable, ImportExportMixin): slices = relationship(Slice, secondary=dashboard_slices, backref="dashboards") owners = relationship(security_manager.user_model, secondary=dashboard_user) published = Column(Boolean, default=False) + is_managed_externally = Column(Boolean, nullable=False, default=False) + external_url = Column(Text, nullable=True) roles = relationship(security_manager.role_model, secondary=DashboardRoles) _filter_sets = relationship( "FilterSet", back_populates="dashboard", cascade="all, delete" diff --git a/superset/models/helpers.py b/superset/models/helpers.py index 42779239985d..be02997a2163 100644 --- a/superset/models/helpers.py +++ b/superset/models/helpers.py @@ -341,7 +341,7 @@ def remove_params(self, param_to_remove: str) -> None: self.params = json.dumps(params) def reset_ownership(self) -> None: - """ object will belong to the user the current user """ + """object will belong to the user the current user""" # make sure the object doesn't have relations to a user # it will be filled by appbuilder on save self.created_by = None diff --git a/superset/models/slice.py b/superset/models/slice.py index ba6204e0f831..9c22efec6d4d 100644 --- a/superset/models/slice.py +++ b/superset/models/slice.py @@ -25,7 +25,16 @@ from flask_appbuilder import Model from flask_appbuilder.models.decorators import renders from markupsafe import escape, Markup -from sqlalchemy import Column, DateTime, ForeignKey, Integer, String, Table, Text +from sqlalchemy import ( + Boolean, + Column, + DateTime, + ForeignKey, + Integer, + String, + Table, + Text, +) from sqlalchemy.engine.base import Connection from sqlalchemy.orm import relationship from sqlalchemy.orm.mapper import Mapper @@ -83,6 +92,8 @@ class Slice( # pylint: disable=too-many-public-methods last_saved_by_fk = Column(Integer, ForeignKey("ab_user.id"), nullable=True) certified_by = Column(Text) certification_details = Column(Text) + is_managed_externally = Column(Boolean, nullable=False, default=False) + external_url = Column(Text, nullable=True) last_saved_by = relationship( security_manager.user_model, foreign_keys=[last_saved_by_fk] )