1use anyhow::Result;
2use anyhow::anyhow;
3use cynic::http::ReqwestExt;
4use reqwest::Client;
5
6pub struct LinearClient {
7 client: Client,
8 url: String,
9 api_key: String,
10}
11
12pub fn extract_data<Q>(resp: cynic::GraphQlResponse<Q>) -> Result<Q> {
14 if let Some(errors) = resp.errors
15 && !errors.is_empty()
16 {
17 let mut parts = Vec::new();
18 for e in errors {
19 let path = e.path.unwrap_or_default();
20 let path_str = if path.is_empty() {
21 String::new()
22 } else {
23 let p = path
24 .into_iter()
25 .map(|v| match v {
26 cynic::GraphQlErrorPathSegment::Field(f) => f,
27 cynic::GraphQlErrorPathSegment::Index(i) => i.to_string(),
28 })
29 .collect::<Vec<_>>()
30 .join(".");
31 format!(" (path: {})", p)
32 };
33 parts.push(format!("{}{}", e.message, path_str));
34 }
35 return Err(anyhow!(
36 "GraphQL errors from Linear:\n- {}",
37 parts.join("\n- ")
38 ));
39 }
40
41 match resp.data {
42 Some(data) => Ok(data),
43 None => Err(anyhow!("No data returned from Linear")),
44 }
45}
46
47impl LinearClient {
48 pub fn new(api_key: Option<String>) -> Result<Self> {
49 let api_key = match api_key.or_else(|| std::env::var("LINEAR_API_KEY").ok()) {
50 Some(k) if !k.is_empty() => k,
51 _ => return Err(anyhow!("LINEAR_API_KEY environment variable is not set")),
52 };
53
54 let url = std::env::var("LINEAR_GRAPHQL_URL")
55 .ok()
56 .filter(|u| !u.is_empty())
57 .unwrap_or_else(|| "https://api.linear.app/graphql".to_string());
58
59 let client = Client::builder().user_agent("linear-tools/0.1.0").build()?;
60
61 Ok(Self {
62 client,
63 url,
64 api_key,
65 })
66 }
67
68 pub async fn run<Q, V>(&self, op: cynic::Operation<Q, V>) -> Result<cynic::GraphQlResponse<Q>>
69 where
70 Q: serde::de::DeserializeOwned + 'static,
71 V: serde::Serialize,
72 {
73 let mut req = self
74 .client
75 .post(&self.url)
76 .header("Content-Type", "application/json");
77
78 if self.api_key.starts_with("lin_api_") {
82 req = req.header("Authorization", &self.api_key);
83 } else {
84 req = req.bearer_auth(&self.api_key);
85 }
86
87 let result = req.run_graphql(op).await;
88 result.map_err(|e| anyhow!(e))
89 }
90}