diff --git a/superset/connectors/base/models.py b/superset/connectors/base/models.py index f8a3261805e7c..7ab9cacd8544c 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, 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 0000000000000..16e5b43ff7a8d --- /dev/null +++ b/superset/migrations/versions/5fd49410a97a_add_columns_for_external_management.py @@ -0,0 +1,80 @@ +# 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=True) + ) + 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=True) + ) + 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=True) + ) + 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=True) + ) + 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=True) + ) + 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 4b7adad2d2b29..9001f4b0fb2c3 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, 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 defe720012dac..b73e2240b2314 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, 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 42779239985d8..be02997a21633 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 ba6204e0f8317..1eb8ec3be5322 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, default=False) + external_url = Column(Text, nullable=True) last_saved_by = relationship( security_manager.user_model, foreign_keys=[last_saved_by_fk] )