gitlab/api/projects/repository/commits/
create_status.rs1use derive_builder::Builder;
8
9use crate::api::common::{self, NameOrId};
10use crate::api::endpoint_prelude::*;
11use crate::api::ParamValue;
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[non_exhaustive]
16pub enum CommitStatusState {
17 Pending,
19 Running,
21 Success,
23 Failed,
25 Canceled,
27 Skipped,
29}
30
31impl CommitStatusState {
32 fn as_str(self) -> &'static str {
33 match self {
34 CommitStatusState::Pending => "pending",
35 CommitStatusState::Running => "running",
36 CommitStatusState::Success => "success",
37 CommitStatusState::Failed => "failed",
38 CommitStatusState::Canceled => "canceled",
39 CommitStatusState::Skipped => "skipped",
40 }
41 }
42}
43
44impl ParamValue<'static> for CommitStatusState {
45 fn as_value(&self) -> Cow<'static, str> {
46 self.as_str().into()
47 }
48}
49
50#[derive(Debug, Builder, Clone)]
52#[builder(setter(strip_option), build_fn(validate = "Self::validate_fields"))]
53pub struct CreateCommitStatus<'a> {
54 #[builder(setter(into))]
56 project: NameOrId<'a>,
57 #[builder(setter(into))]
59 commit: Cow<'a, str>,
60 #[builder(setter(into))]
62 state: CommitStatusState,
63
64 #[builder(setter(into), default)]
66 name: Option<Cow<'a, str>>,
67 #[builder(setter(into), default)]
71 ref_: Option<Cow<'a, str>>,
72 #[builder(setter(into), default)]
76 target_url: Option<Cow<'a, str>>,
77 #[builder(setter(into), default)]
81 description: Option<Cow<'a, str>>,
82 #[builder(default)]
84 coverage: Option<f64>,
85 #[builder(default)]
87 pipeline_id: Option<u64>,
88}
89
90impl<'a> CreateCommitStatus<'a> {
91 pub fn builder() -> CreateCommitStatusBuilder<'a> {
93 CreateCommitStatusBuilder::default()
94 }
95}
96
97impl CreateCommitStatusBuilder<'_> {
98 const MAX_FIELD_LENGTH: usize = 255;
99
100 fn validate_fields(&self) -> Result<(), String> {
101 if let Some(Some(ref r)) = self.ref_ {
102 if r.len() > Self::MAX_FIELD_LENGTH {
103 return Err(format!(
104 "ref_ exceeds maximum length of {} characters",
105 Self::MAX_FIELD_LENGTH,
106 ));
107 }
108 }
109 if let Some(Some(ref url)) = self.target_url {
110 if url.len() > Self::MAX_FIELD_LENGTH {
111 return Err(format!(
112 "target_url exceeds maximum length of {} characters",
113 Self::MAX_FIELD_LENGTH,
114 ));
115 }
116 }
117 if let Some(Some(ref desc)) = self.description {
118 if desc.len() > Self::MAX_FIELD_LENGTH {
119 return Err(format!(
120 "description exceeds maximum length of {} characters",
121 Self::MAX_FIELD_LENGTH,
122 ));
123 }
124 }
125 Ok(())
126 }
127}
128
129impl Endpoint for CreateCommitStatus<'_> {
130 fn method(&self) -> Method {
131 Method::POST
132 }
133
134 fn endpoint(&self) -> Cow<'static, str> {
135 format!(
136 "projects/{}/statuses/{}",
140 self.project,
141 common::path_escaped(&self.commit),
142 )
143 .into()
144 }
145
146 fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
147 let mut params = FormParams::default();
148
149 params
150 .push("state", self.state)
151 .push_opt("name", self.name.as_ref())
152 .push_opt("ref", self.ref_.as_ref())
153 .push_opt("target_url", self.target_url.as_ref())
154 .push_opt("description", self.description.as_ref())
155 .push_opt("coverage", self.coverage)
156 .push_opt("pipeline_id", self.pipeline_id);
157
158 params.into_body()
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use http::Method;
165
166 use crate::api::projects::repository::commits::{
167 CommitStatusState, CreateCommitStatus, CreateCommitStatusBuilderError,
168 };
169 use crate::api::{self, Query};
170 use crate::test::client::{ExpectedUrl, SingleTestClient};
171
172 #[test]
173 fn commit_status_state_as_str() {
174 let items = &[
175 (CommitStatusState::Pending, "pending"),
176 (CommitStatusState::Running, "running"),
177 (CommitStatusState::Success, "success"),
178 (CommitStatusState::Failed, "failed"),
179 (CommitStatusState::Canceled, "canceled"),
180 (CommitStatusState::Skipped, "skipped"),
181 ];
182
183 for (i, s) in items {
184 assert_eq!(i.as_str(), *s);
185 }
186 }
187
188 #[test]
189 fn project_commit_and_state_are_necessary() {
190 let err = CreateCommitStatus::builder().build().unwrap_err();
191 crate::test::assert_missing_field!(err, CreateCommitStatusBuilderError, "project");
192 }
193
194 #[test]
195 fn project_is_necessary() {
196 let err = CreateCommitStatus::builder()
197 .commit("master")
198 .state(CommitStatusState::Pending)
199 .build()
200 .unwrap_err();
201 crate::test::assert_missing_field!(err, CreateCommitStatusBuilderError, "project");
202 }
203
204 #[test]
205 fn commit_is_necessary() {
206 let err = CreateCommitStatus::builder()
207 .project(1)
208 .state(CommitStatusState::Pending)
209 .build()
210 .unwrap_err();
211 crate::test::assert_missing_field!(err, CreateCommitStatusBuilderError, "commit");
212 }
213
214 #[test]
215 fn state_is_necessary() {
216 let err = CreateCommitStatus::builder()
217 .project(1)
218 .commit("master")
219 .build()
220 .unwrap_err();
221 crate::test::assert_missing_field!(err, CreateCommitStatusBuilderError, "state");
222 }
223
224 #[test]
225 fn project_commit_and_state_are_sufficient() {
226 CreateCommitStatus::builder()
227 .project(1)
228 .commit("master")
229 .state(CommitStatusState::Pending)
230 .build()
231 .unwrap();
232 }
233
234 #[test]
235 fn endpoint() {
236 let endpoint = ExpectedUrl::builder()
237 .method(Method::POST)
238 .endpoint("projects/simple%2Fproject/statuses/0000000000000000000000000000000000000000")
239 .content_type("application/x-www-form-urlencoded")
240 .body_str("state=pending")
241 .build()
242 .unwrap();
243 let client = SingleTestClient::new_raw(endpoint, "");
244
245 let endpoint = CreateCommitStatus::builder()
246 .project("simple/project")
247 .commit("0000000000000000000000000000000000000000")
248 .state(CommitStatusState::Pending)
249 .build()
250 .unwrap();
251 api::ignore(endpoint).query(&client).unwrap();
252 }
253
254 #[test]
255 fn endpoint_name() {
256 let endpoint = ExpectedUrl::builder()
257 .method(Method::POST)
258 .endpoint("projects/simple%2Fproject/statuses/0000000000000000000000000000000000000000")
259 .content_type("application/x-www-form-urlencoded")
260 .body_str(concat!("state=pending", "&name=jobname"))
261 .build()
262 .unwrap();
263 let client = SingleTestClient::new_raw(endpoint, "");
264
265 let endpoint = CreateCommitStatus::builder()
266 .project("simple/project")
267 .commit("0000000000000000000000000000000000000000")
268 .state(CommitStatusState::Pending)
269 .name("jobname")
270 .build()
271 .unwrap();
272 api::ignore(endpoint).query(&client).unwrap();
273 }
274
275 #[test]
276 fn endpoint_ref() {
277 let endpoint = ExpectedUrl::builder()
278 .method(Method::POST)
279 .endpoint("projects/simple%2Fproject/statuses/0000000000000000000000000000000000000000")
280 .content_type("application/x-www-form-urlencoded")
281 .body_str(concat!("state=pending", "&ref=refname"))
282 .build()
283 .unwrap();
284 let client = SingleTestClient::new_raw(endpoint, "");
285
286 let endpoint = CreateCommitStatus::builder()
287 .project("simple/project")
288 .commit("0000000000000000000000000000000000000000")
289 .state(CommitStatusState::Pending)
290 .ref_("refname")
291 .build()
292 .unwrap();
293 api::ignore(endpoint).query(&client).unwrap();
294 }
295
296 #[test]
297 fn endpoint_target_url() {
298 let endpoint = ExpectedUrl::builder()
299 .method(Method::POST)
300 .endpoint("projects/simple%2Fproject/statuses/0000000000000000000000000000000000000000")
301 .content_type("application/x-www-form-urlencoded")
302 .body_str(concat!(
303 "state=pending",
304 "&target_url=https%3A%2F%2Ftest.invalid%2Fpath%3Fsome%3Dfoo",
305 ))
306 .build()
307 .unwrap();
308 let client = SingleTestClient::new_raw(endpoint, "");
309
310 let endpoint = CreateCommitStatus::builder()
311 .project("simple/project")
312 .commit("0000000000000000000000000000000000000000")
313 .state(CommitStatusState::Pending)
314 .target_url("https://test.invalid/path?some=foo")
315 .build()
316 .unwrap();
317 api::ignore(endpoint).query(&client).unwrap();
318 }
319
320 #[test]
321 fn endpoint_description() {
322 let endpoint = ExpectedUrl::builder()
323 .method(Method::POST)
324 .endpoint("projects/simple%2Fproject/statuses/0000000000000000000000000000000000000000")
325 .content_type("application/x-www-form-urlencoded")
326 .body_str(concat!("state=pending", "&description=description"))
327 .build()
328 .unwrap();
329 let client = SingleTestClient::new_raw(endpoint, "");
330
331 let endpoint = CreateCommitStatus::builder()
332 .project("simple/project")
333 .commit("0000000000000000000000000000000000000000")
334 .state(CommitStatusState::Pending)
335 .description("description")
336 .build()
337 .unwrap();
338 api::ignore(endpoint).query(&client).unwrap();
339 }
340
341 #[test]
342 fn endpoint_coverage() {
343 let endpoint = ExpectedUrl::builder()
344 .method(Method::POST)
345 .endpoint("projects/simple%2Fproject/statuses/0000000000000000000000000000000000000000")
346 .content_type("application/x-www-form-urlencoded")
347 .body_str(concat!("state=pending", "&coverage=90"))
348 .build()
349 .unwrap();
350 let client = SingleTestClient::new_raw(endpoint, "");
351
352 let endpoint = CreateCommitStatus::builder()
353 .project("simple/project")
354 .commit("0000000000000000000000000000000000000000")
355 .state(CommitStatusState::Pending)
356 .coverage(90.)
357 .build()
358 .unwrap();
359 api::ignore(endpoint).query(&client).unwrap();
360 }
361
362 #[test]
363 fn endpoint_pipeline_id() {
364 let endpoint = ExpectedUrl::builder()
365 .method(Method::POST)
366 .endpoint("projects/simple%2Fproject/statuses/0000000000000000000000000000000000000000")
367 .content_type("application/x-www-form-urlencoded")
368 .body_str(concat!("state=pending", "&pipeline_id=1"))
369 .build()
370 .unwrap();
371 let client = SingleTestClient::new_raw(endpoint, "");
372
373 let endpoint = CreateCommitStatus::builder()
374 .project("simple/project")
375 .commit("0000000000000000000000000000000000000000")
376 .state(CommitStatusState::Pending)
377 .pipeline_id(1)
378 .build()
379 .unwrap();
380 api::ignore(endpoint).query(&client).unwrap();
381 }
382
383 #[test]
384 fn endpoint_field_length_validation() {
385 const MAX_LEN: usize = 255;
386 let long_string: String = "a".repeat(MAX_LEN + 1);
387 let valid_string: String = "a".repeat(MAX_LEN);
388
389 let err = CreateCommitStatus::builder()
391 .project(1)
392 .commit("master")
393 .state(CommitStatusState::Pending)
394 .ref_(&long_string)
395 .build()
396 .unwrap_err();
397 assert!(matches!(
398 err,
399 CreateCommitStatusBuilderError::ValidationError(_),
400 ));
401 if let CreateCommitStatusBuilderError::ValidationError(msg) = err {
402 assert!(msg.contains("ref_ exceeds maximum length"));
403 }
404
405 let status = CreateCommitStatus::builder()
406 .project(1)
407 .commit("master")
408 .state(CommitStatusState::Pending)
409 .ref_(&valid_string)
410 .build()
411 .unwrap();
412 assert_eq!(status.ref_.as_ref().unwrap(), &valid_string);
413
414 let err = CreateCommitStatus::builder()
416 .project(1)
417 .commit("master")
418 .state(CommitStatusState::Pending)
419 .target_url(&long_string)
420 .build()
421 .unwrap_err();
422 assert!(matches!(
423 err,
424 CreateCommitStatusBuilderError::ValidationError(_),
425 ));
426 if let CreateCommitStatusBuilderError::ValidationError(msg) = err {
427 assert!(msg.contains("target_url exceeds maximum length"));
428 }
429
430 let status = CreateCommitStatus::builder()
431 .project(1)
432 .commit("master")
433 .state(CommitStatusState::Pending)
434 .target_url(&valid_string)
435 .build()
436 .unwrap();
437 assert_eq!(status.target_url.as_ref().unwrap(), &valid_string);
438
439 let err = CreateCommitStatus::builder()
441 .project(1)
442 .commit("master")
443 .state(CommitStatusState::Pending)
444 .description(&long_string)
445 .build()
446 .unwrap_err();
447 assert!(matches!(
448 err,
449 CreateCommitStatusBuilderError::ValidationError(_),
450 ));
451 if let CreateCommitStatusBuilderError::ValidationError(msg) = err {
452 assert!(msg.contains("description exceeds maximum length"));
453 }
454
455 let status = CreateCommitStatus::builder()
456 .project(1)
457 .commit("master")
458 .state(CommitStatusState::Pending)
459 .description(&valid_string)
460 .build()
461 .unwrap();
462 assert_eq!(status.description.as_ref().unwrap(), &valid_string);
463 }
464}