1use crate::{
2 api_traits::{ApiOperation, ContainerRegistry},
3 cmds::docker::{DockerListBodyArgs, ImageMetadata, RegistryRepository, RepositoryTag},
4 io::{HttpResponse, HttpRunner},
5 remote::query,
6 Result,
7};
8
9use super::Gitlab;
10
11impl<R: HttpRunner<Response = HttpResponse>> ContainerRegistry for Gitlab<R> {
12 fn list_repositories(&self, args: DockerListBodyArgs) -> Result<Vec<RegistryRepository>> {
13 let url = format!(
14 "{}/registry/repositories?tags_count=true",
15 self.rest_api_basepath()
16 );
17 query::paged(
18 &self.runner,
19 &url,
20 args.body_args,
21 self.headers(),
22 None,
23 ApiOperation::ContainerRegistry,
24 |value| GitlabRegistryRepositoryFields::from(value).into(),
25 )
26 }
27
28 fn list_repository_tags(&self, args: DockerListBodyArgs) -> Result<Vec<RepositoryTag>> {
29 let repository_id = args.repo_id.unwrap();
32 let url = format!(
33 "{}/registry/repositories/{}/tags",
34 self.rest_api_basepath(),
35 repository_id
36 );
37 query::paged(
38 &self.runner,
39 &url,
40 args.body_args,
41 self.headers(),
42 None,
43 ApiOperation::ContainerRegistry,
44 |value| GitlabRepositoryTagFields::from(value).into(),
45 )
46 }
47
48 fn num_pages_repository_tags(&self, repository_id: i64) -> Result<Option<u32>> {
49 let url = self.resource_repository_tags_metadata_url(repository_id);
50 query::num_pages(
51 &self.runner,
52 &url,
53 self.headers(),
54 ApiOperation::ContainerRegistry,
55 )
56 }
57
58 fn num_resources_repository_tags(
59 &self,
60 repository_id: i64,
61 ) -> Result<Option<crate::api_traits::NumberDeltaErr>> {
62 let url = self.resource_repository_tags_metadata_url(repository_id);
63 query::num_resources(
64 &self.runner,
65 &url,
66 self.headers(),
67 ApiOperation::ContainerRegistry,
68 )
69 }
70
71 fn num_pages_repositories(&self) -> Result<Option<u32>> {
72 let url = self.resource_repositories_metadata_url();
73 query::num_pages(
74 &self.runner,
75 &url,
76 self.headers(),
77 ApiOperation::ContainerRegistry,
78 )
79 }
80
81 fn num_resources_repositories(&self) -> Result<Option<crate::api_traits::NumberDeltaErr>> {
82 let url = self.resource_repositories_metadata_url();
83 query::num_resources(
84 &self.runner,
85 &url,
86 self.headers(),
87 ApiOperation::ContainerRegistry,
88 )
89 }
90
91 fn get_image_metadata(&self, repository_id: i64, tag: &str) -> Result<ImageMetadata> {
92 let url = format!(
93 "{}/registry/repositories/{}/tags/{}",
94 self.rest_api_basepath(),
95 repository_id,
96 tag
97 );
98 query::get::<_, (), _>(
99 &self.runner,
100 &url,
101 None,
102 self.headers(),
103 ApiOperation::ContainerRegistry,
104 |value| GitlabImageMetadataFields::from(value).into(),
105 )
106 }
107}
108
109impl<R> Gitlab<R> {
110 fn resource_repository_tags_metadata_url(&self, repository_id: i64) -> String {
111 let url = format!(
112 "{}/registry/repositories/{}/tags?page=1",
113 self.rest_api_basepath(),
114 repository_id
115 );
116 url
117 }
118
119 fn resource_repositories_metadata_url(&self) -> String {
120 let url = format!("{}/registry/repositories?page=1", self.rest_api_basepath());
121 url
122 }
123}
124
125pub struct GitlabRegistryRepositoryFields {
126 id: i64,
127 location: String,
128 tags_count: i64,
129 created_at: String,
130}
131
132impl From<&serde_json::Value> for GitlabRegistryRepositoryFields {
133 fn from(data: &serde_json::Value) -> Self {
134 GitlabRegistryRepositoryFields {
135 id: data["id"].as_i64().unwrap(),
136 location: data["location"].as_str().unwrap().to_string(),
137 tags_count: data["tags_count"].as_i64().unwrap(),
138 created_at: data["created_at"].as_str().unwrap().to_string(),
139 }
140 }
141}
142
143impl From<GitlabRegistryRepositoryFields> for RegistryRepository {
144 fn from(data: GitlabRegistryRepositoryFields) -> Self {
145 RegistryRepository::builder()
146 .id(data.id)
147 .location(data.location)
148 .tags_count(data.tags_count)
149 .created_at(data.created_at)
150 .build()
151 .unwrap()
152 }
153}
154
155pub struct GitlabRepositoryTagFields {
156 name: String,
157 path: String,
158 location: String,
159 created_at: String,
160}
161
162impl From<&serde_json::Value> for GitlabRepositoryTagFields {
163 fn from(data: &serde_json::Value) -> Self {
164 GitlabRepositoryTagFields {
165 name: data["name"].as_str().unwrap().to_string(),
166 path: data["path"].as_str().unwrap().to_string(),
167 location: data["location"].as_str().unwrap().to_string(),
168 created_at: "1970-01-01T00:00:00Z".to_string(),
171 }
172 }
173}
174
175impl From<GitlabRepositoryTagFields> for RepositoryTag {
176 fn from(data: GitlabRepositoryTagFields) -> Self {
177 RepositoryTag::builder()
178 .name(data.name)
179 .path(data.path)
180 .location(data.location)
181 .created_at(data.created_at)
182 .build()
183 .unwrap()
184 }
185}
186
187pub struct GitlabImageMetadataFields {
188 name: String,
189 location: String,
190 short_sha: String,
191 size: i64,
192 created_at: String,
193}
194
195impl From<&serde_json::Value> for GitlabImageMetadataFields {
196 fn from(data: &serde_json::Value) -> Self {
197 GitlabImageMetadataFields {
198 name: data["name"].as_str().unwrap().to_string(),
199 location: data["location"].as_str().unwrap().to_string(),
200 short_sha: data["short_revision"].as_str().unwrap().to_string(),
201 size: data["total_size"].as_i64().unwrap(),
202 created_at: data["created_at"].as_str().unwrap().to_string(),
203 }
204 }
205}
206
207impl From<GitlabImageMetadataFields> for ImageMetadata {
208 fn from(data: GitlabImageMetadataFields) -> Self {
209 ImageMetadata::builder()
210 .name(data.name)
211 .location(data.location)
212 .short_sha(data.short_sha)
213 .size(data.size)
214 .created_at(data.created_at)
215 .build()
216 .unwrap()
217 }
218}
219
220#[cfg(test)]
221mod test {
222 use super::*;
223 use crate::{
224 http::Headers,
225 setup_client,
226 test::utils::{default_gitlab, ContractType, ResponseContracts},
227 };
228
229 #[test]
230 fn test_list_repositories_url() {
231 let contracts = ResponseContracts::new(ContractType::Gitlab).add_contract(
232 200,
233 "list_registry_repositories.json",
234 None,
235 );
236 let (client, gitlab) = setup_client!(contracts, default_gitlab(), dyn ContainerRegistry);
237 let args = DockerListBodyArgs::builder().repos(true).build().unwrap();
238 gitlab.list_repositories(args).unwrap();
239 assert_eq!(
240 "https://gitlab.com/api/v4/projects/jordilin%2Fgitlapi/registry/repositories?tags_count=true",
241 client.url().to_string(),
242 );
243 assert_eq!("1234", client.headers().get("PRIVATE-TOKEN").unwrap());
244 assert_eq!(
245 Some(ApiOperation::ContainerRegistry),
246 *client.api_operation.borrow()
247 );
248 }
249
250 #[test]
251 fn test_list_repository_tags_url() {
252 let contracts = ResponseContracts::new(ContractType::Gitlab).add_contract(
253 200,
254 "list_registry_repository_tags.json",
255 None,
256 );
257 let (client, gitlab) = setup_client!(contracts, default_gitlab(), dyn ContainerRegistry);
258 let args = DockerListBodyArgs::builder()
259 .repos(false)
260 .tags(true)
261 .repo_id(Some(1))
262 .build()
263 .unwrap();
264 gitlab.list_repository_tags(args).unwrap();
265 assert_eq!(
266 client.url().to_string(),
267 "https://gitlab.com/api/v4/projects/jordilin%2Fgitlapi/registry/repositories/1/tags"
268 );
269 assert_eq!("1234", client.headers().get("PRIVATE-TOKEN").unwrap());
270 assert_eq!(
271 Some(ApiOperation::ContainerRegistry),
272 *client.api_operation.borrow()
273 );
274 }
275
276 #[test]
277 fn test_query_num_pages_for_tags() {
278 let link_headers = r#"<https://gitlab.com/api/v4/projects/jordilin%2Fgitlapi/registry/repositories/1/tags?page=1>; rel="next", <https://gitlab.com/api/v4/projects/jordilin%2Fgitlapi/registry/repositories/1/tags?page=1>; rel="last""#;
279 let mut headers = Headers::new();
280 headers.set("link".to_string(), link_headers.to_string());
281 let contracts = ResponseContracts::new(ContractType::Gitlab).add_body::<String>(
282 200,
283 None,
284 Some(headers),
285 );
286 let (client, gitlab) = setup_client!(contracts, default_gitlab(), dyn ContainerRegistry);
287 assert_eq!(Some(1), gitlab.num_pages_repository_tags(1).unwrap());
288 assert_eq!(
289 "https://gitlab.com/api/v4/projects/jordilin%2Fgitlapi/registry/repositories/1/tags?page=1",
290 client.url().to_string(),
291 );
292 assert_eq!(
293 Some(ApiOperation::ContainerRegistry),
294 *client.api_operation.borrow()
295 );
296 }
297
298 #[test]
299 fn test_query_num_pages_for_registry_repositories() {
300 let link_headers = r#"<https://gitlab.com/api/v4/projects/jordilin%2Fgitlapi/registry/repositories?page=1>; rel="next", <https://gitlab.com/api/v4/projects/jordilin%2Fgitlapi/registry/repositories?page=1>; rel="last""#;
301 let mut headers = Headers::new();
302 headers.set("link".to_string(), link_headers.to_string());
303 let contracts = ResponseContracts::new(ContractType::Gitlab).add_body::<String>(
304 200,
305 None,
306 Some(headers),
307 );
308 let (client, gitlab) = setup_client!(contracts, default_gitlab(), dyn ContainerRegistry);
309 assert_eq!(Some(1), gitlab.num_pages_repositories().unwrap());
310 assert_eq!(
311 "https://gitlab.com/api/v4/projects/jordilin%2Fgitlapi/registry/repositories?page=1",
312 client.url().to_string(),
313 );
314 assert_eq!(
315 Some(ApiOperation::ContainerRegistry),
316 *client.api_operation.borrow()
317 );
318 }
319
320 #[test]
321 fn test_get_gitlab_registry_image_metadata() {
322 let contracts = ResponseContracts::new(ContractType::Gitlab).add_contract(
323 200,
324 "get_registry_repository_tag.json",
325 None,
326 );
327 let (client, gitlab) = setup_client!(contracts, default_gitlab(), dyn ContainerRegistry);
328 let _metadata = gitlab.get_image_metadata(1, "v0.0.1").unwrap();
329 assert_eq!("https://gitlab.com/api/v4/projects/jordilin%2Fgitlapi/registry/repositories/1/tags/v0.0.1",
330 client.url().to_string(),
331 );
332 assert_eq!(
333 Some(ApiOperation::ContainerRegistry),
334 *client.api_operation.borrow()
335 );
336 }
337}