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}