use std::ops::Deref;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use crate::config::{
demand_control::DemandControlConfig, error_masking::ErrorMaskingConfig, headers::HeadersConfig,
override_labels::OverrideLabelsConfig, override_subgraph_urls::OverrideSubgraphUrlsConfig,
persisted_documents::PersistedDocumentsConfig, subscriptions::SupergraphSubscriptionsConfig,
traffic_shaping::SupergraphTrafficShapingConfig,
};
use crate::query_planner::planner::{Planner, PlannerError, QueryPlannerOptions};
use crate::query_planner::utils::parsing::safe_parse_schema;
use graphql_tools::static_graphql::schema::Document;
use tokio_util::sync::CancellationToken;
use crate::executor::{
introspection::schema::{SchemaMetadata, SchemaWithMetadata},
plugin_trait::{EndHookPayload, FromGraphQLErrorToResponse, StartHookPayload},
response::graphql_error::GraphQLError,
};
pub struct PublicSchema {
pub document: Arc<Document>,
pub sdl: Arc<str>,
}
#[derive(Debug, thiserror::Error)]
pub enum SupergraphBuildError {
#[error("Failed to parse supergraph SDL: {0}")]
ParseError(#[from] graphql_tools::parser::schema::ParseError),
#[error("Failed to build query planner: {0}")]
PlannerBuilderError(#[from] PlannerError),
}
static NEXT_SUPERGRAPH_DATA_ID: AtomicU64 = AtomicU64::new(0);
#[derive(Clone, Default)]
pub struct SupergraphOptions {
pub query_planner: QueryPlannerOptions,
pub traffic_shaping: SupergraphTrafficShapingConfig,
pub override_subgraph_urls: OverrideSubgraphUrlsConfig,
pub headers: HeadersConfig,
pub override_labels: OverrideLabelsConfig,
pub demand_control: Option<DemandControlConfig>,
pub subscriptions: SupergraphSubscriptionsConfig,
pub error_masking: ErrorMaskingConfig,
pub persisted_documents: PersistedDocumentsConfig,
pub hive_target: Option<String>,
}
pub struct SupergraphData {
pub cache_id: u64,
pub options: SupergraphOptions,
pub metadata: Arc<SchemaMetadata>,
pub planner: Planner,
pub supergraph_schema: Arc<Document>,
pub public_schema: PublicSchema,
}
#[derive(Clone)]
pub struct SupergraphSnapshot {
data: Arc<SupergraphData>,
retirement: CancellationToken,
}
impl Deref for SupergraphSnapshot {
type Target = SupergraphData;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl SupergraphSnapshot {
#[inline]
pub fn is_retired(&self) -> bool {
self.retirement.is_cancelled()
}
#[inline]
pub async fn retired(&self) {
self.retirement.cancelled().await
}
#[inline]
pub fn retirement_token(&self) -> CancellationToken {
self.retirement.clone()
}
}
pub struct Supergraph {
data: Arc<SupergraphData>,
retirement: CancellationToken,
}
impl Deref for Supergraph {
type Target = SupergraphData;
fn deref(&self) -> &Self::Target {
&self.data
}
}
impl Drop for Supergraph {
fn drop(&mut self) {
self.retirement.cancel();
}
}
impl From<&Supergraph> for SupergraphSnapshot {
fn from(val: &Supergraph) -> Self {
SupergraphSnapshot {
data: val.data.clone(),
retirement: val.retirement.clone(),
}
}
}
impl Supergraph {
pub fn from_document(
document: Document,
options: SupergraphOptions,
) -> Result<Self, SupergraphBuildError> {
let planner = Planner::new_from_supergraph(&document, options.query_planner.clone())?;
let metadata = Arc::new(planner.consumer_schema.schema_metadata());
let public_schema = PublicSchema {
document: planner.consumer_schema.document.clone(),
sdl: Arc::<str>::from(planner.consumer_schema.document.to_string()),
};
let cache_id = NEXT_SUPERGRAPH_DATA_ID
.fetch_update(Ordering::Relaxed, Ordering::Relaxed, |id| id.checked_add(1))
.expect("supergraph id space exhausted");
let data = SupergraphData {
cache_id,
options,
metadata,
planner,
supergraph_schema: Arc::new(document),
public_schema,
};
Ok(Self {
data: Arc::new(data),
retirement: CancellationToken::new(),
})
}
pub fn from_sdl(sdl: &str, options: SupergraphOptions) -> Result<Self, SupergraphBuildError> {
Self::from_document(safe_parse_schema(sdl)?, options)
}
pub fn snapshot(&self) -> SupergraphSnapshot {
self.into()
}
}
pub type OnSupergraphLoadResult = Result<Supergraph, GraphQLError>;
pub struct OnSupergraphLoadStartHookPayload {
pub current_supergraph_data: Option<SupergraphSnapshot>,
pub new_ast: Document,
}
impl StartHookPayload<OnSupergraphLoadEndHookPayload, OnSupergraphLoadResult>
for OnSupergraphLoadStartHookPayload
{
}
pub type OnSupergraphLoadStartHookResult<'exec> = crate::executor::plugin_trait::StartHookResult<
'exec,
OnSupergraphLoadStartHookPayload,
OnSupergraphLoadEndHookPayload,
OnSupergraphLoadResult,
>;
pub struct OnSupergraphLoadEndHookPayload {
pub new_supergraph: Supergraph,
}
impl EndHookPayload<OnSupergraphLoadResult> for OnSupergraphLoadEndHookPayload {}
pub type OnSupergraphLoadEndHookResult = crate::executor::plugin_trait::EndHookResult<
OnSupergraphLoadEndHookPayload,
OnSupergraphLoadResult,
>;
impl FromGraphQLErrorToResponse for OnSupergraphLoadResult {
fn from_graphql_error_to_response(error: GraphQLError, _status_code: http::StatusCode) -> Self {
Err(error)
}
}