use reqwest::header::HeaderMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type")]
pub enum GraphqlAuth {
None,
Bearer(String),
#[serde(skip)]
Custom(HeaderMap),
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct GraphqlPagination {
pub has_next_page_path: String,
pub cursor_path: String,
pub cursor_variable: String,
pub page_size: Option<usize>,
pub page_size_variable: String,
}
impl Default for GraphqlPagination {
fn default() -> Self {
Self {
has_next_page_path: "$.data.*.pageInfo.hasNextPage".into(),
cursor_path: "$.data.*.pageInfo.endCursor".into(),
cursor_variable: "after".into(),
page_size: None,
page_size_variable: "first".into(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct GraphqlStreamConfig {
pub endpoint: String,
pub query: String,
pub variables: Value,
pub auth: GraphqlAuth,
#[serde(skip, default)]
pub headers: HeaderMap,
pub records_path: Option<String>,
pub pagination: Option<GraphqlPagination>,
pub max_pages: Option<usize>,
}
impl GraphqlStreamConfig {
pub fn new(endpoint: impl Into<String>, query: impl Into<String>) -> Self {
Self {
endpoint: endpoint.into(),
query: query.into(),
variables: Value::Object(Default::default()),
auth: GraphqlAuth::None,
headers: HeaderMap::new(),
records_path: None,
pagination: None,
max_pages: None,
}
}
pub fn variables(mut self, vars: Value) -> Self {
self.variables = vars;
self
}
pub fn auth(mut self, auth: GraphqlAuth) -> Self {
self.auth = auth;
self
}
pub fn headers(mut self, headers: HeaderMap) -> Self {
self.headers = headers;
self
}
pub fn records_path(mut self, path: impl Into<String>) -> Self {
self.records_path = Some(path.into());
self
}
pub fn pagination(mut self, pagination: GraphqlPagination) -> Self {
self.pagination = Some(pagination);
self
}
pub fn max_pages(mut self, max: usize) -> Self {
self.max_pages = Some(max);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn default_config() {
let config = GraphqlStreamConfig::new(
"https://api.example.com/graphql",
"query { users { id name } }",
);
assert_eq!(config.endpoint, "https://api.example.com/graphql");
assert!(config.records_path.is_none());
assert!(config.pagination.is_none());
assert!(config.max_pages.is_none());
}
#[test]
fn builder_methods() {
let config =
GraphqlStreamConfig::new("https://api.example.com/graphql", "query { users { id } }")
.variables(json!({"org": "acme"}))
.records_path("$.data.users.edges[*].node")
.max_pages(10)
.auth(GraphqlAuth::Bearer("token".into()));
assert_eq!(config.variables["org"], "acme");
assert_eq!(config.records_path.unwrap(), "$.data.users.edges[*].node");
assert_eq!(config.max_pages, Some(10));
}
#[test]
fn default_pagination() {
let pag = GraphqlPagination::default();
assert_eq!(pag.cursor_variable, "after");
assert_eq!(pag.page_size_variable, "first");
assert!(pag.page_size.is_none());
}
}