gr/github/
release.rs

1use crate::{
2    api_traits::{ApiOperation, Deploy, DeployAsset, NumberDeltaErr},
3    cmds::release::{Release, ReleaseAssetListBodyArgs, ReleaseAssetMetadata, ReleaseBodyArgs},
4    io::{HttpResponse, HttpRunner},
5    remote::query,
6    Result,
7};
8
9use super::Github;
10
11impl<R: HttpRunner<Response = HttpResponse>> Deploy for Github<R> {
12    fn list(&self, args: ReleaseBodyArgs) -> Result<Vec<Release>> {
13        let url = format!("{}/repos/{}/releases", self.rest_api_basepath, self.path);
14        query::paged(
15            &self.runner,
16            &url,
17            args.from_to_page,
18            self.request_headers(),
19            None,
20            ApiOperation::Release,
21            |value| GithubReleaseFields::from(value).into(),
22        )
23    }
24
25    fn num_pages(&self) -> Result<Option<u32>> {
26        let (url, headers) = self.resource_release_metadata_url();
27        query::num_pages(&self.runner, &url, headers, ApiOperation::Release)
28    }
29
30    fn num_resources(&self) -> Result<Option<NumberDeltaErr>> {
31        let (url, headers) = self.resource_release_metadata_url();
32        query::num_resources(&self.runner, &url, headers, ApiOperation::Release)
33    }
34}
35
36impl<R> Github<R> {
37    fn resource_release_metadata_url(&self) -> (String, crate::http::Headers) {
38        let url = format!(
39            "{}/repos/{}/releases?page=1",
40            self.rest_api_basepath, self.path
41        );
42        let headers = self.request_headers();
43        (url, headers)
44    }
45
46    fn resource_release_assets_metadata_url(&self, args: ReleaseAssetListBodyArgs) -> String {
47        let url = format!(
48            "{}/repos/{}/releases/{}/assets?page=1",
49            self.rest_api_basepath, self.path, args.id
50        );
51        url
52    }
53}
54
55impl<R: HttpRunner<Response = HttpResponse>> DeployAsset for Github<R> {
56    fn list(&self, args: ReleaseAssetListBodyArgs) -> Result<Vec<ReleaseAssetMetadata>> {
57        let url = format!(
58            "{}/repos/{}/releases/{}/assets",
59            self.rest_api_basepath, self.path, args.id
60        );
61        query::paged(
62            &self.runner,
63            &url,
64            args.list_args,
65            self.request_headers(),
66            None,
67            ApiOperation::Release,
68            |value| GithubReleaseAssetFields::from(value).into(),
69        )
70    }
71
72    fn num_pages(&self, args: ReleaseAssetListBodyArgs) -> Result<Option<u32>> {
73        let url = self.resource_release_assets_metadata_url(args);
74        query::num_pages(
75            &self.runner,
76            &url,
77            self.request_headers(),
78            ApiOperation::Release,
79        )
80    }
81
82    fn num_resources(&self, args: ReleaseAssetListBodyArgs) -> Result<Option<NumberDeltaErr>> {
83        let url = self.resource_release_assets_metadata_url(args);
84        query::num_resources(
85            &self.runner,
86            &url,
87            self.request_headers(),
88            ApiOperation::Release,
89        )
90    }
91}
92
93pub struct GithubReleaseFields {
94    release: Release,
95}
96
97impl From<&serde_json::Value> for GithubReleaseFields {
98    fn from(value: &serde_json::Value) -> Self {
99        Self {
100            release: Release::builder()
101                .id(value["id"].as_i64().unwrap().to_string())
102                .url(value["html_url"].as_str().unwrap().to_string())
103                .tag(value["tag_name"].as_str().unwrap().to_string())
104                .title(value["name"].as_str().unwrap_or_default().to_string())
105                .description(value["body"].as_str().unwrap_or_default().to_string())
106                .prerelease(value["prerelease"].as_bool().unwrap_or(false))
107                .created_at(value["created_at"].as_str().unwrap().to_string())
108                .updated_at(value["published_at"].as_str().unwrap().to_string())
109                .build()
110                .unwrap(),
111        }
112    }
113}
114
115impl From<GithubReleaseFields> for Release {
116    fn from(fields: GithubReleaseFields) -> Self {
117        fields.release
118    }
119}
120
121pub struct GithubReleaseAssetFields {
122    release_asset: ReleaseAssetMetadata,
123}
124
125impl From<&serde_json::Value> for GithubReleaseAssetFields {
126    fn from(value: &serde_json::Value) -> Self {
127        Self {
128            release_asset: ReleaseAssetMetadata::builder()
129                .id(value["id"].as_i64().unwrap().to_string())
130                .name(value["name"].as_str().unwrap().to_string())
131                .url(value["browser_download_url"].as_str().unwrap().to_string())
132                .size(value["size"].as_i64().unwrap().to_string())
133                .created_at(value["created_at"].as_str().unwrap().to_string())
134                .updated_at(value["updated_at"].as_str().unwrap().to_string())
135                .build()
136                .unwrap(),
137        }
138    }
139}
140
141impl From<GithubReleaseAssetFields> for ReleaseAssetMetadata {
142    fn from(fields: GithubReleaseAssetFields) -> Self {
143        fields.release_asset
144    }
145}
146
147#[cfg(test)]
148mod test {
149
150    use crate::{
151        api_traits::ApiOperation,
152        http::Headers,
153        setup_client,
154        test::utils::{default_github, get_contract, ContractType, ResponseContracts},
155    };
156
157    use super::*;
158
159    #[test]
160    fn test_list_releases() {
161        let contracts = ResponseContracts::new(ContractType::Github).add_contract(
162            200,
163            "list_releases.json",
164            None,
165        );
166        let (client, github) = setup_client!(contracts, default_github(), dyn Deploy);
167        let args = ReleaseBodyArgs::builder()
168            .from_to_page(None)
169            .build()
170            .unwrap();
171        let runs = github.list(args).unwrap();
172        assert_eq!(
173            "https://api.github.com/repos/jordilin/githapi/releases",
174            *client.url(),
175        );
176        assert_eq!(Some(ApiOperation::Release), *client.api_operation.borrow());
177        assert_eq!(1, runs.len());
178    }
179
180    #[test]
181    fn test_release_num_pages() {
182        let link_header = "<https://api.github.com/repos/jordilin/githapi/releases?page=2>; rel=\"next\", <https://api.github.com/repos/jordilin/githapi/releases?page=2>; rel=\"last\"";
183        let mut headers = Headers::new();
184        headers.set("link".to_string(), link_header.to_string());
185        let contracts = ResponseContracts::new(ContractType::Github).add_body::<String>(
186            200,
187            None,
188            Some(headers),
189        );
190        let (client, github) = setup_client!(contracts, default_github(), dyn Deploy);
191        let runs = github.num_pages().unwrap();
192        assert_eq!(
193            "https://api.github.com/repos/jordilin/githapi/releases?page=1",
194            *client.url(),
195        );
196        assert_eq!(Some(ApiOperation::Release), *client.api_operation.borrow());
197        assert_eq!(Some(2), runs);
198    }
199
200    #[test]
201    fn test_list_release_assets() {
202        let contracts = ResponseContracts::new(ContractType::Github).add_body(
203            200,
204            Some(format!(
205                "[{}]",
206                get_contract(ContractType::Github, "release_asset.json")
207            )),
208            None,
209        );
210        let (client, github) = setup_client!(contracts, default_github(), dyn DeployAsset);
211        let args = ReleaseAssetListBodyArgs::builder()
212            .id("123".to_string())
213            .list_args(None)
214            .build()
215            .unwrap();
216        let runs = github.list(args).unwrap();
217        assert_eq!(
218            "https://api.github.com/repos/jordilin/githapi/releases/123/assets",
219            *client.url(),
220        );
221        assert_eq!(Some(ApiOperation::Release), *client.api_operation.borrow());
222        assert_eq!(1, runs.len());
223    }
224
225    #[test]
226    fn test_query_num_release_assets_pages() {
227        let link_header = "<https://api.github.com/repos/jordilin/githapi/releases/123/assets?page=2>; rel=\"next\", <https://api.github.com/repos/jordilin/githapi/releases/123/assets?page=2>; rel=\"last\"";
228        let mut headers = Headers::new();
229        headers.set("link".to_string(), link_header.to_string());
230        let contracts = ResponseContracts::new(ContractType::Github).add_body::<String>(
231            200,
232            None,
233            Some(headers),
234        );
235        let (client, github) = setup_client!(contracts, default_github(), dyn DeployAsset);
236        let args = ReleaseAssetListBodyArgs::builder()
237            .id("123".to_string())
238            .list_args(None)
239            .build()
240            .unwrap();
241        let runs = github.num_pages(args).unwrap();
242        assert_eq!(
243            "https://api.github.com/repos/jordilin/githapi/releases/123/assets?page=1",
244            *client.url(),
245        );
246        assert_eq!(Some(ApiOperation::Release), *client.api_operation.borrow());
247        assert_eq!(Some(2), runs);
248    }
249
250    #[test]
251    fn test_query_num_release_assets_resources() {
252        let contracts = ResponseContracts::new(ContractType::Github).add_body(
253            200,
254            Some(format!(
255                "[{}]",
256                get_contract(ContractType::Github, "release_asset.json")
257            )),
258            None,
259        );
260        let (client, github) = setup_client!(contracts, default_github(), dyn DeployAsset);
261        let args = ReleaseAssetListBodyArgs::builder()
262            .id("123".to_string())
263            .list_args(None)
264            .build()
265            .unwrap();
266        github.num_resources(args).unwrap();
267        assert_eq!(
268            "https://api.github.com/repos/jordilin/githapi/releases/123/assets?page=1",
269            *client.url(),
270        );
271        assert_eq!(Some(ApiOperation::Release), *client.api_operation.borrow());
272    }
273}