async-graphql/src/context.rs

249 lines
8.0 KiB
Rust
Raw Normal View History

2020-03-03 11:15:18 +00:00
use crate::registry::Registry;
use crate::{ErrorWithPosition, GQLInputValue, GQLType, QueryError, Result};
2020-03-01 10:54:34 +00:00
use fnv::FnvHasher;
2020-03-05 07:50:57 +00:00
use graphql_parser::query::{
2020-03-06 16:19:22 +00:00
Directive, Field, FragmentDefinition, SelectionSet, Value, VariableDefinition,
2020-03-05 07:50:57 +00:00
};
2020-03-01 10:54:34 +00:00
use std::any::{Any, TypeId};
2020-03-04 03:51:42 +00:00
use std::collections::{BTreeMap, HashMap};
2020-03-01 10:54:34 +00:00
use std::hash::BuildHasherDefault;
use std::ops::{Deref, DerefMut};
2020-03-09 10:05:52 +00:00
/// Variables of query
2020-03-01 16:52:05 +00:00
#[derive(Default)]
2020-03-04 03:51:42 +00:00
pub struct Variables(BTreeMap<String, Value>);
2020-03-01 10:54:34 +00:00
impl Deref for Variables {
2020-03-04 03:51:42 +00:00
type Target = BTreeMap<String, Value>;
2020-03-01 10:54:34 +00:00
fn deref(&self) -> &Self::Target {
&self.0
}
}
2020-03-05 00:39:56 +00:00
impl DerefMut for Variables {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
2020-03-04 03:51:42 +00:00
impl Variables {
2020-03-05 00:39:56 +00:00
pub(crate) fn parse_from_json(value: serde_json::Value) -> Result<Self> {
2020-03-04 03:51:42 +00:00
let gql_value = json_value_to_gql_value(value);
if let Value::Object(obj) = gql_value {
Ok(Variables(obj))
} else {
Ok(Default::default())
}
}
}
fn json_value_to_gql_value(value: serde_json::Value) -> Value {
match value {
serde_json::Value::Null => Value::Null,
serde_json::Value::Bool(n) => Value::Boolean(n),
serde_json::Value::Number(n) if n.is_f64() => Value::Float(n.as_f64().unwrap()),
serde_json::Value::Number(n) => Value::Int((n.as_i64().unwrap() as i32).into()),
serde_json::Value::String(s) => Value::String(s),
serde_json::Value::Array(ls) => Value::List(
ls.into_iter()
.map(|value| json_value_to_gql_value(value))
.collect(),
),
serde_json::Value::Object(obj) => Value::Object(
obj.into_iter()
.map(|(name, value)| (name, json_value_to_gql_value(value)))
.collect(),
),
}
}
2020-03-01 10:54:34 +00:00
#[derive(Default)]
pub struct Data(HashMap<TypeId, Box<dyn Any + Sync + Send>, BuildHasherDefault<FnvHasher>>);
impl Data {
pub fn insert<D: Any + Send + Sync>(&mut self, data: D) {
self.0.insert(TypeId::of::<D>(), Box::new(data));
}
}
2020-03-03 03:48:00 +00:00
pub type ContextSelectionSet<'a> = ContextBase<'a, &'a SelectionSet>;
2020-03-09 10:05:52 +00:00
/// Context object for resolve field.
2020-03-03 03:48:00 +00:00
pub type Context<'a> = ContextBase<'a, &'a Field>;
2020-03-01 10:54:34 +00:00
2020-03-03 03:48:00 +00:00
pub struct ContextBase<'a, T> {
2020-03-01 10:54:34 +00:00
pub(crate) item: T,
pub(crate) variables: Option<&'a Variables>,
2020-03-04 03:51:42 +00:00
pub(crate) variable_definitions: Option<&'a [VariableDefinition]>,
2020-03-03 11:15:18 +00:00
pub(crate) registry: &'a Registry,
2020-03-05 00:39:56 +00:00
pub(crate) data: &'a Data,
2020-03-05 07:50:57 +00:00
pub(crate) fragments: &'a HashMap<String, &'a FragmentDefinition>,
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
}
}
2020-03-03 03:48:00 +00:00
impl<'a, T> ContextBase<'a, T> {
2020-03-01 17:15:05 +00:00
#[doc(hidden)]
2020-03-03 03:48:00 +00:00
pub fn with_item<R>(&self, item: R) -> ContextBase<'a, R> {
ContextBase {
2020-03-01 10:54:34 +00:00
item,
variables: self.variables,
2020-03-04 03:51:42 +00:00
variable_definitions: self.variable_definitions,
2020-03-03 11:15:18 +00:00
registry: self.registry.clone(),
2020-03-05 00:39:56 +00:00
data: self.data,
2020-03-05 07:50:57 +00:00
fragments: self.fragments,
2020-03-01 10:54:34 +00:00
}
}
2020-03-09 10:05:52 +00:00
/// Gets the global data defined in the `Schema`.
pub fn data<D: Any + Send + Sync>(&self) -> &D {
2020-03-05 00:39:56 +00:00
self.data
.0
.get(&TypeId::of::<D>())
.and_then(|d| d.downcast_ref::<D>())
.expect("The specified data type does not exist.")
2020-03-01 10:54:34 +00:00
}
2020-03-05 07:50:57 +00:00
2020-03-04 03:51:42 +00:00
fn resolve_input_value(&self, value: Value) -> Result<Value> {
if let Value::Variable(var_name) = value {
let def = self
.variable_definitions
.and_then(|defs| defs.iter().find(|def| def.name == var_name.as_str()));
if let Some(def) = def {
if let Some(var_value) = self.variables.map(|vars| vars.get(&def.name)).flatten() {
return Ok(var_value.clone());
} else if let Some(default) = &def.default_value {
return Ok(default.clone());
2020-03-04 03:51:42 +00:00
}
}
return Err(QueryError::VarNotDefined {
var_name: var_name.clone(),
}
.into());
} else {
Ok(value)
}
}
#[doc(hidden)]
2020-03-05 09:06:14 +00:00
pub fn is_skip(&self, directives: &[Directive]) -> Result<bool> {
for directive in directives {
if directive.name == "skip" {
if let Some(value) = directive
.arguments
.iter()
.find(|(name, _)| name == "if")
.map(|(_, value)| value)
{
let value = self.resolve_input_value(value.clone())?;
let res: bool = GQLInputValue::parse(&value).ok_or_else(|| {
QueryError::ExpectedType {
expect: bool::qualified_type_name(),
actual: value,
}
.with_position(directive.position)
})?;
if res {
return Ok(true);
}
} else {
return Err(QueryError::RequiredDirectiveArgs {
directive: "@skip",
arg_name: "if",
arg_type: "Boolean!",
}
.with_position(directive.position)
.into());
}
} else if directive.name == "include" {
if let Some(value) = directive
.arguments
.iter()
.find(|(name, _)| name == "if")
.map(|(_, value)| value)
{
let value = self.resolve_input_value(value.clone())?;
let res: bool = GQLInputValue::parse(&value).ok_or_else(|| {
QueryError::ExpectedType {
expect: bool::qualified_type_name(),
actual: value,
}
.with_position(directive.position)
})?;
if !res {
return Ok(true);
}
} else {
return Err(QueryError::RequiredDirectiveArgs {
directive: "@include",
arg_name: "if",
arg_type: "Boolean!",
}
.with_position(directive.position)
.into());
}
} else {
return Err(QueryError::UnknownDirective {
name: directive.name.clone(),
}
.with_position(directive.position)
.into());
}
}
Ok(false)
}
2020-03-01 10:54:34 +00:00
}
2020-03-05 09:06:14 +00:00
impl<'a> ContextBase<'a, &'a Field> {
#[doc(hidden)]
pub fn param_value<T: GQLInputValue, F: FnOnce() -> Value>(
&self,
name: &str,
2020-03-05 13:34:31 +00:00
default: F,
2020-03-05 09:06:14 +00:00
) -> Result<T> {
match self
.arguments
.iter()
.find(|(n, _)| n == name)
.map(|(_, v)| v)
.cloned()
{
Some(value) => {
let value = self.resolve_input_value(value)?;
let res = GQLInputValue::parse(&value).ok_or_else(|| {
QueryError::ExpectedType {
expect: T::qualified_type_name(),
actual: value,
}
.with_position(self.item.position)
})?;
Ok(res)
}
2020-03-05 13:34:31 +00:00
None => {
2020-03-05 09:06:14 +00:00
let value = default();
let res = GQLInputValue::parse(&value).ok_or_else(|| {
QueryError::ExpectedType {
expect: T::qualified_type_name(),
actual: value.clone(),
}
.with_position(self.item.position)
})?;
Ok(res)
}
}
}
2020-03-06 15:58:43 +00:00
2020-03-07 03:27:44 +00:00
#[doc(hidden)]
2020-03-06 15:58:43 +00:00
pub fn result_name(&self) -> String {
self.item.alias.clone().unwrap_or_else(|| self.name.clone())
}
2020-03-05 09:06:14 +00:00
}