fundaia 0.32.1

Command line for the Fundaia deployment platform: projects, services, variables, deployments, logs and metrics
//! Who is signed in, where from, and the spaces they share.

use super::*;

impl ApiClient {
    pub async fn whoami(&self) -> Result<Option<PrincipalIdentity>> {
        Ok(self.get::<Principal>("/api/auth/session").await?.principal)
    }

    pub async fn version(&self) -> Result<ServerVersion> {
        self.get("/api/version").await
    }

    pub async fn workspaces(&self) -> Result<Vec<Workspace>> {
        Ok(self
            .get::<WorkspacesPage>("/api/infra/workspaces")
            .await?
            .workspaces)
    }

    /// The same id-or-slug-or-name lookup projects get, for the same reason.
    ///
    /// With no reference it answers the first one, which is the workspace
    /// somebody created rather than one they were invited into — the same
    /// default the server applies when a request does not name one.
    pub async fn find_workspace(&self, reference: Option<&str>) -> Result<Workspace> {
        let workspaces = self.workspaces().await?;

        let Some(reference) = reference else {
            return workspaces
                .into_iter()
                .next()
                .ok_or_else(|| anyhow!("this account is in no workspace"));
        };

        one_named(workspaces, reference, "workspaces", || {
            format!("there is no workspace called `{reference}`")
        })
    }

    /// What has been getting into this token's own account, newest first.
    ///
    /// `/api/auth/**` rather than `/api/infra/**`, and that is not an
    /// inconsistency: the history belongs to the account, not to a project, and
    /// the bearer token resolves to the same principal on either prefix. There
    /// is no user id in the path because there is nowhere else to point it.
    pub async fn sign_ins(&self, limit: usize) -> Result<Vec<SignIn>> {
        Ok(self
            .get::<SignInPage>(&format!("/api/auth/sign-ins?limit={limit}"))
            .await?
            .sign_ins)
    }

    pub async fn auth_sessions(&self) -> Result<Vec<AuthSession>> {
        Ok(self
            .get::<AuthSessionPage>("/api/auth/sessions")
            .await?
            .sessions)
    }

    /// Signs one out. The id is the one `auth_sessions` reported.
    pub async fn revoke_session(&self, session_id: &str) -> Result<()> {
        self.delete(&format!("/api/auth/sessions/{session_id}"))
            .await
    }

    pub async fn members(&self, workspace_id: &str) -> Result<Vec<Member>> {
        Ok(self
            .get::<MembersPage>(&format!("/api/infra/workspaces/{workspace_id}/members"))
            .await?
            .members)
    }

    /// What is left of the prepaid credit the agents of a space run on.
    pub async fn credits(&self, workspace_id: &str) -> Result<Credits> {
        self.get::<Credits>(&format!("/api/infra/workspaces/{workspace_id}/credits"))
            .await
    }

    pub async fn invitations(&self, workspace_id: &str) -> Result<Vec<Invitation>> {
        Ok(self
            .get::<InvitationsPage>(&format!("/api/infra/workspaces/{workspace_id}/invitations"))
            .await?
            .invitations)
    }

    pub async fn invite(
        &self,
        workspace_id: &str,
        email: &str,
        role: &str,
    ) -> Result<SentInvitation> {
        self.post(
            &format!("/api/infra/workspaces/{workspace_id}/invitations"),
            &NewInvitation {
                email: email.to_string(),
                role: role.to_string(),
            },
        )
        .await
    }

    pub async fn revoke_invitation(&self, workspace_id: &str, invitation_id: &str) -> Result<()> {
        self.delete(&format!(
            "/api/infra/workspaces/{workspace_id}/invitations/{invitation_id}"
        ))
        .await
    }

    pub async fn remove_member(&self, workspace_id: &str, user_id: &str) -> Result<()> {
        self.delete(&format!(
            "/api/infra/workspaces/{workspace_id}/members/{user_id}"
        ))
        .await
    }
}