예제 #1
0
def load_chart_data_into_cache(
    job_metadata: Dict[str, Any],
    form_data: Dict[str, Any],
) -> None:
    # pylint: disable=import-outside-toplevel
    from superset.charts.data.commands.get_data_command import ChartDataCommand

    try:
        ensure_user_is_set(job_metadata.get("user_id"))
        set_form_data(form_data)
        query_context = _create_query_context_from_form(form_data)
        command = ChartDataCommand(query_context)
        result = command.run(cache=True)
        cache_key = result["cache_key"]
        result_url = f"/api/v1/chart/data/{cache_key}"
        async_query_manager.update_job(
            job_metadata,
            async_query_manager.STATUS_DONE,
            result_url=result_url,
        )
    except SoftTimeLimitExceeded as ex:
        logger.warning(
            "A timeout occurred while loading chart data, error: %s", ex)
        raise ex
    except Exception as ex:
        # TODO: QueryContext should support SIP-40 style errors
        error = ex.message if hasattr(ex, "message") else str(ex)  # type: ignore # pylint: disable=no-member
        errors = [{"message": error}]
        async_query_manager.update_job(job_metadata,
                                       async_query_manager.STATUS_ERROR,
                                       errors=errors)
        raise ex
예제 #2
0
    def data_from_cache(self, cache_key: str) -> Response:
        """
        Takes a query context cache key and returns payload
        data response for the given query.
        ---
        get:
          description: >-
            Takes a query context cache key and returns payload data
            response for the given query.
          parameters:
          - in: path
            schema:
              type: string
            name: cache_key
          responses:
            200:
              description: Query result
              content:
                application/json:
                  schema:
                    $ref: "#/components/schemas/ChartDataResponseSchema"
            400:
              $ref: '#/components/responses/400'
            401:
              $ref: '#/components/responses/401'
            404:
              $ref: '#/components/responses/404'
            422:
              $ref: '#/components/responses/422'
            500:
              $ref: '#/components/responses/500'
        """
        try:
            cached_data = self._load_query_context_form_from_cache(cache_key)
            query_context = self._create_query_context_from_form(cached_data)
            command = ChartDataCommand(query_context)
            command.validate()
        except ChartDataCacheLoadError:
            return self.response_404()
        except ValidationError as error:
            return self.response_400(message=_(
                "Request is incorrect: %(error)s", error=error.messages))

        return self._get_data_response(command, True)
예제 #3
0
    def get_data(self, pk: int) -> Response:
        """
        Takes a chart ID and uses the query context stored when the chart was saved
        to return payload data response.
        ---
        get:
          description: >-
            Takes a chart ID and uses the query context stored when the chart was saved
            to return payload data response.
          parameters:
          - in: path
            schema:
              type: integer
            name: pk
            description: The chart ID
          - in: query
            name: format
            description: The format in which the data should be returned
            schema:
              type: string
          - in: query
            name: type
            description: The type in which the data should be returned
            schema:
              type: string
          responses:
            200:
              description: Query result
              content:
                application/json:
                  schema:
                    $ref: "#/components/schemas/ChartDataResponseSchema"
            202:
              description: Async job details
              content:
                application/json:
                  schema:
                    $ref: "#/components/schemas/ChartDataAsyncResponseSchema"
            400:
              $ref: '#/components/responses/400'
            401:
              $ref: '#/components/responses/401'
            500:
              $ref: '#/components/responses/500'
        """
        chart = self.datamodel.get(pk, self._base_filters)
        if not chart:
            return self.response_404()

        try:
            json_body = json.loads(chart.query_context)
        except (TypeError, json.decoder.JSONDecodeError):
            json_body = None

        if json_body is None:
            return self.response_400(message=_(
                "Chart has no query context saved. Please save the chart again."
            ))

        # override saved query context
        json_body["result_format"] = request.args.get(
            "format", ChartDataResultFormat.JSON)
        json_body["result_type"] = request.args.get("type",
                                                    ChartDataResultType.FULL)

        try:
            query_context = self._create_query_context_from_form(json_body)
            command = ChartDataCommand(query_context)
            command.validate()
        except QueryObjectValidationError as error:
            return self.response_400(message=error.message)
        except ValidationError as error:
            return self.response_400(
                message=_("Request is incorrect: %(error)s",
                          error=error.normalized_messages()))

        # TODO: support CSV, SQL query and other non-JSON types
        if (is_feature_enabled("GLOBAL_ASYNC_QUERIES")
                and query_context.result_format == ChartDataResultFormat.JSON
                and query_context.result_type == ChartDataResultType.FULL):
            return self._run_async(json_body, command)

        try:
            form_data = json.loads(chart.params)
        except (TypeError, json.decoder.JSONDecodeError):
            form_data = {}

        return self._get_data_response(command=command,
                                       form_data=form_data,
                                       datasource=query_context.datasource)
예제 #4
0
    def data(self) -> Response:
        """
        Takes a query context constructed in the client and returns payload
        data response for the given query.
        ---
        post:
          description: >-
            Takes a query context constructed in the client and returns payload data
            response for the given query.
          requestBody:
            description: >-
              A query context consists of a datasource from which to fetch data
              and one or many query objects.
            required: true
            content:
              application/json:
                schema:
                  $ref: "#/components/schemas/ChartDataQueryContextSchema"
          responses:
            200:
              description: Query result
              content:
                application/json:
                  schema:
                    $ref: "#/components/schemas/ChartDataResponseSchema"
            202:
              description: Async job details
              content:
                application/json:
                  schema:
                    $ref: "#/components/schemas/ChartDataAsyncResponseSchema"
            400:
              $ref: '#/components/responses/400'
            401:
              $ref: '#/components/responses/401'
            500:
              $ref: '#/components/responses/500'
        """
        json_body = None
        if request.is_json:
            json_body = request.json
        elif request.form.get("form_data"):
            # CSV export submits regular form data
            try:
                json_body = json.loads(request.form["form_data"])
            except (TypeError, json.JSONDecodeError):
                pass

        if json_body is None:
            return self.response_400(message=_("Request is not JSON"))

        try:
            query_context = self._create_query_context_from_form(json_body)
            command = ChartDataCommand(query_context)
            command.validate()
        except QueryObjectValidationError as error:
            return self.response_400(message=error.message)
        except ValidationError as error:
            return self.response_400(
                message=_("Request is incorrect: %(error)s",
                          error=error.normalized_messages()))

        # TODO: support CSV, SQL query and other non-JSON types
        if (is_feature_enabled("GLOBAL_ASYNC_QUERIES")
                and query_context.result_format == ChartDataResultFormat.JSON
                and query_context.result_type == ChartDataResultType.FULL):
            return self._run_async(json_body, command)

        form_data = json_body.get("form_data")
        return self._get_data_response(command,
                                       form_data=form_data,
                                       datasource=query_context.datasource)