async-graphql/src/context.rs

463 lines
14 KiB
Rust
Raw Normal View History

2020-03-26 03:34:28 +00:00
use crate::extensions::BoxExtension;
2020-03-03 11:15:18 +00:00
use crate::registry::Registry;
2020-03-19 09:20:12 +00:00
use crate::{ErrorWithPosition, InputValueType, QueryError, Result, Type};
2020-03-17 09:26:59 +00:00
use bytes::Bytes;
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-26 03:34:28 +00:00
use serde::ser::SerializeSeq;
use serde::{Serialize, Serializer};
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-26 03:34:28 +00:00
use std::sync::atomic::AtomicUsize;
2020-03-01 10:54:34 +00:00
2020-03-09 10:05:52 +00:00
/// Variables of query
2020-03-17 09:26:59 +00:00
#[derive(Debug, Clone)]
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-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 {
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
}
}
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> {
2020-03-04 03:51:42 +00:00
let gql_value = json_value_to_gql_value(value);
2020-03-14 03:46:20 +00:00
if let Value::Object(_) = gql_value {
Ok(Variables(gql_value))
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: &str,
content_type: Option<&str>,
2020-03-17 09:26:59 +00:00
content: Bytes,
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::String(file_string(filename, content_type, &content));
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::String(file_string(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
}
}
}
}
}
fn file_string(filename: &str, content_type: Option<&str>, content: &[u8]) -> String {
if let Some(content_type) = content_type {
format!("file:{}:{}|", filename, content_type)
+ unsafe { std::str::from_utf8_unchecked(content) }
} else {
format!("file:{}|", filename) + unsafe { std::str::from_utf8_unchecked(content) }
}
2020-03-04 03:51:42 +00:00
}
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),
2020-03-21 01:32:13 +00:00
serde_json::Value::Array(ls) => {
Value::List(ls.into_iter().map(json_value_to_gql_value).collect())
}
2020-03-04 03:51:42 +00:00
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-20 03:56:08 +00:00
/// Context for `SelectionSet`
2020-03-03 03:48:00 +00:00
pub type ContextSelectionSet<'a> = ContextBase<'a, &'a SelectionSet>;
2020-03-09 10:05:52 +00:00
2020-03-20 03:56:08 +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-26 03:34:28 +00:00
/// The query path segment
#[derive(Clone)]
pub enum QueryPathSegment {
/// Index
Index(usize),
/// Field name
Name(String),
}
impl Serialize for QueryPathSegment {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
match self {
QueryPathSegment::Index(idx) => serializer.serialize_i32(*idx as i32),
QueryPathSegment::Name(name) => serializer.serialize_str(name),
}
}
}
/// The query path, consists of multiple segments `QueryPathSegment`
#[derive(Default, Clone)]
pub struct QueryPath(im::Vector<QueryPathSegment>);
impl Serialize for QueryPath {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
for segment in &self.0 {
seq.serialize_element(segment)?;
}
seq.end()
}
}
impl std::fmt::Display for QueryPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
for (idx, segment) in self.0.iter().enumerate() {
if idx > 0 {
write!(f, ".")?;
}
match segment {
QueryPathSegment::Name(name) => write!(f, "{}", name)?,
QueryPathSegment::Index(idx) => write!(f, "{}", idx)?,
}
}
Ok(())
}
}
impl QueryPath {
fn append(&self, segment: QueryPathSegment) -> QueryPath {
let mut p = self.0.clone();
p.push_back(segment);
QueryPath(p)
}
pub(crate) fn field_name(&self) -> &str {
for segment in self.0.iter().rev() {
if let QueryPathSegment::Name(name) = segment {
return name.as_str();
}
}
unreachable!()
}
}
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> {
2020-03-26 03:34:28 +00:00
pub(crate) resolve_id: &'a AtomicUsize,
pub(crate) extensions: &'a [BoxExtension],
2020-03-01 10:54:34 +00:00
pub(crate) item: T,
2020-03-14 03:46:20 +00:00
pub(crate) variables: &'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-14 03:46:20 +00:00
pub(crate) fragments: &'a HashMap<String, FragmentDefinition>,
2020-03-26 03:34:28 +00:00
pub(crate) current_path: QueryPath,
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-26 03:34:28 +00:00
pub fn get_resolve_id(&self) -> usize {
self.resolve_id
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
}
#[doc(hidden)]
pub fn with_field(&self, field: &'a Field) -> ContextBase<'a, &'a Field> {
ContextBase {
extensions: self.extensions,
item: field,
resolve_id: self.resolve_id,
variables: self.variables,
variable_definitions: self.variable_definitions,
registry: self.registry,
data: self.data,
fragments: self.fragments,
current_path: self.current_path.append(QueryPathSegment::Name(
field.alias.clone().unwrap_or_else(|| field.name.clone()),
)),
}
}
#[doc(hidden)]
pub fn with_selection_set(
&self,
selection_set: &'a SelectionSet,
) -> ContextBase<'a, &'a SelectionSet> {
2020-03-03 03:48:00 +00:00
ContextBase {
2020-03-26 03:34:28 +00:00
extensions: self.extensions,
item: selection_set,
resolve_id: self.resolve_id,
2020-03-01 10:54:34 +00:00
variables: self.variables,
2020-03-04 03:51:42 +00:00
variable_definitions: self.variable_definitions,
2020-03-21 01:32:13 +00:00
registry: self.registry,
2020-03-05 00:39:56 +00:00
data: self.data,
2020-03-05 07:50:57 +00:00
fragments: self.fragments,
2020-03-26 03:34:28 +00:00
current_path: self.current_path.clone(),
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-10 06:14:09 +00:00
fn var_value(&self, name: &str) -> Result<Value> {
let def = self
.variable_definitions
.and_then(|defs| defs.iter().find(|def| def.name == name));
if let Some(def) = def {
2020-03-14 03:46:20 +00:00
if let Some(var_value) = self.variables.get(&def.name) {
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());
}
}
2020-03-21 01:32:13 +00:00
Err(QueryError::VarNotDefined {
2020-03-10 06:14:09 +00:00
var_name: name.to_string(),
}
2020-03-21 01:32:13 +00:00
.into())
2020-03-10 06:14:09 +00:00
}
fn resolve_input_value(&self, mut value: Value) -> Result<Value> {
match value {
Value::Variable(var_name) => self.var_value(&var_name),
Value::List(ref mut ls) => {
for value in ls {
if let Value::Variable(var_name) = value {
*value = self.var_value(&var_name)?;
}
2020-03-04 03:51:42 +00:00
}
2020-03-10 06:14:09 +00:00
Ok(value)
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() {
2020-03-10 06:14:09 +00:00
if let Value::Variable(var_name) = value {
*value = self.var_value(&var_name)?;
}
}
Ok(value)
2020-03-04 03:51:42 +00:00
}
2020-03-10 06:14:09 +00:00
_ => Ok(value),
2020-03-04 03:51:42 +00:00
}
}
#[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())?;
2020-03-19 09:20:12 +00:00
let res: bool = InputValueType::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())?;
2020-03-19 09:20:12 +00:00
let res: bool = InputValueType::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
2020-03-26 03:34:28 +00:00
impl<'a> ContextBase<'a, &'a SelectionSet> {
#[doc(hidden)]
pub fn with_index(&self, idx: usize) -> ContextBase<'a, &'a SelectionSet> {
ContextBase {
extensions: self.extensions,
item: self.item,
resolve_id: self.resolve_id,
variables: self.variables,
variable_definitions: self.variable_definitions,
registry: self.registry,
data: self.data,
fragments: self.fragments,
current_path: self.current_path.append(QueryPathSegment::Index(idx)),
}
}
}
2020-03-05 09:06:14 +00:00
impl<'a> ContextBase<'a, &'a Field> {
#[doc(hidden)]
2020-03-19 09:20:12 +00:00
pub fn param_value<T: InputValueType, F: FnOnce() -> Value>(
2020-03-05 09:06:14 +00:00
&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)?;
2020-03-19 09:20:12 +00:00
let res = InputValueType::parse(&value).ok_or_else(|| {
2020-03-05 09:06:14 +00:00
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();
2020-03-19 09:20:12 +00:00
let res = InputValueType::parse(&value).ok_or_else(|| {
2020-03-05 09:06:14 +00:00
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-26 03:34:28 +00:00
pub fn result_name(&self) -> &str {
if let Some(QueryPathSegment::Name(segment)) = self.current_path.0.last() {
&segment
} else {
unreachable!()
}
2020-03-06 15:58:43 +00:00
}
2020-03-05 09:06:14 +00:00
}