ephemeris_forge_github/api/http/
access.rs1use 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}