2020-05-28 00:02:00 +00:00
|
|
|
//! Types for Relay-compliant server
|
|
|
|
|
2020-03-21 01:32:13 +00:00
|
|
|
mod connection_type;
|
2020-05-05 05:21:45 +00:00
|
|
|
mod cursor;
|
2020-03-19 09:20:12 +00:00
|
|
|
mod edge;
|
|
|
|
mod page_info;
|
|
|
|
mod slice;
|
|
|
|
|
2020-05-28 00:02:00 +00:00
|
|
|
use crate::{Context, FieldResult, ObjectType, OutputValueType};
|
2020-03-21 01:32:13 +00:00
|
|
|
pub use connection_type::Connection;
|
2020-05-28 00:02:00 +00:00
|
|
|
pub use cursor::CursorType;
|
|
|
|
pub use edge::Edge;
|
2020-05-05 22:52:04 +00:00
|
|
|
pub use page_info::PageInfo;
|
2020-05-28 00:02:00 +00:00
|
|
|
use std::fmt::Display;
|
2020-03-19 09:20:12 +00:00
|
|
|
|
2020-05-28 00:02:00 +00:00
|
|
|
/// Empty additional fields
|
2020-04-17 03:06:33 +00:00
|
|
|
#[async_graphql_derive::SimpleObject(internal)]
|
2020-05-28 00:02:00 +00:00
|
|
|
pub struct EmptyFields;
|
2020-05-05 07:22:01 +00:00
|
|
|
|
2020-03-20 03:56:08 +00:00
|
|
|
/// Data source of GraphQL Cursor Connections type
|
2020-03-19 09:20:12 +00:00
|
|
|
///
|
|
|
|
/// `Edge` is an extension object type that extends the edge fields, If you don't need it, you can use `EmptyEdgeFields`.
|
|
|
|
///
|
|
|
|
/// # References
|
|
|
|
/// (GraphQL Cursor Connections Specification)[https://facebook.github.io/relay/graphql/connections.htm]
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```rust
|
|
|
|
/// use async_graphql::*;
|
2020-05-28 00:02:00 +00:00
|
|
|
/// use async_graphql::connection::*;
|
2020-03-19 09:20:12 +00:00
|
|
|
///
|
|
|
|
/// struct QueryRoot;
|
|
|
|
///
|
2020-05-28 00:02:00 +00:00
|
|
|
/// struct Numbers;
|
|
|
|
///
|
2020-04-17 03:06:33 +00:00
|
|
|
/// #[SimpleObject]
|
2020-05-28 00:02:00 +00:00
|
|
|
/// struct Diff {
|
2020-04-17 03:06:33 +00:00
|
|
|
/// diff: i32,
|
2020-03-19 09:20:12 +00:00
|
|
|
/// }
|
|
|
|
///
|
2020-04-17 03:06:33 +00:00
|
|
|
/// #[DataSource]
|
2020-03-19 09:20:12 +00:00
|
|
|
/// impl DataSource for Numbers {
|
2020-05-28 00:02:00 +00:00
|
|
|
/// type CursorType = usize;
|
|
|
|
/// type NodeType = i32;
|
|
|
|
/// type ConnectionFieldsType = EmptyFields;
|
|
|
|
/// type EdgeFieldsType = Diff;
|
2020-03-19 09:20:12 +00:00
|
|
|
///
|
2020-05-28 00:02:00 +00:00
|
|
|
/// async fn execute_query(&self,
|
|
|
|
/// ctx: &Context<'_>,
|
|
|
|
/// after: Option<usize>,
|
|
|
|
/// before: Option<usize>,
|
|
|
|
/// first: Option<usize>,
|
|
|
|
/// last: Option<usize>,
|
|
|
|
/// ) -> FieldResult<Connection<Self::CursorType, Self::NodeType, Self::ConnectionFieldsType, Self::EdgeFieldsType>> {
|
|
|
|
/// let mut start = after.map(|after| after + 1).unwrap_or(0);
|
|
|
|
/// let mut end = before.unwrap_or(10000);
|
|
|
|
/// if let Some(first) = first {
|
|
|
|
/// end = (start + first).min(end);
|
|
|
|
/// }
|
|
|
|
/// if let Some(last) = last {
|
|
|
|
/// start = if last > end - start {
|
|
|
|
/// end
|
|
|
|
/// } else {
|
|
|
|
/// end - last
|
|
|
|
/// };
|
|
|
|
/// }
|
|
|
|
/// let mut connection = Connection::new(start > 0, end < 10000);
|
|
|
|
/// connection.append(
|
|
|
|
/// (start..end).into_iter().map(|n|
|
2020-05-29 01:24:31 +00:00
|
|
|
/// Edge::new_with_additional_fields(n, n as i32, Diff{ diff: (10000 - n) as i32 })),
|
|
|
|
/// );
|
2020-05-28 00:02:00 +00:00
|
|
|
/// Ok(connection)
|
2020-03-19 09:20:12 +00:00
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// #[Object]
|
|
|
|
/// impl QueryRoot {
|
|
|
|
/// async fn numbers(&self, ctx: &Context<'_>,
|
2020-05-28 00:02:00 +00:00
|
|
|
/// after: Option<String>,
|
|
|
|
/// before: Option<String>,
|
2020-03-19 09:20:12 +00:00
|
|
|
/// first: Option<i32>,
|
|
|
|
/// last: Option<i32>
|
2020-05-28 00:02:00 +00:00
|
|
|
/// ) -> FieldResult<Connection<usize, i32, EmptyFields, Diff>> {
|
2020-03-19 09:20:12 +00:00
|
|
|
/// Numbers.query(ctx, after, before, first, last).await
|
|
|
|
/// }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// #[async_std::main]
|
|
|
|
/// async fn main() {
|
|
|
|
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
|
|
|
///
|
2020-05-28 00:02:00 +00:00
|
|
|
/// assert_eq!(schema.execute("{ numbers(first: 2) { edges { node diff } } }").await.unwrap().data, serde_json::json!({
|
2020-03-19 09:20:12 +00:00
|
|
|
/// "numbers": {
|
|
|
|
/// "edges": [
|
2020-05-28 00:02:00 +00:00
|
|
|
/// {"node": 0, "diff": 10000},
|
|
|
|
/// {"node": 1, "diff": 9999},
|
2020-03-19 09:20:12 +00:00
|
|
|
/// ]
|
|
|
|
/// },
|
|
|
|
/// }));
|
|
|
|
///
|
2020-04-02 04:53:53 +00:00
|
|
|
/// assert_eq!(schema.execute("{ numbers(last: 2) { edges { node diff } } }").await.unwrap().data, serde_json::json!({
|
2020-03-19 09:20:12 +00:00
|
|
|
/// "numbers": {
|
|
|
|
/// "edges": [
|
2020-05-28 00:02:00 +00:00
|
|
|
/// {"node": 9998, "diff": 2},
|
|
|
|
/// {"node": 9999, "diff": 1},
|
2020-03-19 09:20:12 +00:00
|
|
|
/// ]
|
|
|
|
/// },
|
|
|
|
/// }));
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
#[async_trait::async_trait]
|
2020-05-28 00:02:00 +00:00
|
|
|
pub trait DataSource {
|
|
|
|
/// Cursor type
|
|
|
|
type CursorType: CursorType + Send + Sync;
|
|
|
|
|
2020-03-20 03:56:08 +00:00
|
|
|
/// Record type
|
2020-05-28 00:02:00 +00:00
|
|
|
type NodeType: OutputValueType + Send;
|
2020-03-19 09:20:12 +00:00
|
|
|
|
2020-05-28 00:02:00 +00:00
|
|
|
/// Additional fields for connection
|
|
|
|
///
|
|
|
|
/// Is a type that implements `ObjectType` and can be defined by the procedure macro `#[Object]` or `#[SimpleObject]`.
|
|
|
|
///
|
|
|
|
type ConnectionFieldsType: ObjectType + Send;
|
|
|
|
|
|
|
|
/// Additional fields for edge
|
|
|
|
///
|
|
|
|
/// Is a type that implements `ObjectType` and can be defined by the procedure macro `#[Object]` or `#[SimpleObject]`.
|
2020-03-20 03:56:08 +00:00
|
|
|
///
|
2020-05-28 00:02:00 +00:00
|
|
|
type EdgeFieldsType: ObjectType + Send;
|
2020-03-20 03:56:08 +00:00
|
|
|
|
2020-05-28 00:02:00 +00:00
|
|
|
/// Parses the parameters and executes the query.
|
2020-03-19 09:20:12 +00:00
|
|
|
async fn query(
|
2020-05-28 00:02:00 +00:00
|
|
|
&self,
|
2020-05-07 00:04:26 +00:00
|
|
|
ctx: &Context<'_>,
|
2020-05-28 00:02:00 +00:00
|
|
|
after: Option<String>,
|
|
|
|
before: Option<String>,
|
2020-03-19 09:20:12 +00:00
|
|
|
first: Option<i32>,
|
|
|
|
last: Option<i32>,
|
2020-05-28 00:02:00 +00:00
|
|
|
) -> FieldResult<
|
|
|
|
Connection<
|
|
|
|
Self::CursorType,
|
|
|
|
Self::NodeType,
|
|
|
|
Self::ConnectionFieldsType,
|
|
|
|
Self::EdgeFieldsType,
|
|
|
|
>,
|
|
|
|
>
|
|
|
|
where
|
|
|
|
<Self::CursorType as CursorType>::Error: Display + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
if first.is_some() && last.is_some() {
|
|
|
|
return Err(
|
|
|
|
"The \"first\" and \"last\" parameters cannot exist at the same time".into(),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
let first = match first {
|
|
|
|
Some(first) if first < 0 => {
|
|
|
|
return Err("The \"first\" parameter must be a non-negative number".into())
|
|
|
|
}
|
|
|
|
Some(first) => Some(first as usize),
|
|
|
|
None => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let last = match last {
|
|
|
|
Some(last) if last < 0 => {
|
|
|
|
return Err("The \"last\" parameter must be a non-negative number".into())
|
|
|
|
}
|
|
|
|
Some(last) => Some(last as usize),
|
|
|
|
None => None,
|
|
|
|
};
|
|
|
|
|
|
|
|
let before = match before {
|
|
|
|
Some(before) => Some(Self::CursorType::decode_cursor(&before)?),
|
|
|
|
None => None,
|
2020-05-05 07:22:01 +00:00
|
|
|
};
|
|
|
|
|
2020-05-28 00:02:00 +00:00
|
|
|
let after = match after {
|
|
|
|
Some(after) => Some(Self::CursorType::decode_cursor(&after)?),
|
|
|
|
None => None,
|
2020-03-19 09:20:12 +00:00
|
|
|
};
|
|
|
|
|
2020-05-28 00:02:00 +00:00
|
|
|
self.execute_query(ctx, after, before, first, last).await
|
2020-03-19 09:20:12 +00:00
|
|
|
}
|
|
|
|
|
2020-05-28 00:02:00 +00:00
|
|
|
/// Execute query
|
|
|
|
async fn execute_query(
|
|
|
|
&self,
|
2020-05-07 00:04:26 +00:00
|
|
|
ctx: &Context<'_>,
|
2020-05-28 00:02:00 +00:00
|
|
|
after: Option<Self::CursorType>,
|
|
|
|
before: Option<Self::CursorType>,
|
|
|
|
first: Option<usize>,
|
|
|
|
last: Option<usize>,
|
|
|
|
) -> FieldResult<
|
|
|
|
Connection<
|
|
|
|
Self::CursorType,
|
|
|
|
Self::NodeType,
|
|
|
|
Self::ConnectionFieldsType,
|
|
|
|
Self::EdgeFieldsType,
|
|
|
|
>,
|
|
|
|
>;
|
2020-03-19 09:20:12 +00:00
|
|
|
}
|