use std::collections::HashMap; use serde_json::{json, value::RawValue}; use sqlx::types::Json; use windmill_common::error::Error; use windmill_common::jobs::QueuedJob; use windmill_parser_graphql::parse_graphql_sig; use windmill_queue::HTTP_CLIENT; use serde::Deserialize; use crate::{common::build_args_map, AuthedClientBackgroundTask}; #[derive(Deserialize)] struct GraphqlApi { bearer_token: Option, base_url: String, custom_headers: Option>, } #[derive(Deserialize)] struct GraphqlResponse { data: Option>, errors: Option>, } #[derive(Deserialize)] struct GraphqlError { message: String, } pub async fn do_graphql( job: &QueuedJob, client: &AuthedClientBackgroundTask, query: &str, db: &sqlx::Pool, ) -> windmill_common::error::Result> { let args = build_args_map(job, client, db).await?.map(Json); let job_args = if args.is_some() { args.as_ref() } else { job.args.as_ref() }; let api = if let Some(db) = job_args.as_ref().and_then(|x| x.get("api")) { serde_json::from_str::(db.get()) .map_err(|e| Error::ExecutionErr(e.to_string()))? } else { return Err(Error::BadRequest("Missing api argument".to_string())); }; // variables is job_args except for api let mut variables = HashMap::new(); let sig = parse_graphql_sig(&query) .map_err(|x| Error::ExecutionErr(x.to_string()))? .args; if let Some(job_args) = job_args { for arg in &sig { variables.insert( arg.name.clone(), job_args .get(&arg.name) .map(|x| x.to_owned()) .unwrap_or_default(), ); } } let mut request = HTTP_CLIENT.post(api.base_url).json(&json!({ "query": query, "variables": variables })); if let Some(token) = &api.bearer_token { request = request.bearer_auth(token.as_str()); } if let Some(headers) = &api.custom_headers { for (k, v) in headers { request = request.header(k, v); } } let response = request .send() .await .map_err(|e| Error::ExecutionErr(e.to_string()))?; let result = response .json::() .await .map_err(|e| Error::ExecutionErr(e.to_string()))?; if let Some(errors) = result.errors { return Err(Error::ExecutionErr( errors .into_iter() .map(|x| x.message) .collect::>() .join("\n"), )); } // And then check that we got back the same string we sent over. return Ok(result .data .unwrap_or_else(|| serde_json::from_str("{}").unwrap())); }