async-graphql/src/context.rs

639 lines
18 KiB
Rust
Raw Normal View History

2020-05-22 03:58:49 +00:00
use crate::extensions::Extensions;
use crate::parser::query::{Directive, Field, SelectionSet};
use crate::schema::SchemaEnv;
use crate::{
2020-07-31 02:10:03 +00:00
FieldResult, InputValueType, Lookahead, Pos, Positioned, QueryError, Result, Type, Value,
};
use async_graphql_parser::query::Document;
use async_graphql_parser::UploadValue;
2020-04-24 02:05:41 +00:00
use fnv::FnvHashMap;
2020-07-15 10:05:24 +00:00
use serde::ser::SerializeSeq;
use serde::Serializer;
2020-03-01 10:54:34 +00:00
use std::any::{Any, TypeId};
use std::collections::BTreeMap;
use std::fmt::{Display, Formatter};
use std::fs::File;
2020-03-01 10:54:34 +00:00
use std::ops::{Deref, DerefMut};
2020-03-26 03:34:28 +00:00
use std::sync::atomic::AtomicUsize;
use std::sync::Arc;
2020-03-01 10:54:34 +00:00
2020-03-09 10:05:52 +00:00
/// Variables of query
2020-07-15 10:05:24 +00:00
#[derive(Debug, Clone, Serialize)]
2020-03-14 03:46:20 +00:00
pub struct Variables(Value);
impl Default for Variables {
fn default() -> Self {
Self(Value::Object(Default::default()))
}
}
2020-03-01 10:54:34 +00:00
impl Deref for Variables {
2020-05-16 13:14:26 +00:00
type Target = BTreeMap<String, Value>;
2020-03-01 10:54:34 +00:00
fn deref(&self) -> &Self::Target {
2020-03-14 03:46:20 +00:00
if let Value::Object(obj) = &self.0 {
obj
} else {
unreachable!()
}
2020-03-01 10:54:34 +00:00
}
}
impl Display for Variables {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
2020-03-05 00:39:56 +00:00
impl DerefMut for Variables {
fn deref_mut(&mut self) -> &mut Self::Target {
2020-03-14 03:46:20 +00:00
if let Value::Object(obj) = &mut self.0 {
obj
} else {
unreachable!()
}
2020-03-05 00:39:56 +00:00
}
}
2020-03-04 03:51:42 +00:00
impl Variables {
2020-03-20 03:56:08 +00:00
/// Parse variables from JSON object.
2020-03-17 09:26:59 +00:00
pub fn parse_from_json(value: serde_json::Value) -> Result<Self> {
if let Value::Object(obj) = value.into() {
Ok(Variables(Value::Object(obj)))
2020-03-04 03:51:42 +00:00
} else {
Ok(Default::default())
}
}
2020-03-14 03:46:20 +00:00
pub(crate) fn set_upload(
&mut self,
var_path: &str,
filename: String,
content_type: Option<String>,
content: File,
2020-03-14 03:46:20 +00:00
) {
2020-03-21 01:32:13 +00:00
let mut it = var_path.split('.').peekable();
2020-03-14 03:46:20 +00:00
if let Some(first) = it.next() {
if first != "variables" {
return;
}
}
let mut current = &mut self.0;
while let Some(s) = it.next() {
let has_next = it.peek().is_some();
if let Ok(idx) = s.parse::<i32>() {
if let Value::List(ls) = current {
if let Some(value) = ls.get_mut(idx as usize) {
if !has_next {
*value = Value::Upload(UploadValue {
filename,
content_type,
content,
});
2020-03-14 03:46:20 +00:00
return;
} else {
current = value;
}
} else {
return;
}
}
2020-03-21 01:32:13 +00:00
} else if let Value::Object(obj) = current {
if let Some(value) = obj.get_mut(s) {
if !has_next {
*value = Value::Upload(UploadValue {
filename,
content_type,
content,
});
2020-03-14 03:46:20 +00:00
return;
2020-03-21 01:32:13 +00:00
} else {
current = value;
2020-03-14 03:46:20 +00:00
}
2020-03-21 01:32:13 +00:00
} else {
return;
2020-03-14 03:46:20 +00:00
}
}
}
}
}
2020-03-01 10:54:34 +00:00
#[derive(Default)]
/// Schema/Context data
2020-04-23 13:36:04 +00:00
pub struct Data(FnvHashMap<TypeId, Box<dyn Any + Sync + Send>>);
2020-03-01 10:54:34 +00:00
impl Data {
#[allow(missing_docs)]
2020-03-01 10:54:34 +00:00
pub fn insert<D: Any + Send + Sync>(&mut self, data: D) {
self.0.insert(TypeId::of::<D>(), Box::new(data));
}
}
2020-03-20 03:56:08 +00:00
/// Context for `SelectionSet`
2020-05-10 02:59:51 +00:00
pub type ContextSelectionSet<'a> = ContextBase<'a, &'a Positioned<SelectionSet>>;
2020-03-09 10:05:52 +00:00
2020-03-20 03:56:08 +00:00
/// Context object for resolve field
2020-05-10 02:59:51 +00:00
pub type Context<'a> = ContextBase<'a, &'a Positioned<Field>>;
2020-03-01 10:54:34 +00:00
2020-03-26 03:34:28 +00:00
/// The query path segment
#[derive(Clone)]
2020-03-26 10:30:29 +00:00
pub enum QueryPathSegment<'a> {
2020-03-26 03:34:28 +00:00
/// Index
Index(usize),
/// Field name
2020-03-26 10:30:29 +00:00
Name(&'a str),
2020-03-26 03:34:28 +00:00
}
2020-03-26 10:30:29 +00:00
/// The query path node
#[derive(Clone)]
pub struct QueryPathNode<'a> {
/// Parent node
pub parent: Option<&'a QueryPathNode<'a>>,
2020-03-26 03:34:28 +00:00
2020-03-26 10:30:29 +00:00
/// Current path segment
pub segment: QueryPathSegment<'a>,
2020-03-26 03:34:28 +00:00
}
2020-07-15 10:05:24 +00:00
impl<'a> serde::Serialize for QueryPathNode<'a> {
fn serialize<S>(
&self,
serializer: S,
) -> std::result::Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
self.for_each(|segment| match segment {
QueryPathSegment::Index(idx) => {
seq.serialize_element(&idx).ok();
}
QueryPathSegment::Name(name) => {
seq.serialize_element(name).ok();
}
});
seq.end()
}
}
impl<'a> std::fmt::Display for QueryPathNode<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut first = true;
self.for_each(|segment| {
if !first {
write!(f, ".").ok();
}
match segment {
QueryPathSegment::Index(idx) => {
write!(f, "{}", *idx).ok();
}
QueryPathSegment::Name(name) => {
write!(f, "{}", name).ok();
}
}
first = false;
});
Ok(())
}
}
2020-03-26 10:30:29 +00:00
impl<'a> QueryPathNode<'a> {
pub(crate) fn field_name(&self) -> &str {
let mut p = self;
loop {
if let QueryPathSegment::Name(name) = &p.segment {
return name;
2020-03-26 03:34:28 +00:00
}
2020-03-26 10:30:29 +00:00
p = p.parent.unwrap();
2020-03-26 03:34:28 +00:00
}
}
2020-03-26 10:30:29 +00:00
pub(crate) fn for_each<F: FnMut(&QueryPathSegment<'a>)>(&self, mut f: F) {
self.for_each_ref(&mut f);
2020-03-26 03:34:28 +00:00
}
2020-03-26 10:30:29 +00:00
fn for_each_ref<F: FnMut(&QueryPathSegment<'a>)>(&self, f: &mut F) {
if let Some(parent) = &self.parent {
parent.for_each_ref(f);
2020-03-26 03:34:28 +00:00
}
2020-03-26 10:30:29 +00:00
f(&self.segment);
2020-03-26 03:34:28 +00:00
}
}
2020-04-28 07:01:19 +00:00
/// Represents the unique id of the resolve
#[derive(Copy, Clone, Debug)]
pub struct ResolveId {
/// Parent id
pub parent: Option<usize>,
/// Current id
pub current: usize,
}
impl ResolveId {
pub(crate) fn root() -> ResolveId {
ResolveId {
parent: None,
current: 0,
}
}
}
impl std::fmt::Display for ResolveId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(parent) = self.parent {
write!(f, "{}:{}", parent, self.current)
} else {
write!(f, "{}", self.current)
}
}
}
2020-03-20 03:56:08 +00:00
/// Query context
2020-03-25 03:39:28 +00:00
#[derive(Clone)]
2020-03-03 03:48:00 +00:00
pub struct ContextBase<'a, T> {
#[allow(missing_docs)]
pub path_node: Option<QueryPathNode<'a>>,
2020-04-28 07:01:19 +00:00
pub(crate) resolve_id: ResolveId,
pub(crate) inc_resolve_id: &'a AtomicUsize,
2020-05-20 00:18:28 +00:00
#[doc(hidden)]
pub item: T,
pub(crate) schema_env: &'a SchemaEnv,
pub(crate) query_env: &'a QueryEnv,
2020-03-01 10:54:34 +00:00
}
2020-03-03 03:48:00 +00:00
impl<'a, T> Deref for ContextBase<'a, T> {
2020-03-01 10:54:34 +00:00
type Target = T;
fn deref(&self) -> &Self::Target {
&self.item
}
}
#[doc(hidden)]
pub struct QueryEnvInner {
pub extensions: spin::Mutex<Extensions>,
pub variables: Variables,
pub document: Document,
pub ctx_data: Arc<Data>,
}
#[doc(hidden)]
#[derive(Clone)]
pub struct QueryEnv(Arc<QueryEnvInner>);
impl Deref for QueryEnv {
type Target = QueryEnvInner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl QueryEnv {
#[doc(hidden)]
2020-05-22 03:58:49 +00:00
pub fn new(
extensions: spin::Mutex<Extensions>,
2020-05-22 03:58:49 +00:00
variables: Variables,
document: Document,
ctx_data: Arc<Data>,
) -> QueryEnv {
QueryEnv(Arc::new(QueryEnvInner {
2020-05-22 03:58:49 +00:00
extensions,
variables,
document,
ctx_data,
}))
}
#[doc(hidden)]
pub fn create_context<'a, T>(
&'a self,
schema_env: &'a SchemaEnv,
path_node: Option<QueryPathNode<'a>>,
item: T,
2020-04-28 07:01:19 +00:00
inc_resolve_id: &'a AtomicUsize,
) -> ContextBase<'a, T> {
ContextBase {
path_node,
2020-04-28 07:01:19 +00:00
resolve_id: ResolveId::root(),
inc_resolve_id,
item,
schema_env,
query_env: self,
}
}
}
2020-03-03 03:48:00 +00:00
impl<'a, T> ContextBase<'a, T> {
2020-04-28 07:01:19 +00:00
fn get_child_resolve_id(&self) -> ResolveId {
let id = self
.inc_resolve_id
2020-03-26 03:34:28 +00:00
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
2020-04-28 07:01:19 +00:00
+ 1;
ResolveId {
parent: Some(self.resolve_id.current),
current: id,
}
2020-03-26 03:34:28 +00:00
}
#[doc(hidden)]
2020-05-10 02:59:51 +00:00
pub fn with_field(
&'a self,
field: &'a Positioned<Field>,
) -> ContextBase<'a, &'a Positioned<Field>> {
2020-03-26 03:34:28 +00:00
ContextBase {
2020-03-26 10:30:29 +00:00
path_node: Some(QueryPathNode {
parent: self.path_node.as_ref(),
segment: QueryPathSegment::Name(
field
.alias
.as_ref()
2020-05-16 13:14:26 +00:00
.map(|alias| alias.as_str())
.unwrap_or_else(|| field.name.as_str()),
2020-03-26 10:30:29 +00:00
),
}),
2020-03-26 03:34:28 +00:00
item: field,
2020-04-28 07:01:19 +00:00
resolve_id: self.get_child_resolve_id(),
inc_resolve_id: self.inc_resolve_id,
schema_env: self.schema_env,
query_env: self.query_env,
2020-03-26 03:34:28 +00:00
}
}
#[doc(hidden)]
pub fn with_selection_set(
&self,
2020-05-10 02:59:51 +00:00
selection_set: &'a Positioned<SelectionSet>,
) -> ContextBase<'a, &'a Positioned<SelectionSet>> {
2020-03-03 03:48:00 +00:00
ContextBase {
2020-03-26 10:30:29 +00:00
path_node: self.path_node.clone(),
2020-03-26 03:34:28 +00:00
item: selection_set,
resolve_id: self.resolve_id,
2020-04-28 07:01:19 +00:00
inc_resolve_id: &self.inc_resolve_id,
schema_env: self.schema_env,
query_env: self.query_env,
2020-03-01 10:54:34 +00:00
}
}
2020-03-31 03:19:18 +00:00
/// Gets the global data defined in the `Context` or `Schema`.
2020-05-22 06:02:28 +00:00
///
/// If both `Schema` and `Query` have the same data type, the data in the `Query` is obtained.
///
/// Returns a FieldError if the specified type data does not exist.
pub fn data<D: Any + Send + Sync>(&self) -> FieldResult<&D> {
self.data_opt::<D>()
.ok_or_else(|| format!("Data `{}` does not exist.", std::any::type_name::<D>()).into())
}
/// Gets the global data defined in the `Context` or `Schema`.
///
2020-05-22 06:02:28 +00:00
/// # Panics
///
/// It will panic if the specified data type does not exist.
pub fn data_unchecked<D: Any + Send + Sync>(&self) -> &D {
self.data_opt::<D>()
.unwrap_or_else(|| panic!("Data `{}` does not exist.", std::any::type_name::<D>()))
}
/// Gets the global data defined in the `Context` or `Schema`, returns `None` if the specified type data does not exist.
pub fn data_opt<D: Any + Send + Sync>(&self) -> Option<&D> {
self.query_env
.ctx_data
.0
.get(&TypeId::of::<D>())
.or_else(|| self.schema_env.data.0.get(&TypeId::of::<D>()))
2020-03-05 00:39:56 +00:00
.and_then(|d| d.downcast_ref::<D>())
2020-03-01 10:54:34 +00:00
}
2020-03-05 07:50:57 +00:00
fn var_value(&self, name: &str, pos: Pos) -> Result<Value> {
2020-03-10 06:14:09 +00:00
let def = self
.query_env
.document
.current_operation()
2020-03-10 06:14:09 +00:00
.variable_definitions
2020-04-01 08:53:49 +00:00
.iter()
.find(|def| def.name.node == name);
2020-03-10 06:14:09 +00:00
if let Some(def) = def {
if let Some(var_value) = self.query_env.variables.get(def.name.as_str()) {
2020-03-10 06:14:09 +00:00
return Ok(var_value.clone());
} else if let Some(default) = &def.default_value {
return Ok(default.clone_inner());
2020-03-10 06:14:09 +00:00
}
match def.var_type.deref() {
&async_graphql_parser::query::Type::Named(_)
| &async_graphql_parser::query::Type::List(_) => {
// Nullable types can default to null when not given.
return Ok(Value::Null);
}
&async_graphql_parser::query::Type::NonNull(_) => {
// Strict types can not.
}
}
2020-03-10 06:14:09 +00:00
}
2020-03-21 01:32:13 +00:00
Err(QueryError::VarNotDefined {
2020-03-10 06:14:09 +00:00
var_name: name.to_string(),
}
.into_error(pos))
2020-03-10 06:14:09 +00:00
}
fn resolve_input_value(&self, value: &mut Value, pos: Pos) -> Result<()> {
2020-03-10 06:14:09 +00:00
match value {
Value::Variable(var_name) => {
*value = self.var_value(&var_name, pos)?;
Ok(())
}
2020-03-10 06:14:09 +00:00
Value::List(ref mut ls) => {
for value in ls {
self.resolve_input_value(value, pos)?;
2020-03-04 03:51:42 +00:00
}
Ok(())
2020-03-04 03:51:42 +00:00
}
2020-03-10 06:14:09 +00:00
Value::Object(ref mut obj) => {
2020-03-21 01:32:13 +00:00
for value in obj.values_mut() {
self.resolve_input_value(value, pos)?;
2020-03-10 06:14:09 +00:00
}
Ok(())
2020-03-04 03:51:42 +00:00
}
_ => Ok(()),
2020-03-04 03:51:42 +00:00
}
}
2020-08-06 06:52:54 +00:00
#[doc(hidden)]
pub fn is_ifdef(&self, directives: &[Positioned<Directive>]) -> bool {
for directive in directives {
if directive.name.node == "ifdef" {
return true;
}
}
false
}
#[doc(hidden)]
2020-05-10 02:59:51 +00:00
pub fn is_skip(&self, directives: &[Positioned<Directive>]) -> Result<bool> {
2020-03-05 09:06:14 +00:00
for directive in directives {
if directive.name.node == "skip" {
if let Some(value) = directive.get_argument("if") {
let mut inner_value = value.clone_inner();
self.resolve_input_value(&mut inner_value, value.pos)?;
match InputValueType::parse(Some(inner_value)) {
Ok(true) => return Ok(true),
Ok(false) => {}
Err(err) => {
return Err(err.into_error(value.pos, bool::qualified_type_name()))
}
}
} else {
return Err(QueryError::RequiredDirectiveArgs {
directive: "@skip",
arg_name: "if",
arg_type: "Boolean!",
}
.into_error(directive.position()));
}
} else if directive.name.node == "include" {
if let Some(value) = directive.get_argument("if") {
let mut inner_value = value.clone_inner();
self.resolve_input_value(&mut inner_value, value.pos)?;
match InputValueType::parse(Some(inner_value)) {
Ok(false) => return Ok(true),
Ok(true) => {}
Err(err) => {
return Err(err.into_error(value.pos, bool::qualified_type_name()))
}
}
} else {
return Err(QueryError::RequiredDirectiveArgs {
directive: "@include",
arg_name: "if",
arg_type: "Boolean!",
}
.into_error(directive.position()));
}
}
}
Ok(false)
}
#[doc(hidden)]
pub fn is_defer(&self, directives: &[Positioned<Directive>]) -> bool {
directives.iter().any(|d| d.name.node == "defer")
}
2020-05-21 02:12:54 +00:00
#[doc(hidden)]
pub fn is_stream(&self, directives: &[Positioned<Directive>]) -> bool {
directives.iter().any(|d| d.name.node == "stream")
}
2020-03-01 10:54:34 +00:00
}
2020-03-05 09:06:14 +00:00
2020-05-10 02:59:51 +00:00
impl<'a> ContextBase<'a, &'a Positioned<SelectionSet>> {
2020-03-26 03:34:28 +00:00
#[doc(hidden)]
2020-05-10 02:59:51 +00:00
pub fn with_index(&'a self, idx: usize) -> ContextBase<'a, &'a Positioned<SelectionSet>> {
2020-03-26 03:34:28 +00:00
ContextBase {
2020-03-26 10:30:29 +00:00
path_node: Some(QueryPathNode {
parent: self.path_node.as_ref(),
segment: QueryPathSegment::Index(idx),
}),
2020-03-26 03:34:28 +00:00
item: self.item,
2020-04-28 07:01:19 +00:00
resolve_id: self.get_child_resolve_id(),
inc_resolve_id: self.inc_resolve_id,
schema_env: self.schema_env,
query_env: self.query_env,
2020-03-26 03:34:28 +00:00
}
}
}
2020-05-10 02:59:51 +00:00
impl<'a> ContextBase<'a, &'a Positioned<Field>> {
2020-03-05 09:06:14 +00:00
#[doc(hidden)]
pub fn param_value<T: InputValueType>(
2020-03-05 09:06:14 +00:00
&self,
name: &str,
default: Option<fn() -> T>,
2020-03-05 09:06:14 +00:00
) -> Result<T> {
let value = self.get_argument(name).cloned();
if let Some(default) = default {
if value.is_none() {
return Ok(default());
2020-03-05 09:06:14 +00:00
}
}
let pos = value
.as_ref()
.map(|value| value.position())
.unwrap_or_default();
let value = match value {
Some(value) => {
let mut new_value = value.into_inner();
self.resolve_input_value(&mut new_value, pos)?;
Some(new_value)
}
None => None,
};
match InputValueType::parse(value) {
Ok(res) => Ok(res),
Err(err) => Err(err.into_error(pos, T::qualified_type_name())),
2020-03-05 09:06:14 +00:00
}
}
2020-03-06 15:58:43 +00:00
2020-03-07 03:27:44 +00:00
#[doc(hidden)]
2020-03-26 03:34:28 +00:00
pub fn result_name(&self) -> &str {
2020-03-26 10:30:29 +00:00
self.item
.alias
.as_ref()
2020-05-16 13:14:26 +00:00
.map(|alias| alias.as_str())
.unwrap_or_else(|| self.item.name.as_str())
2020-03-06 15:58:43 +00:00
}
2020-05-14 09:35:25 +00:00
/// Get the position of the current field in the query code.
pub fn position(&self) -> Pos {
self.pos
}
2020-05-14 14:13:28 +00:00
/// Creates a uniform interface to inspect the forthcoming selections.
///
/// # Examples
///
/// ```no_run
/// use async_graphql::*;
///
/// #[SimpleObject]
/// struct Detail {
/// c: i32,
/// d: i32,
/// }
///
/// #[SimpleObject]
/// struct MyObj {
/// a: i32,
/// b: i32,
/// detail: Detail,
/// }
///
/// struct Query;
///
/// #[Object]
/// impl Query {
/// async fn obj(&self, ctx: &Context<'_>) -> MyObj {
/// if ctx.look_ahead().field("a").exists() {
/// // This is a query like `obj { a }`
/// } else if ctx.look_ahead().field("detail").field("c").exists() {
/// // This is a query like `obj { detail { c } }`
/// } else {
/// // This query doesn't have `a`
/// }
/// unimplemented!()
/// }
/// }
/// ```
pub fn look_ahead(&self) -> Lookahead {
Lookahead {
document: &self.query_env.document,
2020-05-14 14:13:28 +00:00
field: Some(&self.item.node),
}
}
2020-03-05 09:06:14 +00:00
}