leash_sdk/integrations/
linear.rs1use serde::Serialize;
8use serde_json::json;
9
10use crate::errors::{LeashError, Result};
11use crate::transport::Transport;
12
13use super::types::{
14 LinearComment, LinearCreateIssueInput, LinearIssue, LinearListIssuesFilter,
15 LinearListIssuesResult, LinearListProjectsFilter, LinearProject, LinearTeam,
16 LinearUpdateIssuePatch,
17};
18
19const PROVIDER: &str = "linear";
20
21#[derive(Debug, Clone)]
23pub struct Linear {
24 transport: Transport,
25}
26
27impl Linear {
28 pub(crate) fn new(transport: Transport) -> Self {
29 Self { transport }
30 }
31
32 pub async fn list_issues(
34 &self,
35 filter: LinearListIssuesFilter,
36 ) -> Result<LinearListIssuesResult> {
37 let raw = self
38 .transport
39 .integrations_call(PROVIDER, "list_issues", &filter)
40 .await?;
41 if raw.is_null() {
42 return Ok(LinearListIssuesResult::default());
43 }
44 if raw.is_array() {
45 let issues: Vec<LinearIssue> =
46 serde_json::from_value(raw).map_err(|e| LeashError::MalformedResponse {
47 message: format!("Linear list_issues: failed to decode array: {e}"),
48 })?;
49 return Ok(LinearListIssuesResult {
50 issues,
51 cursor: None,
52 });
53 }
54 serde_json::from_value(raw).map_err(|e| LeashError::MalformedResponse {
55 message: format!("Linear list_issues: failed to decode envelope: {e}"),
56 })
57 }
58
59 pub async fn get_issue(&self, id: &str) -> Result<LinearIssue> {
61 let raw = self
62 .transport
63 .integrations_call(PROVIDER, "get_issue", &json!({ "id": id }))
64 .await?;
65 decode(raw, "get_issue")
66 }
67
68 pub async fn create_issue(&self, input: LinearCreateIssueInput) -> Result<LinearIssue> {
70 let raw = self
71 .transport
72 .integrations_call(PROVIDER, "create_issue", &input)
73 .await?;
74 decode(raw, "create_issue")
75 }
76
77 pub async fn update_issue(
79 &self,
80 id: &str,
81 patch: LinearUpdateIssuePatch,
82 ) -> Result<LinearIssue> {
83 #[derive(Serialize)]
84 struct Body<'a> {
85 id: &'a str,
86 #[serde(flatten)]
87 patch: LinearUpdateIssuePatch,
88 }
89 let raw = self
90 .transport
91 .integrations_call(PROVIDER, "update_issue", &Body { id, patch })
92 .await?;
93 decode(raw, "update_issue")
94 }
95
96 pub async fn add_comment(&self, issue_id: &str, body: &str) -> Result<LinearComment> {
98 let raw = self
99 .transport
100 .integrations_call(
101 PROVIDER,
102 "add_comment",
103 &json!({ "issueId": issue_id, "body": body }),
104 )
105 .await?;
106 decode(raw, "add_comment")
107 }
108
109 pub async fn list_teams(&self) -> Result<Vec<LinearTeam>> {
111 let raw = self
112 .transport
113 .integrations_call(PROVIDER, "list_teams", &json!({}))
114 .await?;
115 if raw.is_null() {
116 return Ok(vec![]);
117 }
118 if raw.is_array() {
119 return serde_json::from_value(raw).map_err(|e| LeashError::MalformedResponse {
120 message: format!("Linear list_teams: failed to decode array: {e}"),
121 });
122 }
123 let envelope: serde_json::Value = raw;
124 if let Some(teams) = envelope.get("teams").cloned() {
125 return serde_json::from_value(teams).map_err(|e| LeashError::MalformedResponse {
126 message: format!("Linear list_teams: failed to decode teams: {e}"),
127 });
128 }
129 Ok(vec![])
130 }
131
132 pub async fn list_projects(
134 &self,
135 filter: LinearListProjectsFilter,
136 ) -> Result<Vec<LinearProject>> {
137 let raw = self
138 .transport
139 .integrations_call(PROVIDER, "list_projects", &filter)
140 .await?;
141 if raw.is_null() {
142 return Ok(vec![]);
143 }
144 if raw.is_array() {
145 return serde_json::from_value(raw).map_err(|e| LeashError::MalformedResponse {
146 message: format!("Linear list_projects: failed to decode array: {e}"),
147 });
148 }
149 let envelope: serde_json::Value = raw;
150 if let Some(projects) = envelope.get("projects").cloned() {
151 return serde_json::from_value(projects).map_err(|e| LeashError::MalformedResponse {
152 message: format!("Linear list_projects: failed to decode projects: {e}"),
153 });
154 }
155 Ok(vec![])
156 }
157}
158
159fn decode<T: serde::de::DeserializeOwned + Default>(
160 raw: serde_json::Value,
161 action: &str,
162) -> Result<T> {
163 if raw.is_null() {
164 return Ok(T::default());
165 }
166 serde_json::from_value(raw).map_err(|e| LeashError::MalformedResponse {
167 message: format!("Linear {action}: failed to decode: {e}"),
168 })
169}