Skip to main content

ephemeris_forge_github/api/http/
access.rs

1use ephemeris_forge::{RepoRef, UserName, encode, path_segment};
2use reqwest::Method;
3
4use crate::Permission;
5use crate::api::HttpApi;
6use crate::api::error::ForgeError;
7use crate::api::model::{
8    Collaborator, CollaboratorResponse, CurrentUserResponse, Invitation, InvitationResponse,
9    Membership, MembershipResponse, OwnerKind, OwnerResponse, Team, TeamResponse,
10};
11use crate::api::operations::AccessApi;
12use crate::api::request::{InvitationBody, PermissionBody};
13
14impl AccessApi for HttpApi {
15    async fn add_collaborator(
16        &self,
17        repository: &RepoRef,
18        user: &UserName,
19        permission: &Permission,
20    ) -> Result<(), ForgeError> {
21        self.empty(
22            Method::PUT,
23            &repository.path(&format!("collaborators/{user}")),
24            Some(encode(&PermissionBody {
25                permission: permission.as_str(),
26            })),
27        )
28        .await
29    }
30
31    async fn add_team(
32        &self,
33        repository: &RepoRef,
34        slug: &str,
35        permission: &Permission,
36    ) -> Result<(), ForgeError> {
37        self.empty(
38            Method::PUT,
39            &team_repository_path(repository, slug),
40            Some(encode(&PermissionBody {
41                permission: permission.as_str(),
42            })),
43        )
44        .await
45    }
46
47    async fn cancel_invitation(&self, repository: &RepoRef, id: u64) -> Result<(), ForgeError> {
48        self.empty(
49            Method::DELETE,
50            &repository.path(&format!("invitations/{id}")),
51            None,
52        )
53        .await
54    }
55
56    async fn collaborators(&self, repository: &RepoRef) -> Result<Vec<Collaborator>, ForgeError> {
57        let collaborators: Vec<CollaboratorResponse> = self
58            .paged(&repository.path("collaborators?affiliation=direct"))
59            .await?;
60        Ok(collaborators
61            .into_iter()
62            .filter_map(CollaboratorResponse::into_collaborator)
63            .collect())
64    }
65
66    async fn current_user(&self) -> Result<String, ForgeError> {
67        let response: CurrentUserResponse = self.send(Method::GET, "user", None).await?;
68        Ok(response.login)
69    }
70
71    async fn user_teams(&self) -> Result<Vec<Membership>, ForgeError> {
72        let teams: Vec<MembershipResponse> = self.paged("user/teams").await?;
73        Ok(teams.into_iter().map(Into::into).collect())
74    }
75
76    async fn invitations(&self, repository: &RepoRef) -> Result<Vec<Invitation>, ForgeError> {
77        let invitations: Vec<InvitationResponse> =
78            self.paged(&repository.path("invitations")).await?;
79        Ok(invitations
80            .into_iter()
81            .filter_map(InvitationResponse::into_invitation)
82            .collect())
83    }
84
85    async fn organization_teams(&self, organization: &str) -> Result<Vec<Team>, ForgeError> {
86        let teams: Vec<TeamResponse> = self.paged(&format!("orgs/{organization}/teams")).await?;
87        Ok(teams.into_iter().map(Into::into).collect())
88    }
89
90    async fn owner_kind(&self, owner: &str) -> Result<OwnerKind, ForgeError> {
91        let response: OwnerResponse = self
92            .send(Method::GET, &format!("users/{owner}"), None)
93            .await?;
94        Ok(response.kind)
95    }
96
97    async fn remove_collaborator(
98        &self,
99        repository: &RepoRef,
100        user: &UserName,
101    ) -> Result<(), ForgeError> {
102        self.empty(
103            Method::DELETE,
104            &repository.path(&format!("collaborators/{user}")),
105            None,
106        )
107        .await
108    }
109
110    async fn remove_team(&self, repository: &RepoRef, slug: &str) -> Result<(), ForgeError> {
111        self.empty(
112            Method::DELETE,
113            &team_repository_path(repository, slug),
114            None,
115        )
116        .await
117    }
118
119    async fn teams(&self, repository: &RepoRef) -> Result<Vec<Team>, ForgeError> {
120        let teams: Vec<TeamResponse> = self.paged(&repository.path("teams")).await?;
121        Ok(teams.into_iter().map(Into::into).collect())
122    }
123
124    async fn update_invitation(
125        &self,
126        repository: &RepoRef,
127        id: u64,
128        permission: &Permission,
129    ) -> Result<(), ForgeError> {
130        let name = permission
131            .invitation_name()
132            .ok_or(ForgeError::InvitationRoleUnsupported)?;
133        self.empty(
134            Method::PATCH,
135            &repository.path(&format!("invitations/{id}")),
136            Some(encode(&InvitationBody { permissions: name })),
137        )
138        .await
139    }
140}
141
142fn team_repository_path(repository: &RepoRef, slug: &str) -> String {
143    format!(
144        "orgs/{}/teams/{}/{}",
145        path_segment(repository.owner()),
146        path_segment(slug),
147        repository.path("")
148    )
149}