This is an automated email from the ASF dual-hosted git repository. amitmiran pushed a commit to branch feat/on_import_set_self_as_owner in repository https://gitbox.apache.org/repos/asf/superset.git
commit 919b7b6006463679dfdf500ed57afce5a4a74c90 Author: amitmiran137 <[email protected]> AuthorDate: Mon Jun 21 19:30:57 2021 +0300 feat(import): requester as owner option --- .../src/components/ImportModal/index.tsx | 24 ++++++++++++++ superset-frontend/src/views/CRUD/hooks.ts | 5 +++ superset/charts/commands/importers/v1/__init__.py | 12 +++++-- superset/charts/commands/importers/v1/utils.py | 7 +++- superset/commands/importers/v1/__init__.py | 13 ++++++-- superset/commands/importers/v1/examples.py | 8 ++++- superset/config.py | 2 +- superset/dashboards/api.py | 8 +++-- .../dashboards/commands/importers/v1/__init__.py | 37 ++++++++++++++++++---- superset/dashboards/commands/importers/v1/utils.py | 8 ++++- .../databases/commands/importers/v1/__init__.py | 19 +++++++++-- superset/databases/commands/importers/v1/utils.py | 7 +++- .../datasets/commands/importers/v1/__init__.py | 19 +++++++++-- superset/datasets/commands/importers/v1/utils.py | 3 ++ .../commands/importers/v1/__init__.py | 12 +++++-- 15 files changed, 157 insertions(+), 27 deletions(-) diff --git a/superset-frontend/src/components/ImportModal/index.tsx b/superset-frontend/src/components/ImportModal/index.tsx index 193d3c5..e157544 100644 --- a/superset-frontend/src/components/ImportModal/index.tsx +++ b/superset-frontend/src/components/ImportModal/index.tsx @@ -26,6 +26,7 @@ import Modal from 'src/components/Modal'; import { Upload } from 'src/common/components'; import { useImportResource } from 'src/views/CRUD/hooks'; import { ImportResourceName } from 'src/views/CRUD/types'; +import Checkbox from '../Checkbox'; export const StyledIcon = styled(Icon)` margin: auto ${({ theme }) => theme.gridUnit * 2}px auto 0; @@ -135,6 +136,10 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({ false, ); const [confirmedOverwrite, setConfirmedOverwrite] = useState<boolean>(false); + const [ + confirmedRequesterAsOwner, + setConfirmedRequesterAsOwner, + ] = useState<boolean>(false); const [fileList, setFileList] = useState<UploadFile[]>([]); const [importingModel, setImportingModel] = useState<boolean>(false); @@ -144,6 +149,7 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({ setPasswords({}); setNeedsOverwriteConfirm(false); setConfirmedOverwrite(false); + setConfirmedRequesterAsOwner(false); setImportingModel(false); }; @@ -188,6 +194,7 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({ fileList[0].originFileObj, passwords, confirmedOverwrite, + confirmedRequesterAsOwner, ).then(result => { if (result) { addSuccessToast(t('The import was successful')); @@ -269,6 +276,22 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({ ); }; + const renderRequesterAsOwner = () => ( + <> + <StyledInputContainer> + <div className="confirm-requester-as-owner"> + {confirmOverwriteMessage} + </div> + <div className="control-label">{t('Set My User as owner')}</div> + <Checkbox + data-test="requester-as-owner-modal-input" + onChange={(val?: boolean) => setConfirmedRequesterAsOwner(val)} + checked={confirmedRequesterAsOwner} + /> + </StyledInputContainer> + </> + ); + // Show/hide if (isHidden && show) { setIsHidden(false); @@ -308,6 +331,7 @@ const ImportModelsModal: FunctionComponent<ImportModelsModalProps> = ({ </StyledInputContainer> {renderPasswordFields()} {renderOverwriteConfirmation()} + {renderRequesterAsOwner()} </Modal> ); }; diff --git a/superset-frontend/src/views/CRUD/hooks.ts b/superset-frontend/src/views/CRUD/hooks.ts index 785d608..ecd99b6 100644 --- a/superset-frontend/src/views/CRUD/hooks.ts +++ b/superset-frontend/src/views/CRUD/hooks.ts @@ -394,6 +394,7 @@ export function useImportResource( bundle: File, databasePasswords: Record<string, string> = {}, overwrite = false, + requesterAsOwner = false, ) => { // Set loading state updateState({ @@ -416,6 +417,10 @@ export function useImportResource( formData.append('overwrite', 'true'); } + if (requesterAsOwner) { + formData.append('requesterAsOwner', 'true'); + } + return SupersetClient.post({ endpoint: `/api/v1/${resourceName}/import/`, body: formData, diff --git a/superset/charts/commands/importers/v1/__init__.py b/superset/charts/commands/importers/v1/__init__.py index 0e2b5b3..cc12d68 100644 --- a/superset/charts/commands/importers/v1/__init__.py +++ b/superset/charts/commands/importers/v1/__init__.py @@ -48,7 +48,10 @@ class ImportChartsCommand(ImportModelsCommand): @staticmethod def _import( - session: Session, configs: Dict[str, Any], overwrite: bool = False + session: Session, + configs: Dict[str, Any], + overwrite: bool = False, + requester_as_owner: bool = False, ) -> None: # discover datasets associated with charts dataset_uuids: Set[str] = set() @@ -95,4 +98,9 @@ class ImportChartsCommand(ImportModelsCommand): } ) config["params"].update({"datasource": dataset.uid}) - import_chart(session, config, overwrite=overwrite) + import_chart( + session, + config, + overwrite=overwrite, + requester_as_owner=requester_as_owner, + ) diff --git a/superset/charts/commands/importers/v1/utils.py b/superset/charts/commands/importers/v1/utils.py index b3d4237..671bd31 100644 --- a/superset/charts/commands/importers/v1/utils.py +++ b/superset/charts/commands/importers/v1/utils.py @@ -24,7 +24,10 @@ from superset.models.slice import Slice def import_chart( - session: Session, config: Dict[str, Any], overwrite: bool = False + session: Session, + config: Dict[str, Any], + overwrite: bool = False, + requester_as_owner: bool = False, ) -> Slice: existing = session.query(Slice).filter_by(uuid=config["uuid"]).first() if existing: @@ -36,6 +39,8 @@ def import_chart( config["params"] = json.dumps(config["params"]) chart = Slice.import_from_dict(session, config, recursive=False) + if requester_as_owner: + chart.reset_ownership() if chart.id is None: session.flush() diff --git a/superset/commands/importers/v1/__init__.py b/superset/commands/importers/v1/__init__.py index b9ea244..b28f28e 100644 --- a/superset/commands/importers/v1/__init__.py +++ b/superset/commands/importers/v1/__init__.py @@ -29,7 +29,7 @@ from superset.commands.importers.v1.utils import ( METADATA_FILE_NAME, ) from superset.dao.base import BaseDAO -from superset.models.core import Database +from superset.models.core import Database, logger class ImportModelsCommand(BaseCommand): @@ -47,11 +47,16 @@ class ImportModelsCommand(BaseCommand): self.contents = contents self.passwords: Dict[str, str] = kwargs.get("passwords") or {} self.overwrite: bool = kwargs.get("overwrite", False) + logger.info(f"requesterAsOwner: {kwargs} {args}") + self.requester_as_owner: bool = kwargs.get("requester_as_owner", False) self._configs: Dict[str, Any] = {} @staticmethod def _import( - session: Session, configs: Dict[str, Any], overwrite: bool = False + session: Session, + configs: Dict[str, Any], + overwrite: bool = False, + requester_as_owner: bool = False, ) -> None: raise NotImplementedError("Subclasses MUST implement _import") @@ -64,7 +69,9 @@ class ImportModelsCommand(BaseCommand): # rollback to prevent partial imports try: - self._import(db.session, self._configs, self.overwrite) + self._import( + db.session, self._configs, self.overwrite, self.requester_as_owner + ) db.session.commit() except Exception: db.session.rollback() diff --git a/superset/commands/importers/v1/examples.py b/superset/commands/importers/v1/examples.py index 2b56ee0..c6092ef 100644 --- a/superset/commands/importers/v1/examples.py +++ b/superset/commands/importers/v1/examples.py @@ -76,6 +76,7 @@ class ImportExamplesCommand(ImportModelsCommand): session: Session, configs: Dict[str, Any], overwrite: bool = False, + requester_as_owner: bool = False, force_data: bool = False, ) -> None: # import databases @@ -124,7 +125,12 @@ class ImportExamplesCommand(ImportModelsCommand): for file_name, config in configs.items(): if file_name.startswith("dashboards/"): config = update_id_refs(config, chart_ids) - dashboard = import_dashboard(session, config, overwrite=overwrite) + dashboard = import_dashboard( + session, + config, + overwrite=overwrite, + requester_as_owner=requester_as_owner, + ) for uuid in find_chart_uuids(config["position"]): chart_id = chart_ids[uuid] if (dashboard.id, chart_id) not in existing_relationships: diff --git a/superset/config.py b/superset/config.py index 883f0bc..e262779 100644 --- a/superset/config.py +++ b/superset/config.py @@ -361,7 +361,7 @@ DEFAULT_FEATURE_FLAGS: Dict[str, bool] = { "DASHBOARD_CROSS_FILTERS": False, "DASHBOARD_NATIVE_FILTERS_SET": False, "GLOBAL_ASYNC_QUERIES": False, - "VERSIONED_EXPORT": False, + "VERSIONED_EXPORT": True, # Note that: RowLevelSecurityFilter is only given by default to the Admin role # and the Admin Role does have the all_datasources security permission. # But, if users create a specific role with access to RowLevelSecurityFilter MVC diff --git a/superset/dashboards/api.py b/superset/dashboards/api.py index a581a2d..c1f9fc6 100644 --- a/superset/dashboards/api.py +++ b/superset/dashboards/api.py @@ -955,9 +955,13 @@ class DashboardRestApi(BaseSupersetModelRestApi): else None ) overwrite = request.form.get("overwrite") == "true" - + requester_as_owner = request.form.get("requesterAsOwner") + logger.info(f"import formdata {request.form}") command = ImportDashboardsCommand( - contents, passwords=passwords, overwrite=overwrite + contents, + passwords=passwords, + overwrite=overwrite, + requester_as_owner=requester_as_owner, ) command.run() return self.response(200, message="OK") diff --git a/superset/dashboards/commands/importers/v1/__init__.py b/superset/dashboards/commands/importers/v1/__init__.py index c698e89..7528ecc 100644 --- a/superset/dashboards/commands/importers/v1/__init__.py +++ b/superset/dashboards/commands/importers/v1/__init__.py @@ -14,7 +14,6 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - from typing import Any, Dict, List, Set, Tuple from marshmallow import Schema @@ -30,7 +29,7 @@ from superset.dashboards.commands.importers.v1.utils import ( import_dashboard, update_id_refs, ) -from superset.dashboards.dao import DashboardDAO +from superset.dashboards.dao import DashboardDAO, logger from superset.dashboards.schemas import ImportV1DashboardSchema from superset.databases.commands.importers.v1.utils import import_database from superset.databases.schemas import ImportV1DatabaseSchema @@ -58,7 +57,10 @@ class ImportDashboardsCommand(ImportModelsCommand): # pylint: disable=too-many-branches, too-many-locals @staticmethod def _import( - session: Session, configs: Dict[str, Any], overwrite: bool = False + session: Session, + configs: Dict[str, Any], + overwrite: bool = False, + requester_as_owner: bool = False, ) -> None: # discover charts associated with dashboards chart_uuids: Set[str] = set() @@ -82,7 +84,12 @@ class ImportDashboardsCommand(ImportModelsCommand): database_ids: Dict[str, int] = {} for file_name, config in configs.items(): if file_name.startswith("databases/") and config["uuid"] in database_uuids: - database = import_database(session, config, overwrite=False) + database = import_database( + session, + config, + overwrite=False, + requester_as_owner=requester_as_owner, + ) database_ids[str(database.uuid)] = database.id # import datasets with the correct parent ref @@ -93,7 +100,12 @@ class ImportDashboardsCommand(ImportModelsCommand): and config["database_uuid"] in database_ids ): config["database_id"] = database_ids[config["database_uuid"]] - dataset = import_dataset(session, config, overwrite=False) + dataset = import_dataset( + session, + config, + overwrite=False, + requester_as_owner=requester_as_owner, + ) dataset_info[str(dataset.uuid)] = { "datasource_id": dataset.id, "datasource_type": "view" if dataset.is_sqllab_view else "table", @@ -109,7 +121,12 @@ class ImportDashboardsCommand(ImportModelsCommand): ): # update datasource id, type, and name config.update(dataset_info[config["dataset_uuid"]]) - chart = import_chart(session, config, overwrite=False) + chart = import_chart( + session, + config, + overwrite=False, + requester_as_owner=requester_as_owner, + ) chart_ids[str(chart.uuid)] = chart.id # store the existing relationship between dashboards and charts @@ -122,7 +139,13 @@ class ImportDashboardsCommand(ImportModelsCommand): for file_name, config in configs.items(): if file_name.startswith("dashboards/"): config = update_id_refs(config, chart_ids) - dashboard = import_dashboard(session, config, overwrite=overwrite) + dashboard = import_dashboard( + session, + config, + overwrite=overwrite, + requester_as_owner=requester_as_owner, + ) + for uuid in find_chart_uuids(config["position"]): if uuid not in chart_ids: break diff --git a/superset/dashboards/commands/importers/v1/utils.py b/superset/dashboards/commands/importers/v1/utils.py index 7b2ad91..a8775c4 100644 --- a/superset/dashboards/commands/importers/v1/utils.py +++ b/superset/dashboards/commands/importers/v1/utils.py @@ -107,7 +107,10 @@ def update_id_refs(config: Dict[str, Any], chart_ids: Dict[str, int]) -> Dict[st def import_dashboard( - session: Session, config: Dict[str, Any], overwrite: bool = False + session: Session, + config: Dict[str, Any], + overwrite: bool = False, + requester_as_owner: bool = False, ) -> Dashboard: existing = session.query(Dashboard).filter_by(uuid=config["uuid"]).first() if existing: @@ -126,6 +129,9 @@ def import_dashboard( logger.info("Unable to encode `%s` field: %s", key, value) dashboard = Dashboard.import_from_dict(session, config, recursive=False) + logger.info(f"requester_as_owner: {requester_as_owner}") + if requester_as_owner: + dashboard.reset_ownership() if dashboard.id is None: session.flush() diff --git a/superset/databases/commands/importers/v1/__init__.py b/superset/databases/commands/importers/v1/__init__.py index 239bd09..a4d944f 100644 --- a/superset/databases/commands/importers/v1/__init__.py +++ b/superset/databases/commands/importers/v1/__init__.py @@ -44,13 +44,21 @@ class ImportDatabasesCommand(ImportModelsCommand): @staticmethod def _import( - session: Session, configs: Dict[str, Any], overwrite: bool = False + session: Session, + configs: Dict[str, Any], + overwrite: bool = False, + requester_as_owner: bool = False, ) -> None: # first import databases database_ids: Dict[str, int] = {} for file_name, config in configs.items(): if file_name.startswith("databases/"): - database = import_database(session, config, overwrite=overwrite) + database = import_database( + session, + config, + overwrite=overwrite, + requester_as_owner=requester_as_owner, + ) database_ids[str(database.uuid)] = database.id # import related datasets @@ -61,4 +69,9 @@ class ImportDatabasesCommand(ImportModelsCommand): ): config["database_id"] = database_ids[config["database_uuid"]] # overwrite=False prevents deleting any non-imported columns/metrics - import_dataset(session, config, overwrite=False) + import_dataset( + session, + config, + overwrite=False, + requester_as_owner=requester_as_owner, + ) diff --git a/superset/databases/commands/importers/v1/utils.py b/superset/databases/commands/importers/v1/utils.py index 6e016d0..5392af4 100644 --- a/superset/databases/commands/importers/v1/utils.py +++ b/superset/databases/commands/importers/v1/utils.py @@ -24,7 +24,10 @@ from superset.models.core import Database def import_database( - session: Session, config: Dict[str, Any], overwrite: bool = False + session: Session, + config: Dict[str, Any], + overwrite: bool = False, + requester_as_owner: bool = False, ) -> Database: existing = session.query(Database).filter_by(uuid=config["uuid"]).first() if existing: @@ -36,6 +39,8 @@ def import_database( config["extra"] = json.dumps(config["extra"]) database = Database.import_from_dict(session, config, recursive=False) + if requester_as_owner: + database.reset_ownership() if database.id is None: session.flush() diff --git a/superset/datasets/commands/importers/v1/__init__.py b/superset/datasets/commands/importers/v1/__init__.py index e732133..cef94da 100644 --- a/superset/datasets/commands/importers/v1/__init__.py +++ b/superset/datasets/commands/importers/v1/__init__.py @@ -44,7 +44,10 @@ class ImportDatasetsCommand(ImportModelsCommand): @staticmethod def _import( - session: Session, configs: Dict[str, Any], overwrite: bool = False + session: Session, + configs: Dict[str, Any], + overwrite: bool = False, + requester_as_owner: bool = False, ) -> None: # discover databases associated with datasets database_uuids: Set[str] = set() @@ -56,7 +59,12 @@ class ImportDatasetsCommand(ImportModelsCommand): database_ids: Dict[str, int] = {} for file_name, config in configs.items(): if file_name.startswith("databases/") and config["uuid"] in database_uuids: - database = import_database(session, config, overwrite=False) + database = import_database( + session, + config, + overwrite=False, + requester_as_owner=requester_as_owner, + ) database_ids[str(database.uuid)] = database.id # import datasets with the correct parent ref @@ -66,4 +74,9 @@ class ImportDatasetsCommand(ImportModelsCommand): and config["database_uuid"] in database_ids ): config["database_id"] = database_ids[config["database_uuid"]] - import_dataset(session, config, overwrite=overwrite) + import_dataset( + session, + config, + overwrite=overwrite, + requester_as_owner=requester_as_owner, + ) diff --git a/superset/datasets/commands/importers/v1/utils.py b/superset/datasets/commands/importers/v1/utils.py index 6b4dbeb..87b0f42 100644 --- a/superset/datasets/commands/importers/v1/utils.py +++ b/superset/datasets/commands/importers/v1/utils.py @@ -81,6 +81,7 @@ def import_dataset( session: Session, config: Dict[str, Any], overwrite: bool = False, + requester_as_owner: bool = False, force_data: bool = False, ) -> SqlaTable: existing = session.query(SqlaTable).filter_by(uuid=config["uuid"]).first() @@ -113,6 +114,8 @@ def import_dataset( # import recursively to include columns and metrics dataset = SqlaTable.import_from_dict(session, config, recursive=True, sync=sync) + if requester_as_owner: + dataset.reset_ownership() if dataset.id is None: session.flush() diff --git a/superset/queries/saved_queries/commands/importers/v1/__init__.py b/superset/queries/saved_queries/commands/importers/v1/__init__.py index 1412dbd..dac0911 100644 --- a/superset/queries/saved_queries/commands/importers/v1/__init__.py +++ b/superset/queries/saved_queries/commands/importers/v1/__init__.py @@ -46,7 +46,10 @@ class ImportSavedQueriesCommand(ImportModelsCommand): @staticmethod def _import( - session: Session, configs: Dict[str, Any], overwrite: bool = False + session: Session, + configs: Dict[str, Any], + overwrite: bool = False, + requester_as_owner: bool = False, ) -> None: # discover databases associated with saved queries database_uuids: Set[str] = set() @@ -58,7 +61,12 @@ class ImportSavedQueriesCommand(ImportModelsCommand): database_ids: Dict[str, int] = {} for file_name, config in configs.items(): if file_name.startswith("databases/") and config["uuid"] in database_uuids: - database = import_database(session, config, overwrite=False) + database = import_database( + session, + config, + overwrite=False, + requester_as_owner=requester_as_owner, + ) database_ids[str(database.uuid)] = database.id # import saved queries with the correct parent ref
