Remove connection::DataSource
This commit is contained in:
parent
1124124099
commit
c05127bb3e
|
@ -4,7 +4,6 @@ mod connection_type;
|
|||
mod cursor;
|
||||
mod edge;
|
||||
mod page_info;
|
||||
mod slice;
|
||||
|
||||
use crate::FieldResult;
|
||||
pub use connection_type::Connection;
|
||||
|
@ -18,158 +17,6 @@ use std::fmt::Display;
|
|||
#[async_graphql_derive::SimpleObject(internal)]
|
||||
pub struct EmptyFields;
|
||||
|
||||
/// Data source of GraphQL Cursor Connections type
|
||||
///
|
||||
/// `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::*;
|
||||
/// use async_graphql::connection::*;
|
||||
///
|
||||
/// struct QueryRoot;
|
||||
///
|
||||
/// struct Numbers;
|
||||
///
|
||||
/// #[SimpleObject]
|
||||
/// struct Diff {
|
||||
/// diff: i32,
|
||||
/// }
|
||||
///
|
||||
/// #[DataSource]
|
||||
/// impl DataSource for Numbers {
|
||||
/// type CursorType = usize;
|
||||
/// type NodeType = i32;
|
||||
/// type ConnectionFieldsType = EmptyFields;
|
||||
/// type EdgeFieldsType = Diff;
|
||||
///
|
||||
/// async fn execute_query(&self,
|
||||
/// 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|
|
||||
/// Edge::with_additional_fields(n, n as i32, Diff{ diff: (10000 - n) as i32 })),
|
||||
/// );
|
||||
/// Ok(connection)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[Object]
|
||||
/// impl QueryRoot {
|
||||
/// async fn numbers(&self,
|
||||
/// after: Option<String>,
|
||||
/// before: Option<String>,
|
||||
/// first: Option<i32>,
|
||||
/// last: Option<i32>
|
||||
/// ) -> FieldResult<Connection<usize, i32, EmptyFields, Diff>> {
|
||||
/// Numbers.query(after, before, first, last).await
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[async_std::main]
|
||||
/// async fn main() {
|
||||
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
|
||||
///
|
||||
/// assert_eq!(schema.execute("{ numbers(first: 2) { edges { node diff } } }").await.unwrap().data, serde_json::json!({
|
||||
/// "numbers": {
|
||||
/// "edges": [
|
||||
/// {"node": 0, "diff": 10000},
|
||||
/// {"node": 1, "diff": 9999},
|
||||
/// ]
|
||||
/// },
|
||||
/// }));
|
||||
///
|
||||
/// assert_eq!(schema.execute("{ numbers(last: 2) { edges { node diff } } }").await.unwrap().data, serde_json::json!({
|
||||
/// "numbers": {
|
||||
/// "edges": [
|
||||
/// {"node": 9998, "diff": 2},
|
||||
/// {"node": 9999, "diff": 1},
|
||||
/// ]
|
||||
/// },
|
||||
/// }));
|
||||
/// }
|
||||
/// ```
|
||||
#[async_trait::async_trait]
|
||||
pub trait DataSource {
|
||||
/// Cursor type
|
||||
type CursorType: CursorType + Send + Sync;
|
||||
|
||||
/// Record type
|
||||
type NodeType;
|
||||
|
||||
/// Additional fields for connection
|
||||
///
|
||||
/// Is a type that implements `ObjectType` and can be defined by the procedure macro `#[Object]` or `#[SimpleObject]`.
|
||||
///
|
||||
type ConnectionFieldsType;
|
||||
|
||||
/// Additional fields for edge
|
||||
///
|
||||
/// Is a type that implements `ObjectType` and can be defined by the procedure macro `#[Object]` or `#[SimpleObject]`.
|
||||
///
|
||||
type EdgeFieldsType;
|
||||
|
||||
/// Parses the parameters and executes the query.
|
||||
async fn query(
|
||||
&self,
|
||||
after: Option<String>,
|
||||
before: Option<String>,
|
||||
first: Option<i32>,
|
||||
last: Option<i32>,
|
||||
) -> FieldResult<
|
||||
Connection<
|
||||
Self::CursorType,
|
||||
Self::NodeType,
|
||||
Self::ConnectionFieldsType,
|
||||
Self::EdgeFieldsType,
|
||||
>,
|
||||
>
|
||||
where
|
||||
<Self::CursorType as CursorType>::Error: Display + Send + Sync + 'static,
|
||||
{
|
||||
query(after, before, first, last, |after, before, first, last| {
|
||||
self.execute_query(after, before, first, last)
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Execute query
|
||||
async fn execute_query(
|
||||
&self,
|
||||
after: Option<Self::CursorType>,
|
||||
before: Option<Self::CursorType>,
|
||||
first: Option<usize>,
|
||||
last: Option<usize>,
|
||||
) -> FieldResult<
|
||||
Connection<
|
||||
Self::CursorType,
|
||||
Self::NodeType,
|
||||
Self::ConnectionFieldsType,
|
||||
Self::EdgeFieldsType,
|
||||
>,
|
||||
>;
|
||||
}
|
||||
|
||||
/// Parses the parameters and executes the query.
|
||||
///
|
||||
/// If you don't want to implement `DataSource`, you can also use this function to query data directly.
|
||||
|
@ -262,7 +109,7 @@ where
|
|||
|
||||
let first = match first {
|
||||
Some(first) if first < 0 => {
|
||||
return Err("The \"first\" parameter must be a non-negative number".into())
|
||||
return Err("The \"first\" parameter must be a non-negative number".into());
|
||||
}
|
||||
Some(first) => Some(first as usize),
|
||||
None => None,
|
||||
|
@ -270,7 +117,7 @@ where
|
|||
|
||||
let last = match last {
|
||||
Some(last) if last < 0 => {
|
||||
return Err("The \"last\" parameter must be a non-negative number".into())
|
||||
return Err("The \"last\" parameter must be a non-negative number".into());
|
||||
}
|
||||
Some(last) => Some(last as usize),
|
||||
None => None,
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
use crate::connection::{Connection, DataSource, Edge, EmptyFields};
|
||||
use crate::FieldResult;
|
||||
use async_graphql_derive::DataSource;
|
||||
|
||||
#[DataSource(internal)]
|
||||
impl<'a, T: Send + Sync> DataSource for &'a [T] {
|
||||
type CursorType = usize;
|
||||
type NodeType = &'a T;
|
||||
type ConnectionFieldsType = EmptyFields;
|
||||
type EdgeFieldsType = EmptyFields;
|
||||
|
||||
#[allow(clippy::suspicious_else_formatting)]
|
||||
async fn execute_query(
|
||||
&self,
|
||||
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 = 0usize;
|
||||
let mut end = self.len();
|
||||
|
||||
if let Some(after) = after {
|
||||
if after >= self.len() {
|
||||
return Ok(Connection::new(false, false));
|
||||
}
|
||||
start = after + 1;
|
||||
}
|
||||
|
||||
if let Some(before) = before {
|
||||
if before == 0 {
|
||||
return Ok(Connection::new(false, false));
|
||||
}
|
||||
end = before;
|
||||
}
|
||||
|
||||
let mut slice = &self[start..end];
|
||||
|
||||
if let Some(first) = first {
|
||||
slice = &slice[..first.min(slice.len())];
|
||||
end -= first.min(slice.len());
|
||||
} else if let Some(last) = last {
|
||||
slice = &slice[slice.len() - last.min(slice.len())..];
|
||||
start = end - last.min(slice.len());
|
||||
}
|
||||
|
||||
let mut connection = Connection::new(start > 0, end < self.len());
|
||||
connection.append(
|
||||
slice
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, item)| Edge::new(start + idx, item)),
|
||||
);
|
||||
Ok(connection)
|
||||
}
|
||||
}
|
|
@ -1,126 +1,10 @@
|
|||
use async_graphql::connection::*;
|
||||
use async_graphql::*;
|
||||
use serde_json::json;
|
||||
|
||||
#[async_std::test]
|
||||
pub async fn test_slice_datasource() {
|
||||
struct Query;
|
||||
|
||||
#[Object]
|
||||
impl Query {
|
||||
async fn values(
|
||||
&self,
|
||||
after: Option<String>,
|
||||
before: Option<String>,
|
||||
first: Option<i32>,
|
||||
last: Option<i32>,
|
||||
) -> FieldResult<Connection<usize, &&str>> {
|
||||
const ROWS: &[&str] = &[
|
||||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p",
|
||||
"q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
|
||||
];
|
||||
ROWS.query(after, before, first, last).await
|
||||
}
|
||||
}
|
||||
|
||||
static QUERY: &str = r#"
|
||||
query(
|
||||
$after: String
|
||||
$before: String
|
||||
$first: Int
|
||||
$last: Int
|
||||
) {
|
||||
values(
|
||||
after: $after
|
||||
before: $before
|
||||
first: $first
|
||||
last: $last
|
||||
) {
|
||||
pageInfo {
|
||||
hasPreviousPage
|
||||
hasNextPage
|
||||
}
|
||||
edges {
|
||||
node
|
||||
}
|
||||
}
|
||||
}
|
||||
"#;
|
||||
|
||||
async fn do_test(
|
||||
vars: serde_json::Value,
|
||||
(has_prev_page, has_next_page, nodes): (bool, bool, &[&str]),
|
||||
) {
|
||||
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
|
||||
let query = QueryBuilder::new(QUERY).variables(Variables::parse_from_json(vars).unwrap());
|
||||
let edges = nodes
|
||||
.iter()
|
||||
.map(|s| json!({ "node": s.to_owned() }))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(
|
||||
query.execute(&schema).await.unwrap().data,
|
||||
serde_json::json!({
|
||||
"values": {
|
||||
"pageInfo": {
|
||||
"hasPreviousPage": has_prev_page,
|
||||
"hasNextPage": has_next_page,
|
||||
},
|
||||
"edges": edges,
|
||||
},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
do_test(
|
||||
json!({ "after": null, "before": null, "first": 2, "last": null }),
|
||||
(false, true, &["a", "b"]),
|
||||
)
|
||||
.await;
|
||||
|
||||
do_test(
|
||||
json!({ "after": 1usize.encode_cursor().unwrap(), "before": null, "first": 2, "last": null }),
|
||||
(true, true, &["c", "d"]),
|
||||
)
|
||||
.await;
|
||||
|
||||
do_test(
|
||||
json!({ "after": 1usize.encode_cursor().unwrap(), "before": null, "first": 6, "last": null }),
|
||||
(true, true, &["c", "d", "e", "f", "g", "h"]),
|
||||
)
|
||||
.await;
|
||||
|
||||
do_test(
|
||||
json!({ "after": null, "before": null, "first": 3, "last": null }),
|
||||
(false, true, &["a", "b", "c"]),
|
||||
)
|
||||
.await;
|
||||
|
||||
do_test(
|
||||
json!({ "after": null, "before": null, "first": null, "last": 3 }),
|
||||
(true, false, &["x", "y", "z"]),
|
||||
)
|
||||
.await;
|
||||
|
||||
do_test(
|
||||
json!({ "after": null, "before": 1usize.encode_cursor().unwrap(), "first": 10, "last": null }),
|
||||
(false, true, &["a"]),
|
||||
)
|
||||
.await;
|
||||
|
||||
do_test(
|
||||
json!({ "after": null, "before": 1usize.encode_cursor().unwrap(), "first": null, "last": 10 }),
|
||||
(false, true, &["a"]),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[async_std::test]
|
||||
pub async fn test_datasource_additional_fields() {
|
||||
struct QueryRoot;
|
||||
|
||||
struct Numbers;
|
||||
|
||||
#[SimpleObject]
|
||||
struct ConnectionFields {
|
||||
total_count: i32,
|
||||
|
@ -131,27 +15,21 @@ pub async fn test_datasource_additional_fields() {
|
|||
diff: i32,
|
||||
}
|
||||
|
||||
#[DataSource]
|
||||
impl DataSource for Numbers {
|
||||
type CursorType = usize;
|
||||
type NodeType = i32;
|
||||
type ConnectionFieldsType = ConnectionFields;
|
||||
type EdgeFieldsType = Diff;
|
||||
|
||||
async fn execute_query(
|
||||
#[Object]
|
||||
impl QueryRoot {
|
||||
async fn numbers(
|
||||
&self,
|
||||
after: Option<usize>,
|
||||
before: Option<usize>,
|
||||
first: Option<usize>,
|
||||
last: Option<usize>,
|
||||
) -> FieldResult<
|
||||
Connection<
|
||||
Self::CursorType,
|
||||
Self::NodeType,
|
||||
Self::ConnectionFieldsType,
|
||||
Self::EdgeFieldsType,
|
||||
>,
|
||||
> {
|
||||
after: Option<String>,
|
||||
before: Option<String>,
|
||||
first: Option<i32>,
|
||||
last: Option<i32>,
|
||||
) -> FieldResult<Connection<usize, i32, ConnectionFields, Diff>> {
|
||||
connection::query(
|
||||
after,
|
||||
before,
|
||||
first,
|
||||
last,
|
||||
|after, before, first, last| async move {
|
||||
let mut start = after.map(|after| after + 1).unwrap_or(0);
|
||||
let mut end = before.unwrap_or(10000);
|
||||
if let Some(first) = first {
|
||||
|
@ -175,19 +53,9 @@ pub async fn test_datasource_additional_fields() {
|
|||
)
|
||||
}));
|
||||
Ok(connection)
|
||||
}
|
||||
}
|
||||
|
||||
#[Object]
|
||||
impl QueryRoot {
|
||||
async fn numbers(
|
||||
&self,
|
||||
after: Option<String>,
|
||||
before: Option<String>,
|
||||
first: Option<i32>,
|
||||
last: Option<i32>,
|
||||
) -> FieldResult<Connection<usize, i32, ConnectionFields, Diff>> {
|
||||
Numbers.query(after, before, first, last).await
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user