adnv 1.5.0

CLI that tracks, checks and auto-updates infrastructure dependency versions across Terraform, Helm, Docker, Git and npm
Documentation
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()?;

    // Manual implementation to avoid reqwest version conflicts
    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"));
        }

        // Manual implementation to avoid reqwest version conflicts
        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)
}