use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use super::{directive::DirectiveDefinition, mutation::MutationDefinition, query::QueryDefinition};
use crate::{
compiler::fact_table::FactTableMetadata,
schema::{
config_types::{
ChangelogConfig, DebugConfig, FederationConfig, GrpcConfig, McpConfig,
NamingConvention, ObserversConfig, RestConfig, SessionVariablesConfig,
SubscriptionsConfig, ValidationConfig,
},
graphql_type_defs::{
EnumDefinition, InputObjectDefinition, InterfaceDefinition, TypeDefinition,
UnionDefinition,
},
hierarchy::HierarchiesConfig,
observer_types::ObserverDefinition,
security_config::SecurityConfig,
source_types::SourceDefinition,
subscription_types::SubscriptionDefinition,
},
validation::CustomTypeRegistry,
};
pub const CURRENT_FRAISEQL_VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct ProducerVersion(Option<String>);
impl Default for ProducerVersion {
fn default() -> Self {
Self(Some(CURRENT_FRAISEQL_VERSION.to_string()))
}
}
impl ProducerVersion {
#[must_use]
pub const fn unstamped() -> Self {
Self(None)
}
#[must_use]
pub fn get(&self) -> Option<&str> {
self.0.as_deref()
}
#[must_use]
pub const fn is_unstamped(&self) -> bool {
self.0.is_none()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SubscribableEntity {
pub entity_type: String,
pub tables: Vec<String>,
#[serde(default, skip_serializing_if = "core::ops::Not::not")]
pub pre_image: bool,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct CompiledSchema {
#[serde(default)]
pub types: Vec<TypeDefinition>,
#[serde(default)]
pub enums: Vec<EnumDefinition>,
#[serde(default)]
pub input_types: Vec<InputObjectDefinition>,
#[serde(default)]
pub interfaces: Vec<InterfaceDefinition>,
#[serde(default)]
pub unions: Vec<UnionDefinition>,
#[serde(default)]
pub queries: Vec<QueryDefinition>,
#[serde(default)]
pub mutations: Vec<MutationDefinition>,
#[serde(default)]
pub subscriptions: Vec<SubscriptionDefinition>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub directives: Vec<DirectiveDefinition>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub fact_tables: HashMap<String, FactTableMetadata>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub observers: Vec<ObserverDefinition>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub sources: Vec<SourceDefinition>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub subscribable: Vec<SubscribableEntity>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub operation_cost_weights: HashMap<String, usize>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub federation: Option<FederationConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub security: Option<SecurityConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub auth: Option<AuthClientConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub observers_config: Option<ObserversConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subscriptions_config: Option<SubscriptionsConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub validation_config: Option<ValidationConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub debug_config: Option<DebugConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mcp_config: Option<McpConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rest_config: Option<RestConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub grpc_config: Option<GrpcConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub changelog: Option<ChangelogConfig>,
#[serde(default)]
pub session_variables: SessionVariablesConfig,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub locale: Option<crate::schema::LocaleConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub hierarchies_config: Option<HierarchiesConfig>,
#[serde(default)]
pub naming_convention: NamingConvention,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub naming_acronyms: Vec<String>,
#[serde(
default = "ProducerVersion::unstamped",
skip_serializing_if = "ProducerVersion::is_unstamped"
)]
pub fraiseql_version: ProducerVersion,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub schema_sdl: Option<String>,
#[serde(skip)]
pub derived: DerivedState,
}
#[derive(Debug, Clone, Default)]
pub struct DerivedState {
pub custom_scalars: CustomTypeRegistry,
pub query_index: HashMap<String, usize>,
pub mutation_index: HashMap<String, usize>,
pub subscription_index: HashMap<String, usize>,
pub where_relation_fields: fraiseql_db::where_clause::RelationFieldMaps,
}
impl PartialEq for DerivedState {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl CompiledSchema {
#[must_use]
pub fn new() -> Self {
Self::default()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AuthClientConfig {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub pkce: Option<PkceClientConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub social: Option<SocialAuthConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub local: Option<LocalAuthConfig>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LocalAuthConfig {
#[serde(default)]
pub password: bool,
#[serde(default)]
pub otp: bool,
#[serde(default)]
pub mfa: bool,
#[serde(default)]
pub anonymous: bool,
#[serde(default)]
pub email_verification: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mfa_issuer: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub email_from: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reset_url_template: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub magic_link_template: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub verification_url_template: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PkceClientConfig {
pub discovery_url: String,
pub client_id: String,
pub client_secret_env: String,
pub server_redirect_uri: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SocialAuthConfig {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub redirect_uri_allowlist: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub google: Option<GoogleSocialConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub github: Option<GitHubSocialConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub apple: Option<AppleSocialConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub discord: Option<DiscordSocialConfig>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub facebook: Option<FacebookSocialConfig>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GoogleSocialConfig {
pub client_id: String,
pub client_secret_env: String,
pub redirect_uri: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub discovery_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GitHubSocialConfig {
pub client_id: String,
pub client_secret_env: String,
pub redirect_uri: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub base_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub api_base_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct AppleSocialConfig {
pub client_id: String,
pub team_id: String,
pub key_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub private_key_env: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub private_key_path: Option<String>,
pub redirect_uri: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub base_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct DiscordSocialConfig {
pub client_id: String,
pub client_secret_env: String,
pub redirect_uri: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub base_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct FacebookSocialConfig {
pub client_id: String,
pub client_secret_env: String,
pub redirect_uri: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub api_version: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub base_url: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub graph_base_url: Option<String>,
}