Skip to main content

ephemeris_forge_github/reconcile/
access.rs

1use ephemeris_forge::{Access, AccessOperation, Caller, RepoRef, TeamName};
2
3use super::Client;
4use crate::api::{Api, ForgeError};
5use crate::operation::access::Operation;
6use crate::{Live, Permission};
7
8impl<A: Api> Client<A> {
9    pub(super) async fn read_access(
10        &self,
11        repository: &RepoRef,
12        config: Option<&Access<Permission>>,
13        live: &mut Live,
14    ) -> Result<(), ForgeError> {
15        let Some(access) = config else {
16            return Ok(());
17        };
18        let owned = access.mode == ephemeris_forge::Mode::Owned;
19        if owned || !access.teams.is_empty() {
20            for team in self.api.teams(repository).await? {
21                let Ok(name) = TeamName::try_from(team.slug) else {
22                    continue;
23                };
24                if team.inherited {
25                    live.access.inherited_teams.insert(name.clone());
26                }
27                live.access.teams.insert(name, team.permission);
28            }
29        }
30        if !access.teams.is_empty() {
31            for team in self.api.organization_teams(repository.owner()).await? {
32                let Ok(name) = TeamName::try_from(team.slug) else {
33                    continue;
34                };
35                live.access.organization_teams.insert(name, team.permission);
36            }
37        }
38        if owned || !access.collaborators.is_empty() {
39            live.access.collaborators = self
40                .api
41                .collaborators(repository)
42                .await?
43                .into_iter()
44                .map(|collaborator| (collaborator.user, collaborator.permission))
45                .collect();
46            live.invitations = self
47                .api
48                .invitations(repository)
49                .await?
50                .into_iter()
51                .map(|invitation| (invitation.user.clone(), invitation))
52                .collect();
53        }
54        let custom = access
55            .teams
56            .values()
57            .chain(access.collaborators.values())
58            .any(|permission| permission.custom().is_some());
59        if custom {
60            live.owner = Some(self.api.owner_kind(repository.owner()).await?);
61        }
62        live.access.caller = Some(self.read_caller(repository.owner()).await?);
63        Ok(())
64    }
65
66    pub(super) async fn read_caller(&self, organization: &str) -> Result<Caller, ForgeError> {
67        if let Some(caller) = &self.caller {
68            return Ok(caller.clone());
69        }
70        let login = self.api.current_user().await?;
71        let teams = self
72            .api
73            .user_teams()
74            .await?
75            .into_iter()
76            .filter(|membership| membership.organization.eq_ignore_ascii_case(organization))
77            .filter_map(|membership| TeamName::try_from(membership.team).ok())
78            .collect();
79        Ok(Caller::user(login, teams)?)
80    }
81
82    pub(super) async fn apply_access(
83        &self,
84        repository: &RepoRef,
85        operation: &Operation,
86    ) -> Result<(), ForgeError> {
87        match operation {
88            Operation::Grant(AccessOperation::AddTeam { team, permission }) => {
89                self.api
90                    .add_team(repository, team.as_str(), permission)
91                    .await
92            }
93            Operation::Grant(AccessOperation::RemoveTeam { team }) => {
94                self.api.remove_team(repository, team.as_str()).await
95            }
96            Operation::Grant(AccessOperation::AddCollaborator { user, permission }) => {
97                self.api
98                    .add_collaborator(repository, user, permission)
99                    .await
100            }
101            Operation::Grant(AccessOperation::RemoveCollaborator { user }) => {
102                self.api.remove_collaborator(repository, user).await
103            }
104            Operation::SetInvitationPermission {
105                id,
106                user: _,
107                permission,
108            } => {
109                self.api
110                    .update_invitation(repository, *id, permission)
111                    .await
112            }
113            Operation::CancelInvitation { id, user: _ } => {
114                self.api.cancel_invitation(repository, *id).await
115            }
116        }
117    }
118}