use std::sync::Arc;
use serde_json_bytes::Value;
use serde_json_bytes::json;
use tokio::sync::mpsc;
use tower::BoxError;
use crate::Context;
use crate::error::Error;
use crate::error::FetchError;
use crate::graphql::Request as GraphQLRequest;
use crate::json_ext::Path;
use crate::plugins::subscription::SubscriptionConfig;
use crate::query_planner::fetch::FetchNode;
use crate::query_planner::fetch::Variables;
use crate::query_planner::subscription::SubscriptionHandle;
use crate::query_planner::subscription::SubscriptionNode;
const SUBGRAPH_NAME_EXTENSION_KEY: &str = "apollo.private.subgraph.name";
pub(crate) type BoxService = tower::util::BoxService<Request, Response, BoxError>;
#[allow(clippy::large_enum_variant)]
pub(crate) enum Request {
Fetch(FetchRequest),
Subscription(SubscriptionRequest),
}
pub(crate) type Response = (Value, Vec<Error>);
#[non_exhaustive]
pub(crate) struct FetchRequest {
pub(crate) context: Context,
pub(crate) fetch_node: FetchNode,
pub(crate) supergraph_request: Arc<http::Request<GraphQLRequest>>,
pub(crate) variables: Variables,
pub(crate) current_dir: Path,
}
#[buildstructor::buildstructor]
impl FetchRequest {
#[builder(visibility = "pub")]
fn new(
context: Context,
fetch_node: FetchNode,
supergraph_request: Arc<http::Request<GraphQLRequest>>,
variables: Variables,
current_dir: Path,
) -> Self {
Self {
context,
fetch_node,
supergraph_request,
variables,
current_dir,
}
}
}
pub(crate) struct SubscriptionRequest {
pub(crate) context: Context,
pub(crate) subscription_node: SubscriptionNode,
pub(crate) supergraph_request: Arc<http::Request<GraphQLRequest>>,
pub(crate) variables: Variables,
pub(crate) current_dir: Path,
pub(crate) sender: mpsc::Sender<crate::graphql::Response>,
pub(crate) subscription_handle: Option<SubscriptionHandle>,
pub(crate) subscription_config: Option<SubscriptionConfig>,
}
#[buildstructor::buildstructor]
impl SubscriptionRequest {
#[builder(visibility = "pub")]
fn new(
context: Context,
subscription_node: SubscriptionNode,
supergraph_request: Arc<http::Request<GraphQLRequest>>,
variables: Variables,
current_dir: Path,
sender: mpsc::Sender<crate::graphql::Response>,
subscription_handle: Option<SubscriptionHandle>,
subscription_config: Option<SubscriptionConfig>,
) -> Self {
Self {
context,
subscription_node,
supergraph_request,
variables,
current_dir,
sender,
subscription_handle,
subscription_config,
}
}
}
pub(crate) trait ErrorMapping<T> {
#[allow(clippy::result_large_err)]
fn map_to_graphql_error(self, service_name: String, current_dir: &Path) -> Result<T, Error>;
}
impl<T> ErrorMapping<T> for Result<T, BoxError> {
fn map_to_graphql_error(self, service_name: String, current_dir: &Path) -> Result<T, Error> {
self.map_err(|e| match e.downcast::<FetchError>() {
Ok(inner) => match *inner {
FetchError::SubrequestHttpError { .. } => *inner,
_ => FetchError::SubrequestHttpError {
status_code: None,
service: service_name,
reason: inner.to_string(),
},
},
Err(e) => FetchError::SubrequestHttpError {
status_code: None,
service: service_name,
reason: e.to_string(),
},
})
.map_err(|e| e.to_graphql_error(Some(current_dir.to_owned())))
}
}
pub(crate) trait AddSubgraphNameExt {
fn with_subgraph_name(self, subgraph_name: &str) -> Self;
fn add_subgraph_name(&mut self, subgraph_name: &str);
}
impl AddSubgraphNameExt for Error {
fn with_subgraph_name(mut self, subgraph_name: &str) -> Self {
self.add_subgraph_name(subgraph_name);
self
}
fn add_subgraph_name(&mut self, subgraph_name: &str) {
self.extensions
.insert(SUBGRAPH_NAME_EXTENSION_KEY, json!(subgraph_name));
}
}
impl<T> AddSubgraphNameExt for Result<T, Error> {
fn with_subgraph_name(self, subgraph_name: &str) -> Self {
self.map_err(|e| e.with_subgraph_name(subgraph_name))
}
fn add_subgraph_name(&mut self, subgraph_name: &str) {
if let Err(e) = self {
e.add_subgraph_name(subgraph_name);
}
}
}
pub(crate) trait SubgraphNameExt {
fn subgraph_name(&mut self) -> Option<String>;
}
impl SubgraphNameExt for Error {
fn subgraph_name(&mut self) -> Option<String> {
if let Some(subgraph_name) = self.extensions.remove(SUBGRAPH_NAME_EXTENSION_KEY) {
subgraph_name
.as_str()
.map(|subgraph_name| subgraph_name.to_string())
} else {
None
}
}
}