bitbucket_server_rs/api/pull_request_changes_get.rs
1//! # Pull Request Changes API
2//!
3//! This module provides functionality to retrieve changes in pull requests from Bitbucket Server.
4//! It allows fetching the list of files that were modified, added, or deleted in a pull request.
5
6use crate::api::Api;
7use crate::client::{ApiRequest, ApiResponse, Client};
8use derive_builder::Builder;
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12/// Represents the changes in a pull request.
13///
14/// This struct contains information about the changes between the source and target branches
15/// of a pull request, including the list of modified files.
16#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
17#[serde(rename_all = "camelCase")]
18pub struct PullRequestChanges {
19 /// The commit hash of the source branch
20 pub from_hash: String,
21
22 /// The commit hash of the target branch
23 pub to_hash: String,
24
25 /// Array of changes (files that were modified, added, or deleted)
26 #[serde(rename = "values", skip_serializing_if = "Option::is_none")]
27 pub values: Option<Vec<ChangeItem>>,
28}
29
30/// Represents a single change item in a pull request.
31///
32/// This struct contains information about a single file that was changed in a pull request.
33#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
34pub struct ChangeItem {
35 /// The content ID of the change
36 #[serde(rename = "contentId")]
37 pub content_id: String,
38
39 /// The type of change (e.g., "ADD", "MODIFY", "DELETE")
40 #[serde(rename = "type")]
41 pub change_type: String,
42
43 /// The path of the file that was changed
44 #[serde(rename = "path")]
45 pub path: Path,
46}
47
48/// Represents the path of a file in a change.
49#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
50pub struct Path {
51 /// The string representation of the path
52 #[serde(rename = "toString")]
53 pub to_string: String,
54}
55
56/// Request builder for retrieving pull request changes.
57///
58/// This struct is used to build and send requests to retrieve changes in a pull request.
59#[derive(Debug, Default, Builder)]
60pub struct PullRequestChangesGet {
61 /// The HTTP client to use for making requests
62 client: Client,
63
64 /// The key of the project containing the repository
65 project_key: String,
66
67 /// The ID of the pull request
68 pull_request_id: String,
69
70 /// The slug of the repository
71 repository_slug: String,
72
73 /// The "since" commit hash to stream changes for a RANGE arbitrary change scope
74 #[builder(setter(into, strip_option), default)]
75 since_id: Option<String>,
76
77 /// UNREVIEWED to stream the unreviewed changes for the current user (if they exist);
78 /// RANGE to stream changes between two arbitrary commits (requires 'sinceId' and 'untilId');
79 /// otherwise ALL to stream all changes (the default)
80 #[builder(setter(into, strip_option), default)]
81 change_scope: Option<String>,
82
83 /// The "until" commit hash to stream changes for a RANGE arbitrary change scope
84 #[builder(setter(into, strip_option), default)]
85 until_id: Option<String>,
86
87 /// Start number for the page (inclusive). If not passed, first page is assumed.
88 #[builder(setter(into, strip_option), default)]
89 start: Option<u32>,
90
91 /// Number of items to return. If not passed, a page size of 25 is used.
92 #[builder(setter(into, strip_option), default)]
93 limit: Option<u32>,
94
95 /// If true, the response will include all comments on the changed files
96 #[builder(setter(into, strip_option), default)]
97 with_comments: Option<bool>,
98}
99
100impl ApiRequest for PullRequestChangesGet {
101 type Output = PullRequestChanges;
102
103 /// Sends the request to retrieve pull request changes.
104 ///
105 /// # Returns
106 ///
107 /// A Result containing either the pull request changes or an error.
108 async fn send(&self) -> ApiResponse<Self::Output> {
109 let request_uri = format!(
110 "api/latest/projects/{}/repos/{}/pull-requests/{}/changes",
111 self.project_key, self.repository_slug, self.pull_request_id
112 );
113
114 let mut params = HashMap::new();
115
116 if let Some(since_id) = &self.since_id {
117 params.insert("sinceId".to_string(), since_id.clone());
118 }
119 if let Some(change_scope) = &self.change_scope {
120 params.insert("changeScope".to_string(), change_scope.clone());
121 }
122 if let Some(until_id) = &self.until_id {
123 params.insert("untilId".to_string(), until_id.clone());
124 }
125 if let Some(start) = &self.start {
126 params.insert("start".to_string(), start.to_string());
127 }
128 if let Some(limit) = &self.limit {
129 params.insert("limit".to_string(), limit.to_string());
130 }
131 if let Some(with_comments) = &self.with_comments {
132 params.insert("withComments".to_string(), with_comments.to_string());
133 }
134
135 self.client.get::<Self>(&request_uri, Some(params)).await
136 }
137}
138
139impl Api {
140 /// Creates a request builder for retrieving changes in a pull request.
141 ///
142 /// This method returns a builder that can be used to configure and send a request
143 /// to retrieve the changes in a pull request.
144 ///
145 /// # Arguments
146 ///
147 /// * `project_key` - The key of the project containing the repository
148 /// * `repository_slug` - The slug of the repository
149 /// * `pull_request_id` - The ID of the pull request
150 ///
151 /// # Returns
152 ///
153 /// A builder for configuring and sending the request
154 ///
155 /// # Example
156 ///
157 /// ```no_run
158 /// use bitbucket_server_rs::client::{new, ApiRequest};
159 ///
160 /// #[tokio::main]
161 /// async fn main() -> Result<(), Box<dyn std::error::Error>> {
162 /// let client = new("https://bitbucket-server/rest", "API_TOKEN");
163 ///
164 /// let response = client
165 /// .api()
166 /// .pull_request_changes_get("PROJECT", "REPO", "123")
167 /// .limit(50u32)
168 /// .build()?
169 /// .send()
170 /// .await?;
171 ///
172 /// Ok(())
173 /// }
174 /// ```
175 ///
176 /// See the [Bitbucket Data Center REST API documentation](https://developer.atlassian.com/server/bitbucket/rest/v811/api-group-pull-requests/#api-api-latest-projects-projectkey-repos-repositoryslug-pull-requests-pullrequestid-changes-get)
177 pub fn pull_request_changes_get(
178 self,
179 project_key: &str,
180 repository_slug: &str,
181 pull_request_id: &str,
182 ) -> PullRequestChangesGetBuilder {
183 let mut builder = PullRequestChangesGetBuilder::default();
184 builder
185 .client(self.client.clone())
186 .project_key(project_key.to_string())
187 .repository_slug(repository_slug.to_string())
188 .pull_request_id(pull_request_id.to_string());
189 builder
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196
197 #[test]
198 fn it_can_deserialize() {
199 let json = mock_json();
200 let pull_request_changes: PullRequestChanges = serde_json::from_str(&json).unwrap();
201
202 assert_eq!(pull_request_changes, mock_struct());
203 } // end of it_can_deserialize
204
205 #[test]
206 fn it_can_serialize() {
207 let pull_request_changes_struct = mock_struct();
208 let json = serde_json::to_string(&pull_request_changes_struct).unwrap();
209
210 assert_eq!(json, mock_json());
211 } // end of it_can_serialize
212
213 fn mock_struct() -> PullRequestChanges {
214 PullRequestChanges {
215 from_hash: "from_hash".to_string(),
216 to_hash: "to_hash".to_string(),
217 values: Some(vec![
218 ChangeItem {
219 content_id: "12345".to_string(),
220 change_type: "ADD".to_string(),
221 path: Path {
222 to_string: "path/to/file".to_string(),
223 },
224 },
225 ChangeItem {
226 content_id: "67890".to_string(),
227 change_type: "COPY".to_string(),
228 path: Path {
229 to_string: "another/target".to_string(),
230 },
231 },
232 ]),
233 }
234 }
235
236 fn mock_json() -> String {
237 r#"{"fromHash":"from_hash","toHash":"to_hash","values":[{"contentId":"12345","type":"ADD","path":{"toString":"path/to/file"}},{"contentId":"67890","type":"COPY","path":{"toString":"another/target"}}]}"#.to_string()
238 }
239}