use std::collections::HashMap;
use std::sync::Arc;
use apollo_federation::connectors::CustomConfiguration;
use apollo_federation::connectors::SourceName;
use apollo_federation::connectors::expand::Connectors;
use http::Uri;
use schemars::JsonSchema;
use serde::Deserialize;
use serde::Serialize;
use super::incompatible::warn_incompatible_plugins;
use crate::Configuration;
use crate::plugins::connectors::plugin::PLUGIN_NAME;
use crate::services::connector_service::ConnectorSourceRef;
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub(crate) struct ConnectorsConfig {
#[serde(default)]
#[deprecated(note = "use `sources`")]
pub(crate) subgraphs: HashMap<String, SubgraphConnectorConfiguration>,
#[serde(default)]
pub(crate) sources: HashMap<String, SourceConfiguration>,
#[serde(default)]
pub(crate) debug_extensions: bool,
#[serde(default)]
pub(crate) max_requests_per_operation_per_source: Option<usize>,
#[serde(default)]
pub(crate) expose_sources_in_context: bool,
#[serde(default)]
#[deprecated(note = "Connect spec v0.2 is now available.")]
pub(crate) preview_connect_v0_2: Option<bool>,
#[serde(default)]
#[deprecated(note = "Connect spec v0.3 is now available.")]
pub(crate) preview_connect_v0_3: Option<bool>,
#[serde(default)]
pub(crate) preview_connect_v0_4: Option<bool>,
}
#[derive(Clone, Debug, Default, Deserialize, JsonSchema, Serialize)]
#[serde(deny_unknown_fields, default)]
pub(crate) struct SubgraphConnectorConfiguration {
#[schemars(schema_with = "sources_configuration_schema")]
pub(crate) sources: HashMap<SourceName, SourceConfiguration>,
#[serde(rename = "$config")]
pub(crate) custom: CustomConfiguration,
}
fn sources_configuration_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "object",
"additionalProperties": generator.subschema_for::<SourceConfiguration>(),
})
}
#[derive(Clone, Debug, Default, Deserialize, JsonSchema, Serialize)]
#[serde(deny_unknown_fields, default)]
pub(crate) struct SourceConfiguration {
#[serde(default, with = "http_serde::option::uri")]
#[schemars(schema_with = "uri_schema")]
pub(crate) override_url: Option<Uri>,
pub(crate) max_requests_per_operation: Option<usize>,
#[serde(rename = "$config")]
pub(crate) custom: CustomConfiguration,
}
fn uri_schema(_generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": ["string", "null"],
"format": "uri",
})
}
pub(crate) fn apply_config(
router_config: &Configuration,
mut connectors: Connectors,
) -> Connectors {
warn_incompatible_plugins(router_config, &connectors);
let Some(config) = router_config.apollo_plugins.plugins.get(PLUGIN_NAME) else {
return connectors;
};
let Ok(config) = serde_json::from_value::<ConnectorsConfig>(config.clone()) else {
return connectors;
};
for connector in Arc::make_mut(&mut connectors.by_service_name).values_mut() {
if let Ok(source_ref) = ConnectorSourceRef::try_from(&mut *connector)
&& let Some(source_config) = config.sources.get(&source_ref.to_string())
{
if let Some(uri) = source_config.override_url.as_ref() {
connector.transport.source_template = uri.to_string().parse().ok();
}
if let Some(max_requests) = source_config.max_requests_per_operation {
connector.max_requests = Some(max_requests);
}
connector.config = Some(source_config.custom.clone());
}
#[allow(deprecated)]
let Some(subgraph_config) = config.subgraphs.get(&connector.id.subgraph_name) else {
continue;
};
if let Some(source_config) = connector
.id
.source_name
.as_ref()
.and_then(|source_name| subgraph_config.sources.get(source_name))
{
if let Some(uri) = source_config.override_url.as_ref() {
connector.transport.source_template = uri.to_string().parse().ok();
}
if let Some(max_requests) = source_config.max_requests_per_operation {
connector.max_requests = Some(max_requests);
}
}
connector.config = Some(subgraph_config.custom.clone());
}
connectors
}