gitlab 0.1900.1

Gitlab API client.
Documentation
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use derive_builder::Builder;

use crate::api::common::NameOrId;
use crate::api::endpoint_prelude::*;
use crate::api::ParamValue;

/// HTTP method to use when uploading a project export archive to a remote URL.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ExportUploadMethod {
    /// Upload using HTTP PUT.
    Put,
    /// Upload using HTTP POST.
    Post,
}

impl ExportUploadMethod {
    /// The string representation of the upload method.
    pub fn as_str(self) -> &'static str {
        match self {
            Self::Put => "PUT",
            Self::Post => "POST",
        }
    }
}

impl ParamValue<'static> for ExportUploadMethod {
    fn as_value(&self) -> Cow<'static, str> {
        self.as_str().into()
    }
}

/// Upload destination for a project export archive.
#[derive(Debug, Builder, Clone)]
#[builder(setter(strip_option))]
pub struct ExportUpload<'a> {
    /// URL to upload the project export archive to.
    #[builder(setter(into))]
    url: Cow<'a, str>,

    /// HTTP method to use when uploading the archive. Defaults to `PUT`.
    #[builder(default)]
    http_method: Option<ExportUploadMethod>,
}

impl<'a> ExportUpload<'a> {
    /// Create a builder for the upload destination.
    pub fn builder() -> ExportUploadBuilder<'a> {
        ExportUploadBuilder::default()
    }

    fn add_params<'b>(&'b self, params: &mut FormParams<'b>) {
        params
            .push("upload[url]", &self.url)
            .push_opt("upload[http_method]", self.http_method);
    }
}

/// Schedule a project export.
///
/// The export runs asynchronously. Use [`super::ExportStatus`] to poll for completion and
/// [`super::DownloadExport`] to retrieve the archive once it is ready.
#[derive(Debug, Builder, Clone)]
#[builder(setter(strip_option))]
pub struct ScheduleExport<'a> {
    /// The project to export.
    #[builder(setter(into))]
    project: NameOrId<'a>,

    /// Overrides the project description in the export.
    #[builder(setter(into), default)]
    description: Option<Cow<'a, str>>,

    /// Upload the export archive to a remote URL after it is generated.
    #[builder(default)]
    upload: Option<ExportUpload<'a>>,
}

impl<'a> ScheduleExport<'a> {
    /// Create a builder for the endpoint.
    pub fn builder() -> ScheduleExportBuilder<'a> {
        ScheduleExportBuilder::default()
    }
}

impl Endpoint for ScheduleExport<'_> {
    fn method(&self) -> Method {
        Method::POST
    }

    fn endpoint(&self) -> Cow<'static, str> {
        format!("projects/{}/export", self.project).into()
    }

    fn body(&self) -> Result<Option<(&'static str, Vec<u8>)>, BodyError> {
        let mut params = FormParams::default();

        params.push_opt("description", self.description.as_ref());

        if let Some(upload) = self.upload.as_ref() {
            upload.add_params(&mut params);
        }

        params.into_body()
    }
}

#[cfg(test)]
mod tests {
    use http::Method;

    use crate::api::projects::export::{
        ExportUpload, ExportUploadBuilderError, ExportUploadMethod, ScheduleExport,
        ScheduleExportBuilderError,
    };
    use crate::api::{self, Query};
    use crate::test::client::{ExpectedUrl, SingleTestClient};

    #[test]
    fn upload_method_as_str() {
        let items = &[
            (ExportUploadMethod::Put, "PUT"),
            (ExportUploadMethod::Post, "POST"),
        ];

        for (method, s) in items {
            assert_eq!(method.as_str(), *s);
        }
    }

    #[test]
    fn upload_url_is_necessary() {
        let err = ExportUpload::builder().build().unwrap_err();
        crate::test::assert_missing_field!(err, ExportUploadBuilderError, "url");
    }

    #[test]
    fn upload_url_is_sufficient() {
        ExportUpload::builder()
            .url("https://example.com/upload")
            .build()
            .unwrap();
    }

    #[test]
    fn project_is_necessary() {
        let err = ScheduleExport::builder().build().unwrap_err();
        crate::test::assert_missing_field!(err, ScheduleExportBuilderError, "project");
    }

    #[test]
    fn project_is_sufficient() {
        ScheduleExport::builder().project(1).build().unwrap();
    }

    #[test]
    fn endpoint() {
        let endpoint = ExpectedUrl::builder()
            .method(Method::POST)
            .endpoint("projects/simple%2Fproject/export")
            .content_type("application/x-www-form-urlencoded")
            .build()
            .unwrap();
        let client = SingleTestClient::new_raw(endpoint, "");

        let endpoint = ScheduleExport::builder()
            .project("simple/project")
            .build()
            .unwrap();
        api::ignore(endpoint).query(&client).unwrap();
    }

    #[test]
    fn endpoint_description() {
        let endpoint = ExpectedUrl::builder()
            .method(Method::POST)
            .endpoint("projects/simple%2Fproject/export")
            .content_type("application/x-www-form-urlencoded")
            .body_str("description=My+export+description")
            .build()
            .unwrap();
        let client = SingleTestClient::new_raw(endpoint, "");

        let endpoint = ScheduleExport::builder()
            .project("simple/project")
            .description("My export description")
            .build()
            .unwrap();
        api::ignore(endpoint).query(&client).unwrap();
    }

    #[test]
    fn endpoint_upload() {
        let endpoint = ExpectedUrl::builder()
            .method(Method::POST)
            .endpoint("projects/simple%2Fproject/export")
            .content_type("application/x-www-form-urlencoded")
            .body_str("upload%5Burl%5D=https%3A%2F%2Fexample.com%2Fupload")
            .build()
            .unwrap();
        let client = SingleTestClient::new_raw(endpoint, "");

        let endpoint = ScheduleExport::builder()
            .project("simple/project")
            .upload(
                ExportUpload::builder()
                    .url("https://example.com/upload")
                    .build()
                    .unwrap(),
            )
            .build()
            .unwrap();
        api::ignore(endpoint).query(&client).unwrap();
    }

    #[test]
    fn endpoint_upload_http_method() {
        let endpoint = ExpectedUrl::builder()
            .method(Method::POST)
            .endpoint("projects/simple%2Fproject/export")
            .content_type("application/x-www-form-urlencoded")
            .body_str(concat!(
                "upload%5Burl%5D=https%3A%2F%2Fexample.com%2Fupload",
                "&upload%5Bhttp_method%5D=POST",
            ))
            .build()
            .unwrap();
        let client = SingleTestClient::new_raw(endpoint, "");

        let endpoint = ScheduleExport::builder()
            .project("simple/project")
            .upload(
                ExportUpload::builder()
                    .url("https://example.com/upload")
                    .http_method(ExportUploadMethod::Post)
                    .build()
                    .unwrap(),
            )
            .build()
            .unwrap();
        api::ignore(endpoint).query(&client).unwrap();
    }
}