gitlab/api/projects/pipelines/
create.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7use derive_builder::Builder;
8
9use crate::api::common::NameOrId;
10use crate::api::endpoint_prelude::*;
11
12/// The type of a pipeline variable.
13#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
14#[non_exhaustive]
15pub enum PipelineVariableType {
16    /// An environment variable.
17    ///
18    /// The value of the variable is available as the value of the named environment variable.
19    #[default]
20    EnvVar,
21    /// A file variable.
22    ///
23    /// The value of the variable is available in a file given by the value of the named
24    /// environment variable.
25    File,
26}
27
28impl PipelineVariableType {
29    /// The variable type query parameter.
30    fn as_str(self) -> &'static str {
31        match self {
32            PipelineVariableType::EnvVar => "env_var",
33            PipelineVariableType::File => "file",
34        }
35    }
36}
37
38/// A pipeline variable.
39#[derive(Debug, Clone, Builder)]
40pub struct PipelineVariable<'a> {
41    /// The name of the pipeline variable.
42    #[builder(setter(into))]
43    pub key: Cow<'a, str>,
44    /// The value of the pipeline variable.
45    #[builder(setter(into))]
46    pub value: Cow<'a, str>,
47    /// The way the variable should be exposed to pipeline jobs.
48    #[builder(default)]
49    pub variable_type: PipelineVariableType,
50}
51
52impl<'a> PipelineVariable<'a> {
53    /// Create a builder for the pipeline variable.
54    pub fn builder() -> PipelineVariableBuilder<'a> {
55        PipelineVariableBuilder::default()
56    }
57}
58
59/// Create a new pipeline on a project.
60#[derive(Debug, Builder, Clone)]
61pub struct CreatePipeline<'a> {
62    /// The project to create the pipeline within.
63    #[builder(setter(into))]
64    project: NameOrId<'a>,
65
66    /// The ref to create the pipeline for.
67    #[builder(setter(into))]
68    ref_: Cow<'a, str>,
69
70    /// Search for users with a given custom attribute set.
71    #[builder(setter(name = "_variables"), default, private)]
72    variables: Vec<PipelineVariable<'a>>,
73}
74
75impl<'a> CreatePipeline<'a> {
76    /// Create a builder for the endpoint.
77    pub fn builder() -> CreatePipelineBuilder<'a> {
78        CreatePipelineBuilder::default()
79    }
80}
81
82impl<'a> CreatePipelineBuilder<'a> {
83    /// Add a variable.
84    pub fn variable(&mut self, variable: PipelineVariable<'a>) -> &mut Self {
85        self.variables.get_or_insert_with(Vec::new).push(variable);
86        self
87    }
88
89    /// Add multiple variables.
90    pub fn variables<I, V>(&mut self, iter: I) -> &mut Self
91    where
92        I: Iterator<Item = V>,
93        V: Into<PipelineVariable<'a>>,
94    {
95        self.variables
96            .get_or_insert_with(Vec::new)
97            .extend(iter.map(Into::into));
98        self
99    }
100}
101
102impl Endpoint for CreatePipeline<'_> {
103    fn method(&self) -> Method {
104        Method::POST
105    }
106
107    fn endpoint(&self) -> Cow<'static, str> {
108        format!("projects/{}/pipeline", self.project).into()
109    }
110
111    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
112        let mut params = FormParams::default();
113
114        params.push("ref", &self.ref_);
115
116        self.variables.iter().for_each(|variable| {
117            params.extend(
118                [
119                    ("variables[][key]", variable.key.as_ref()),
120                    ("variables[][value]", variable.value.as_ref()),
121                    (
122                        "variables[][variable_type]",
123                        variable.variable_type.as_str(),
124                    ),
125                ]
126                .iter()
127                .cloned(),
128            );
129        });
130
131        params.into_body()
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use http::Method;
138
139    use crate::api::projects::pipelines::{
140        CreatePipeline, CreatePipelineBuilderError, PipelineVariable, PipelineVariableBuilderError,
141        PipelineVariableType,
142    };
143    use crate::api::{self, Query};
144    use crate::test::client::{ExpectedUrl, SingleTestClient};
145
146    #[test]
147    fn pipeline_variable_type_default() {
148        assert_eq!(
149            PipelineVariableType::default(),
150            PipelineVariableType::EnvVar,
151        );
152    }
153
154    #[test]
155    fn pipeline_variable_type_as_str() {
156        let items = &[
157            (PipelineVariableType::EnvVar, "env_var"),
158            (PipelineVariableType::File, "file"),
159        ];
160
161        for (i, s) in items {
162            assert_eq!(i.as_str(), *s);
163        }
164    }
165
166    #[test]
167    fn pipeline_variable_key_and_value_are_necessary() {
168        let err = PipelineVariable::builder().build().unwrap_err();
169        crate::test::assert_missing_field!(err, PipelineVariableBuilderError, "key");
170    }
171
172    #[test]
173    fn pipeline_variable_key_is_necessary() {
174        let err = PipelineVariable::builder()
175            .value("value")
176            .build()
177            .unwrap_err();
178        crate::test::assert_missing_field!(err, PipelineVariableBuilderError, "key");
179    }
180
181    #[test]
182    fn pipeline_variable_value_is_necessary() {
183        let err = PipelineVariable::builder().key("key").build().unwrap_err();
184        crate::test::assert_missing_field!(err, PipelineVariableBuilderError, "value");
185    }
186
187    #[test]
188    fn pipeline_variable_key_and_value_are_sufficient() {
189        PipelineVariable::builder()
190            .key("key")
191            .value("value")
192            .build()
193            .unwrap();
194    }
195
196    #[test]
197    fn project_and_ref_are_needed() {
198        let err = CreatePipeline::builder().build().unwrap_err();
199        crate::test::assert_missing_field!(err, CreatePipelineBuilderError, "project");
200    }
201
202    #[test]
203    fn project_is_needed() {
204        let err = CreatePipeline::builder()
205            .ref_("testref")
206            .build()
207            .unwrap_err();
208        crate::test::assert_missing_field!(err, CreatePipelineBuilderError, "project");
209    }
210
211    #[test]
212    fn ref_is_needed() {
213        let err = CreatePipeline::builder().project(1).build().unwrap_err();
214        crate::test::assert_missing_field!(err, CreatePipelineBuilderError, "ref_");
215    }
216
217    #[test]
218    fn project_and_ref_are_sufficient() {
219        CreatePipeline::builder()
220            .project(1)
221            .ref_("testref")
222            .build()
223            .unwrap();
224    }
225
226    #[test]
227    fn endpoint() {
228        let endpoint = ExpectedUrl::builder()
229            .method(Method::POST)
230            .endpoint("projects/simple%2Fproject/pipeline")
231            .content_type("application/x-www-form-urlencoded")
232            .body_str("ref=master")
233            .build()
234            .unwrap();
235        let client = SingleTestClient::new_raw(endpoint, "");
236
237        let endpoint = CreatePipeline::builder()
238            .project("simple/project")
239            .ref_("master")
240            .build()
241            .unwrap();
242        api::ignore(endpoint).query(&client).unwrap();
243    }
244
245    #[test]
246    fn endpoint_pipeline_variables() {
247        let endpoint = ExpectedUrl::builder()
248            .method(Method::POST)
249            .endpoint("projects/1/pipeline")
250            .content_type("application/x-www-form-urlencoded")
251            .body_str(concat!(
252                "ref=master",
253                "&variables%5B%5D%5Bkey%5D=key",
254                "&variables%5B%5D%5Bvalue%5D=value",
255                "&variables%5B%5D%5Bvariable_type%5D=env_var",
256                "&variables%5B%5D%5Bkey%5D=file",
257                "&variables%5B%5D%5Bvalue%5D=contents",
258                "&variables%5B%5D%5Bvariable_type%5D=file",
259            ))
260            .build()
261            .unwrap();
262        let client = SingleTestClient::new_raw(endpoint, "");
263
264        let endpoint = CreatePipeline::builder()
265            .project(1)
266            .ref_("master")
267            .variable(
268                PipelineVariable::builder()
269                    .key("key")
270                    .value("value")
271                    .build()
272                    .unwrap(),
273            )
274            .variables(
275                [PipelineVariable::builder()
276                    .key("file")
277                    .value("contents")
278                    .variable_type(PipelineVariableType::File)
279                    .build()
280                    .unwrap()]
281                .iter()
282                .cloned(),
283            )
284            .build()
285            .unwrap();
286        api::ignore(endpoint).query(&client).unwrap();
287    }
288}