Exemplo n.º 1
0
    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")
Exemplo n.º 2
0
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)
Exemplo n.º 3
0
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)
Exemplo n.º 4
0
    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))
Exemplo n.º 5
0
    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")
Exemplo n.º 6
0
    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")
Exemplo n.º 7
0
    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")
Exemplo n.º 8
0
    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")
Exemplo n.º 9
0
    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))