use apollo_compiler::ExecutableDocument;
use apollo_compiler::Node;
use apollo_compiler::executable::Field;
use apollo_compiler::executable::Operation;
use apollo_compiler::executable::Selection;
use apollo_compiler::schema::Value;
use serde_json::Number;
use serde_json_bytes::ByteString;
use serde_json_bytes::Map;
use serde_json_bytes::Value as JSONValue;
use tower::BoxError;
use super::ENTITIES;
use super::MakeRequestError;
pub(super) fn field_arguments_map(
field: &Node<Field>,
variables: &Map<ByteString, JSONValue>,
) -> Result<Map<ByteString, JSONValue>, BoxError> {
let mut arguments = Map::new();
for argument in field.arguments.iter() {
arguments.insert(
argument.name.as_str(),
argument_value_to_json(&argument.value, variables)?,
);
}
for argument_def in field.definition.arguments.iter() {
if let Some(value) = argument_def.default_value.as_ref()
&& !arguments.contains_key(argument_def.name.as_str())
{
arguments.insert(
argument_def.name.as_str(),
argument_value_to_json(value, variables).map_err(|err| {
format!(
"failed to convert default value on {}({}:) to json: {}",
field.definition.name, argument_def.name, err
)
})?,
);
}
}
Ok(arguments)
}
pub(super) fn argument_value_to_json(
value: &apollo_compiler::ast::Value,
variables: &Map<ByteString, JSONValue>,
) -> Result<JSONValue, BoxError> {
match value {
Value::Null => Ok(JSONValue::Null),
Value::Enum(e) => Ok(JSONValue::String(e.as_str().into())),
Value::Variable(name) => Ok(variables
.get(name.as_str())
.cloned()
.unwrap_or(JSONValue::Null)),
Value::String(s) => Ok(JSONValue::String(s.as_str().into())),
Value::Float(f) => Ok(JSONValue::Number(
Number::from_f64(
f.try_to_f64()
.map_err(|_| BoxError::from("Failed to parse float"))?,
)
.ok_or_else(|| BoxError::from("Failed to parse float"))?,
)),
Value::Int(i) => Ok(JSONValue::Number(Number::from(
i.try_to_i32().map_err(|_| "Failed to parse int")?,
))),
Value::Boolean(b) => Ok(JSONValue::Bool(*b)),
Value::List(l) => Ok(JSONValue::Array(
l.iter()
.map(|v| argument_value_to_json(v, variables))
.collect::<Result<Vec<_>, _>>()?,
)),
Value::Object(o) => Ok(JSONValue::Object(
o.iter()
.map(|(k, v)| argument_value_to_json(v, variables).map(|v| (k.as_str().into(), v)))
.collect::<Result<Map<_, _>, _>>()?,
)),
}
}
pub(super) fn get_entity_fields<'a>(
document: &'a ExecutableDocument,
op: &'a Node<Operation>,
) -> Result<(&'a Node<Field>, bool), MakeRequestError> {
use MakeRequestError::*;
let root_field = op
.selection_set
.selections
.iter()
.find_map(|s| match s {
Selection::Field(f) if f.name == ENTITIES => Some(f),
_ => None,
})
.ok_or_else(|| InvalidOperation("missing entities root field".into()))?;
let mut typename_requested = false;
for selection in root_field.selection_set.selections.iter() {
match selection {
Selection::Field(f) => {
if f.name == "__typename" {
typename_requested = true;
}
}
Selection::FragmentSpread(f) => {
let fragment = document
.fragments
.get(f.fragment_name.as_str())
.ok_or_else(|| InvalidOperation("missing fragment".into()))?;
for selection in fragment.selection_set.selections.iter() {
match selection {
Selection::Field(f) => {
if f.name == "__typename" {
typename_requested = true;
}
}
Selection::FragmentSpread(_) | Selection::InlineFragment(_) => {}
}
}
}
Selection::InlineFragment(f) => {
for selection in f.selection_set.selections.iter() {
match selection {
Selection::Field(f) => {
if f.name == "__typename" {
typename_requested = true;
}
}
Selection::FragmentSpread(_) | Selection::InlineFragment(_) => {}
}
}
}
}
}
Ok((root_field, typename_requested))
}