Remove connection::DataSource
This commit is contained in:
parent
1124124099
commit
c05127bb3e
|
@ -4,7 +4,6 @@ mod connection_type;
|
||||||
mod cursor;
|
mod cursor;
|
||||||
mod edge;
|
mod edge;
|
||||||
mod page_info;
|
mod page_info;
|
||||||
mod slice;
|
|
||||||
|
|
||||||
use crate::FieldResult;
|
use crate::FieldResult;
|
||||||
pub use connection_type::Connection;
|
pub use connection_type::Connection;
|
||||||
|
@ -18,158 +17,6 @@ use std::fmt::Display;
|
||||||
#[async_graphql_derive::SimpleObject(internal)]
|
#[async_graphql_derive::SimpleObject(internal)]
|
||||||
pub struct EmptyFields;
|
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.
|
/// 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.
|
/// 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 {
|
let first = match first {
|
||||||
Some(first) if first < 0 => {
|
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),
|
Some(first) => Some(first as usize),
|
||||||
None => None,
|
None => None,
|
||||||
|
@ -270,7 +117,7 @@ where
|
||||||
|
|
||||||
let last = match last {
|
let last = match last {
|
||||||
Some(last) if last < 0 => {
|
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),
|
Some(last) => Some(last as usize),
|
||||||
None => None,
|
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::connection::*;
|
||||||
use async_graphql::*;
|
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]
|
#[async_std::test]
|
||||||
pub async fn test_datasource_additional_fields() {
|
pub async fn test_datasource_additional_fields() {
|
||||||
struct QueryRoot;
|
struct QueryRoot;
|
||||||
|
|
||||||
struct Numbers;
|
|
||||||
|
|
||||||
#[SimpleObject]
|
#[SimpleObject]
|
||||||
struct ConnectionFields {
|
struct ConnectionFields {
|
||||||
total_count: i32,
|
total_count: i32,
|
||||||
|
@ -131,53 +15,6 @@ pub async fn test_datasource_additional_fields() {
|
||||||
diff: i32,
|
diff: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[DataSource]
|
|
||||||
impl DataSource for Numbers {
|
|
||||||
type CursorType = usize;
|
|
||||||
type NodeType = i32;
|
|
||||||
type ConnectionFieldsType = ConnectionFields;
|
|
||||||
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::with_additional_fields(
|
|
||||||
start > 0,
|
|
||||||
end < 10000,
|
|
||||||
ConnectionFields { total_count: 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]
|
#[Object]
|
||||||
impl QueryRoot {
|
impl QueryRoot {
|
||||||
async fn numbers(
|
async fn numbers(
|
||||||
|
@ -187,7 +24,38 @@ pub async fn test_datasource_additional_fields() {
|
||||||
first: Option<i32>,
|
first: Option<i32>,
|
||||||
last: Option<i32>,
|
last: Option<i32>,
|
||||||
) -> FieldResult<Connection<usize, i32, ConnectionFields, Diff>> {
|
) -> FieldResult<Connection<usize, i32, ConnectionFields, Diff>> {
|
||||||
Numbers.query(after, before, first, last).await
|
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 {
|
||||||
|
end = (start + first).min(end);
|
||||||
|
}
|
||||||
|
if let Some(last) = last {
|
||||||
|
start = if last > end - start { end } else { end - last };
|
||||||
|
}
|
||||||
|
let mut connection = Connection::with_additional_fields(
|
||||||
|
start > 0,
|
||||||
|
end < 10000,
|
||||||
|
ConnectionFields { total_count: 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)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user