pub trait DataService {
    type Query: DeserializeOwned + Send + Sync;
    type QueryError: DataQueryError;
    type Stream: Stream<Item = Result<DataResponse, Self::QueryError>> + Send;

    // Required method
    fn query_data<'life0, 'async_trait>(
        &'life0 self,
        request: QueryDataRequest<Self::Query>
    ) -> Pin<Box<dyn Future<Output = Self::Stream> + Send + 'async_trait>>
       where Self: 'async_trait,
             'life0: 'async_trait;
}
Expand description

Used to respond for requests for data from datasources and app plugins.

Datasource plugins will usually want to implement this trait to perform the bulk of their processing.

Example

use futures_util::stream::FuturesOrdered;
use grafana_plugin_sdk::{backend, data, prelude::*};
use thiserror::Error;

struct MyPlugin;

/// An error that may occur during a query.
///
/// This must store the `ref_id` of the query so that Grafana can line it up.
#[derive(Debug, Error)]
#[error("Error querying backend for query {ref_id}: {source}")]
struct QueryError {
    source: data::Error,
    ref_id: String,
}

impl backend::DataQueryError for QueryError {
    fn ref_id(self) -> String {
        self.ref_id
    }
}

#[backend::async_trait]
impl backend::DataService for MyPlugin {

    /// The type of JSON data sent from Grafana to our backend plugin.
    ///
    /// This will correspond to the `TQuery` type parameter of the frontend
    /// datasource.
    ///
    /// We can use `serde_json::Value` if we want to accept any JSON.
    type Query = serde_json::Value;

    /// The type of error that could be returned by an individual query.
    type QueryError = QueryError;

    /// The type of iterator we're returning.
    ///
    /// In general the concrete type will be impossible to name in advance,
    /// so the `backend::BoxDataResponseStream` type alias will be useful.
    type Stream = backend::BoxDataResponseStream<Self::QueryError>;

    /// Respond to a request for data from Grafana.
    ///
    /// This request will contain zero or more queries, as well as information
    /// about the datasource instance on behalf of which this request is made,
    /// such as address, credentials, etc.
    ///
    /// Our plugin must respond to each query and return an iterator of `DataResponse`s,
    /// which themselves can contain zero or more `Frame`s.
    async fn query_data(&self, request: backend::QueryDataRequest<Self::Query>) -> Self::Stream {
        Box::pin(
            request
                .queries
                .into_iter()
                .map(|x| async {
                    // Here we create a single response Frame for each query.
                    // Frames can be created from iterators of fields using [`IntoFrame`].
                    Ok(backend::DataResponse::new(
                        x.ref_id.clone(),
                        // Return zero or more frames.
                        // A real implementation would fetch this data from a database
                        // or something.
                        vec![[
                            [1_u32, 2, 3].into_field("x"),
                            ["a", "b", "c"].into_field("y"),
                        ]
                        .into_frame("foo")
                        .check()
                        .map_err(|source| QueryError {
                            ref_id: x.ref_id,
                            source,
                        })?],
                    ))
                })
                .collect::<FuturesOrdered<_>>(),
        )
    }
}

Required Associated Types§

source

type Query: DeserializeOwned + Send + Sync

The type of the JSON query sent from Grafana to the plugin.

source

type QueryError: DataQueryError

The error type that can be returned by individual queries.

This must implement DataQueryError, which allows the SDK to align queries up with any failed requests.

source

type Stream: Stream<Item = Result<DataResponse, Self::QueryError>> + Send

The type of stream returned by the query_data method.

This will generally be impossible to name directly, so returning the BoxDataResponseStream type alias will probably be more convenient.

Required Methods§

source

fn query_data<'life0, 'async_trait>( &'life0 self, request: QueryDataRequest<Self::Query> ) -> Pin<Box<dyn Future<Output = Self::Stream> + Send + 'async_trait>>
where Self: 'async_trait, 'life0: 'async_trait,

Query data for an input request.

The request will contain zero or more queries, as well as information about the origin of the queries (such as the datasource instance) in the plugin_context field.

Implementors§