def import_(self) -> Response: """Import multiple assets --- post: requestBody: required: true content: multipart/form-data: schema: type: object properties: bundle: description: upload file (ZIP or JSON) type: string format: binary passwords: description: >- JSON map of passwords for each featured database in the ZIP file. If the ZIP includes a database config in the path `databases/MyDatabase.yaml`, the password should be provided in the following format: `{"databases/MyDatabase.yaml": "my_password"}`. type: string responses: 200: description: Dashboard import result content: application/json: schema: type: object properties: message: type: string 400: $ref: '#/components/responses/400' 401: $ref: '#/components/responses/401' 422: $ref: '#/components/responses/422' 500: $ref: '#/components/responses/500' """ upload = request.files.get("bundle") if not upload: return self.response_400() if not is_zipfile(upload): raise IncorrectFormatError("Not a ZIP file") with ZipFile(upload) as bundle: contents = get_contents_from_bundle(bundle) if not contents: raise NoValidFilesFoundError() passwords = (json.loads(request.form["passwords"]) if "passwords" in request.form else None) command = ImportAssetsCommand(contents, passwords=passwords) command.run() return self.response(200, message="OK")
def test_export_assets(mocker: MockFixture, client: Any) -> 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", "version: 1.0.0\ntype: assets\ntimestamp: '2022-01-01T00:00:00+00:00'\n", ), ("databases/example.yaml", "<DATABASE CONTENTS>"), ] ExportAssetsCommand = mocker.patch( "superset.importexport.api.ExportAssetsCommand") ExportAssetsCommand().run.return_value = mocked_contents[:] response = client.get("/api/v1/assets/export/") assert response.status_code == 200 buf = BytesIO(response.data) assert is_zipfile(buf) buf.seek(0) with ZipFile(buf) as bundle: contents = get_contents_from_bundle(bundle) assert contents == dict(mocked_contents)
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 mocked_contents = [ ( "metadata.yaml", "version: 1.0.0\ntype: assets\ntimestamp: '2022-01-01T00:00:00+00:00'\n", ), ("databases/example.yaml", "<DATABASE CONTENTS>"), ] ExportAssetsCommand = mocker.patch( "superset.importexport.api.ExportAssetsCommand") ExportAssetsCommand().run.return_value = mocked_contents[:] response = client.get("/api/v1/assets/export/") assert response.status_code == 200 buf = BytesIO(response.data) assert is_zipfile(buf) buf.seek(0) with ZipFile(buf) as bundle: contents = get_contents_from_bundle(bundle) assert contents == dict(mocked_contents)
def import_(self) -> Response: """Import database(s) with associated datasets --- post: requestBody: required: true content: multipart/form-data: schema: type: object properties: formData: type: string format: binary passwords: type: string overwrite: type: bool responses: 200: description: Database import result content: application/json: schema: type: object properties: message: type: string 400: $ref: '#/components/responses/400' 401: $ref: '#/components/responses/401' 422: $ref: '#/components/responses/422' 500: $ref: '#/components/responses/500' """ upload = request.files.get("formData") if not upload: return self.response_400() with ZipFile(upload) as bundle: contents = get_contents_from_bundle(bundle) passwords = (json.loads(request.form["passwords"]) if "passwords" in request.form else None) overwrite = request.form.get("overwrite") == "true" command = ImportDatabasesCommand(contents, passwords=passwords, overwrite=overwrite) try: command.run() return self.response(200, message="OK") except CommandInvalidError as exc: logger.warning("Import database failed") return self.response_422(message=exc.normalized_messages()) except DatabaseImportError as exc: logger.exception("Import database failed") return self.response_500(message=str(exc))
def import_(self) -> Response: """Import database(s) with associated datasets --- post: requestBody: required: true content: multipart/form-data: schema: type: object properties: formData: description: upload file (ZIP) type: string format: binary passwords: description: JSON map of passwords for each file type: string overwrite: description: overwrite existing databases? type: boolean responses: 200: description: Database import result content: application/json: schema: type: object properties: message: type: string 400: $ref: '#/components/responses/400' 401: $ref: '#/components/responses/401' 422: $ref: '#/components/responses/422' 500: $ref: '#/components/responses/500' """ upload = request.files.get("formData") if not upload: return self.response_400() with ZipFile(upload) as bundle: contents = get_contents_from_bundle(bundle) if not contents: raise NoValidFilesFoundError() passwords = (json.loads(request.form["passwords"]) if "passwords" in request.form else None) overwrite = request.form.get("overwrite") == "true" command = ImportDatabasesCommand(contents, passwords=passwords, overwrite=overwrite) command.run() return self.response(200, message="OK")
def import_datasources(path: str) -> None: """Import datasources from ZIP file""" from superset.commands.importers.v1.utils import get_contents_from_bundle from superset.datasets.commands.importers.dispatcher import ( ImportDatasetsCommand, ) if is_zipfile(path): with ZipFile(path) as bundle: contents = get_contents_from_bundle(bundle) else: contents = {path: open(path).read()} try: ImportDatasetsCommand(contents, overwrite=True).run() except Exception: # pylint: disable=broad-except logger.exception( "There was an error when importing the dataset(s), please check the " "exception traceback in the log")
def import_dashboards(path: str, username: Optional[str]) -> None: """Import dashboards from ZIP file""" from superset.commands.importers.v1.utils import get_contents_from_bundle from superset.dashboards.commands.importers.dispatcher import ( ImportDashboardsCommand, ) if username is not None: g.user = security_manager.find_user(username=username) if is_zipfile(path): with ZipFile(path) as bundle: contents = get_contents_from_bundle(bundle) else: contents = {path: open(path).read()} try: ImportDashboardsCommand(contents, overwrite=True).run() except Exception: # pylint: disable=broad-except logger.exception( "There was an error when importing the dashboards(s), please check " "the exception traceback in the log")
def import_(self) -> Response: """Import dashboard(s) with associated charts/datasets/databases --- post: requestBody: required: true content: multipart/form-data: schema: type: object properties: formData: description: upload file (ZIP or JSON) type: string format: binary passwords: description: >- JSON map of passwords for each featured database in the ZIP file. If the ZIP includes a database config in the path `databases/MyDatabase.yaml`, the password should be provided in the following format: `{"databases/MyDatabase.yaml": "my_password"}`. type: string overwrite: description: overwrite existing dashboards? type: boolean responses: 200: description: Dashboard import result content: application/json: schema: type: object properties: message: type: string 400: $ref: '#/components/responses/400' 401: $ref: '#/components/responses/401' 422: $ref: '#/components/responses/422' 500: $ref: '#/components/responses/500' """ upload = request.files.get("formData") if not upload: return self.response_400() if is_zipfile(upload): with ZipFile(upload) as bundle: contents = get_contents_from_bundle(bundle) else: upload.seek(0) contents = {upload.filename: upload.read()} if not contents: raise NoValidFilesFoundError() passwords = (json.loads(request.form["passwords"]) if "passwords" in request.form else None) overwrite = request.form.get("overwrite") == "true" command = ImportDashboardsCommand(contents, passwords=passwords, overwrite=overwrite) command.run() return self.response(200, message="OK")
def import_(self) -> Response: """Import dataset(s) with associated databases --- post: requestBody: required: true content: multipart/form-data: schema: type: object properties: formData: description: upload file (ZIP or YAML) type: string format: binary passwords: description: JSON map of passwords for each file type: string overwrite: description: overwrite existing datasets? type: boolean responses: 200: description: Dataset import result content: application/json: schema: type: object properties: message: type: string 400: $ref: '#/components/responses/400' 401: $ref: '#/components/responses/401' 422: $ref: '#/components/responses/422' 500: $ref: '#/components/responses/500' """ upload = request.files.get("formData") if not upload: return self.response_400() if is_zipfile(upload): with ZipFile(upload) as bundle: contents = get_contents_from_bundle(bundle) else: upload.seek(0) contents = {upload.filename: upload.read()} passwords = (json.loads(request.form["passwords"]) if "passwords" in request.form else None) overwrite = request.form.get("overwrite") == "true" command = ImportDatasetsCommand(contents, passwords=passwords, overwrite=overwrite) try: command.run() return self.response(200, message="OK") except CommandInvalidError as exc: logger.warning("Import dataset failed") return self.response_422(message=exc.normalized_messages()) except DatasetImportError as exc: logger.error("Import dataset failed", exc_info=True) return self.response_500(message=str(exc))