use super::{ListOptions, Tag};
use crate::{Error, Result};
mod queries {
pub use self::list_tags::{GetProjectRootRepository, get_project_root_repository};
pub use self::list_tags::{ListContainerTags, list_container_tags};
mod list_tags;
}
pub async fn fetch(source: &url::Url, options: &ListOptions<'_>) -> Result<Vec<Tag>> {
use graphql_client::GraphQLQuery;
use queries::{GetProjectRootRepository, ListContainerTags};
let host = source.host_str().ok_or_else(|| {
Error::invalid_source(source.clone(), "expected docker+gitlab://<HOST>/<PATH>")
})?;
let path = source.path().trim_start_matches('/');
let client = crate::util::http::client()?;
let operation =
GetProjectRootRepository::build_query(queries::get_project_root_repository::Variables {
path: path.to_string(),
});
let res = client
.post(format!("https://{host}/api/graphql"))
.json(&operation)
.send()
.await
.map_err(Error::ReqwestError)?;
let response_body: graphql_client::Response<
queries::get_project_root_repository::ResponseData,
> = serde_json::from_str(&crate::util::http::body(res).await?)?;
let data = response_body
.data
.ok_or_else(|| Error::invalid_source(source.clone(), "GitLab GraphQL returned no data"))?;
let project = data
.project
.ok_or_else(|| Error::invalid_source(source.clone(), "GitLab project does not exist"))?;
let id = project
.container_repositories
.nodes
.first()
.ok_or_else(|| {
Error::invalid_source(
source.clone(),
"No container repositories found for project",
)
})?
.id
.as_str();
let mut cursor = None;
let mut tags = Vec::new();
let mut page_number = 0usize;
'tags: loop {
page_number += 1;
if page_number > super::super::MAX_PAGES {
return Err(super::super::too_many_pages("GitLab"));
}
let operation = ListContainerTags::build_query(queries::list_container_tags::Variables {
container_id: id.to_string(),
after: cursor.clone(),
});
let res = client
.post(format!("https://{host}/api/graphql"))
.json(&operation)
.send()
.await
.map_err(Error::ReqwestError)?;
let response_body: graphql_client::Response<queries::list_container_tags::ResponseData> =
serde_json::from_str(&crate::util::http::body(res).await?)?;
let data = response_body.data.ok_or_else(|| {
Error::invalid_source(source.clone(), "GitLab GraphQL returned no data for tags")
})?;
let repository_tags = data.container_repository.tags;
cursor = repository_tags.page_info.end_cursor;
if cursor.is_none() {
break;
}
for node in repository_tags.nodes {
use crate::util::iter::FilterWhileAction;
match options.consider(&node.name, &node.created_at) {
FilterWhileAction::Pick => tags.push(Tag::new(node.name, node.created_at)),
FilterWhileAction::Skip => {}
FilterWhileAction::Break => break 'tags,
}
}
}
Ok(tags)
}