cloud_terrastodon_azure 0.35.1

Helpers for interacting with Azure for the Cloud Terrastodon project
use crate::ResourceGraphHelper;
use cloud_terrastodon_azure_types::AzureTenantId;
use cloud_terrastodon_azure_types::ResourceGroup;
use cloud_terrastodon_command::CacheKey;
use cloud_terrastodon_command::CacheableCommand;
use cloud_terrastodon_command::async_trait;
use eyre::Result;
use indoc::indoc;
use std::path::PathBuf;

#[must_use = "This is a future request, you must .await it"]
pub struct ResourceGroupListRequest {
    pub tenant_id: AzureTenantId,
}

pub fn fetch_all_resource_groups(tenant_id: AzureTenantId) -> ResourceGroupListRequest {
    ResourceGroupListRequest { tenant_id }
}

#[async_trait]
impl CacheableCommand for ResourceGroupListRequest {
    type Output = Vec<ResourceGroup>;

    fn cache_key(&self) -> CacheKey {
        CacheKey::new(PathBuf::from_iter([
            "az",
            "resource_graph",
            "resource_groups",
            self.tenant_id.to_string().as_str(),
        ]))
    }
    async fn run(self) -> Result<Self::Output> {
        ResourceGraphHelper::new(
            self.tenant_id,
            indoc! {r#"
                resourcecontainers
                | where type =~ "microsoft.resources/subscriptions/resourcegroups"
                | join kind=leftouter (
                    resourcecontainers
                    | where type =~ "Microsoft.Resources/subscriptions"
                    | project subscriptionId,subscription_name=name
                ) on $left.subscriptionId == $right.subscriptionId
                | project
                    id,
                    tenant_id=tenantId,
                    location,
                    managed_by=managedBy,
                    name,
                    properties,
                    tags,
                    subscription_name
            "#},
            Some(self.cache_key()),
        )
        .collect_all::<ResourceGroup>()
        .await
    }
}

cloud_terrastodon_command::impl_cacheable_into_future!(ResourceGroupListRequest);

#[cfg(test)]
mod tests {

    use super::*;
    use crate::get_test_tenant_id;
    use cloud_terrastodon_user_input::PickerTui;

    #[test_log::test(tokio::test)]
    async fn it_works() -> Result<()> {
        let tenant_id = get_test_tenant_id().await?;
        let result = fetch_all_resource_groups(tenant_id).await?;
        assert!(!result.is_empty());
        for rg in result {
            assert!(!rg.name.is_empty());
        }
        Ok(())
    }

    #[test_log::test(tokio::test)]
    #[ignore]
    async fn invalidation() -> Result<()> {
        let tenant_id = get_test_tenant_id().await?;
        fetch_all_resource_groups(tenant_id)
            .cache_key()
            .invalidate()
            .await?;
        Ok(())
    }

    #[test_log::test(tokio::test)]
    #[ignore]
    async fn pick() -> Result<()> {
        let chosen = PickerTui::new()
            .pick_many_reloadable(async |invalidate| {
                let tenant_id = get_test_tenant_id().await?;
                if invalidate {
                    fetch_all_resource_groups(tenant_id)
                        .cache_key()
                        .invalidate()
                        .await?;
                }
                fetch_all_resource_groups(tenant_id).await
            })
            .await?;
        assert!(!chosen.is_empty());
        Ok(())
    }
}