Commit eb1c29f9 authored by Marcel's avatar Marcel
Browse files

gitlab: project export and import apis

types: project export and import types
error: add io error

Add support for the gitlab project import and export api.
parent ce3b3bb4
Pipeline #142189 failed with stage
......@@ -11,9 +11,15 @@ error_chain! {
foreign_links {
Reqwest(reqwest::Error)
#[doc = "An error from the reqwest crate."];
Io(::std::io::Error)
#[doc = "An IO error from the std lib."];
}
errors {
/// IO Error
IoError {
display("io error")
}
/// Error occurred when communicating with Gitlab.
Communication {
display("communication error")
......
......@@ -19,6 +19,9 @@ use types::*;
use std::borrow::Borrow;
use std::fmt::{self, Debug, Display};
use std::fs;
use std::io;
use std::path;
/// A Gitlab API token
///
......@@ -268,6 +271,18 @@ impl Gitlab {
)
}
/// Retrieve a project's export status
pub fn project_export_status(&self, project: ProjectId) -> Result<ProjectExport>
{
self.get(&format!("projects/{}/export", project))
}
/// Retrieve a project's import status
pub fn project_import_status(&self, project: ProjectId) -> Result<ProjectImport>
{
self.get(&format!("projects/{}/import", project))
}
/// Get all accessible groups.
pub fn groups<I, K, V>(&self, params: I) -> Result<Vec<Group>>
where
......@@ -371,6 +386,17 @@ impl Gitlab {
self.get_with_param(&format!("groups/{}/members/{}", group, user), params)
}
/// Get the projects of a group.
pub fn group_projects<I, K, V>(&self, group: GroupId, params: I) -> Result<Vec<Project>>
where
I: IntoIterator,
I::Item: Borrow<(K, V)>,
K: AsRef<str>,
V: AsRef<str>,
{
self.get_paged_with_param(&format!("groups/{}/projects", group), params)
}
/// Get the team members of a project.
pub fn project_members<I, K, V>(&self, project: ProjectId, params: I) -> Result<Vec<Member>>
where
......@@ -1324,6 +1350,60 @@ impl Gitlab {
self.put_with_param(path, &[("labels", labels.into_iter().join(","))])
}
/// Start a project export
pub fn export_project<U>(&self, project: ProjectId, params: U) -> Result<ProjectsExportBasic>
where
U: Serialize
{
let path = &format!("projects/{}/export", project);
self.post_with_param(path, params)
}
/// Download the finished project export.
pub fn download_project_export<P>(
&self,
project: ProjectId,
target_file: P
) -> Result<()>
where
P: AsRef<path::Path>
{
let path = &format!("projects/{}/export/download", project);
let mut response = self.get_raw(path)?;
let mut target_file = fs::File::create(target_file)?;
let _bytes_copied = io::copy(&mut response, &mut target_file)?;
Ok(())
}
/// Import a downloaded project export
pub fn import_project<P>(
&self,
import_project: ImportProject<P>
) -> Result<ProjectImport>
where
P: AsRef<path::Path>
{
let file = fs::File::open(import_project.file)?;
let path = &format!("projects/import");
let full_url = self.create_url(path)?;
let mut params: Vec<(&str, String)> = Vec::new();
params.push(("path", import_project.path));
if let Some(namespace) = import_project.namespace {
params.push(("namespace", namespace.full_path));
}
if let Some(overwrite) = import_project.overwrite {
params.push(("overwrite", overwrite.to_string()));
}
let req = self.client.post(full_url).form(&params).body(file);
self.send(req)
}
/// Create a URL to an API endpoint.
fn create_url(&self, url: &str) -> Result<Url> {
debug!(target: "gitlab", "api call {}", url);
......@@ -1372,6 +1452,29 @@ impl Gitlab {
serde_json::from_value::<T>(v).chain_err(|| ErrorKind::Deserialize)
}
fn send_raw(&self, req: RequestBuilder) -> Result<reqwest::Response>
{
let rsp = self.token.set_header(req)?
.send()
.chain_err(|| ErrorKind::Communication)?;
let status = rsp.status();
if status.is_server_error() {
return Err(ErrorKind::Gitlab(format!(
"server error: {} {:?}",
status.as_u16(),
status.canonical_reason(),
))
.into());
}
let success = status.is_success();
if !success {
let v = serde_json::from_reader(rsp).chain_err(|| ErrorKind::Deserialize)?;
return Err(Error::from_gitlab(v));
}
Ok(rsp)
}
/// Create a `GET` request to an API endpoint.
fn get<T>(&self, url: &str) -> Result<T>
where
......@@ -1394,6 +1497,13 @@ impl Gitlab {
self.send(req)
}
fn get_raw(&self, url: &str) -> Result<reqwest::Response>
{
let full_url = self.create_url(url)?;
let req = self.client.get(full_url);
self.send_raw(req)
}
/// Create a `POST` request to an API endpoint with query parameters.
fn post_with_param<T, U>(&self, url: &str, param: U) -> Result<T>
where
......
......@@ -664,6 +664,115 @@ pub struct ProjectStatistics {
pub job_artifacts_size: u64,
}
#[cfg_attr(feature = "strict", serde(deny_unknown_fields))]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProjectsExportBasic {
/// Project export response message
pub message: String,
}
#[cfg_attr(feature = "strict", serde(deny_unknown_fields))]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProjectExportStatus {
/// A project export has not been triggered yet
#[serde(rename = "none")]
None,
/// Export process has been started
#[serde(rename = "started")]
Started,
/// Export process has been completed successfully and the
/// platform is performing some actions on the resulted file.
#[serde(rename = "after_export_action")]
AfterExportAction,
/// The project export is completed
#[serde(rename = "finished")]
Finished,
}
#[cfg_attr(feature = "strict", serde(deny_unknown_fields))]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProjectExportLinks {
pub api_url: String,
pub web_url: String,
}
#[cfg_attr(feature = "strict", serde(deny_unknown_fields))]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProjectExport {
/// The ID of the project.
pub id: ProjectId,
/// The description of the project.
pub description: Option<String>,
/// The display name of the project.
pub name: String,
/// The display name of the project with the namespace.
pub name_with_namespace: String,
/// The path to the project's repository.
pub path: String,
/// The path to the project's repository with its namespace.
pub path_with_namespace: String,
pub created_at: DateTime<Utc>,
/// Shows whether the project is currently being exported
pub export_status: ProjectExportStatus,
/// Links to related API URLs provided by GitLab after the project export.
pub _links: Option<ProjectExportLinks>,
}
#[cfg_attr(feature = "strict", serde(deny_unknown_fields))]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ImportProject<P : AsRef<std::path::Path>> {
/// The file to be uploaded.
pub file: P,
/// Name and path for new project.
pub path: String,
/// The ID or path of the namespace that the project will be imported to.
/// Defaults to the current user’s namespace
pub namespace: Option<Namespace>,
/// If there is a project with the same path the import will overwrite it.
pub overwrite: Option<bool>,
}
#[cfg_attr(feature = "strict", serde(deny_unknown_fields))]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProjectImportStatus {
/// A project import has not been triggered yet
#[serde(rename = "none")]
None,
/// Import process has been scheduled
#[serde(rename = "scheduled")]
Scheduled,
/// The project import process failed
#[serde(rename = "failed")]
Failed,
/// Import process has been started
#[serde(rename = "started")]
Started,
/// The project export is completed
#[serde(rename = "finished")]
Finished,
}
#[cfg_attr(feature = "strict", serde(deny_unknown_fields))]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ProjectImport {
/// The ID of the project.
pub id: ProjectId,
/// The description of the project.
pub description: Option<String>,
/// The display name of the project.
pub name: String,
/// The display name of the project with the namespace.
pub name_with_namespace: String,
/// The path to the project's repository.
pub path: String,
/// The path to the project's repository with its namespace.
pub path_with_namespace: String,
pub created_at: DateTime<Utc>,
/// Shows whether the project is currently being imported
pub import_status: ProjectImportStatus,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
/// Access levels for groups and projects.
pub enum AccessLevel {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment