Skip to main content

ephemeris_forge_github/api/http/
security.rs

1use ephemeris_forge::{RepoRef, encode};
2use reqwest::Method;
3
4use crate::api::HttpApi;
5use crate::api::error::ForgeError;
6use crate::api::model::{CodeScanning, EnabledResponse};
7use crate::api::operations::SecurityApi;
8use crate::api::request::{AnalysisBody, AnalysisPatch, CodeScanningPatch};
9
10impl HttpApi {
11    pub(super) async fn put_switch(
12        &self,
13        repository: &RepoRef,
14        name: &str,
15        enabled: bool,
16    ) -> Result<(), ForgeError> {
17        let method = if enabled { Method::PUT } else { Method::DELETE };
18        self.empty(method, &switch_path(repository, name), None)
19            .await
20    }
21}
22
23impl SecurityApi for HttpApi {
24    async fn automated_security_fixes(
25        &self,
26        repository: &RepoRef,
27    ) -> Result<Option<bool>, ForgeError> {
28        let setting: Option<EnabledResponse> = self
29            .optional(&switch_path(repository, "automated-security-fixes"))
30            .await?;
31        Ok(Some(setting.is_some_and(|setting| setting.enabled)))
32    }
33
34    async fn code_scanning_default_setup(
35        &self,
36        repository: &RepoRef,
37    ) -> Result<Option<CodeScanning>, ForgeError> {
38        match self
39            .optional(&repository.path("code-scanning/default-setup"))
40            .await
41        {
42            // A private repository without Advanced Security (free plan) answers 403.
43            Err(error) if error.status() == Some(403) => Ok(None),
44            result => result,
45        }
46    }
47
48    async fn patch_code_scanning_default_setup(
49        &self,
50        repository: &RepoRef,
51        patch: &CodeScanningPatch,
52    ) -> Result<(), ForgeError> {
53        self.empty(
54            Method::PATCH,
55            &repository.path("code-scanning/default-setup"),
56            Some(encode(patch)),
57        )
58        .await
59    }
60
61    async fn patch_security_analysis(
62        &self,
63        repository: &RepoRef,
64        patch: &AnalysisPatch,
65    ) -> Result<(), ForgeError> {
66        self.empty(
67            Method::PATCH,
68            &repository.path(""),
69            Some(encode(&AnalysisBody {
70                security_and_analysis: patch.clone(),
71            })),
72        )
73        .await
74    }
75
76    async fn private_vulnerability_reporting(
77        &self,
78        repository: &RepoRef,
79    ) -> Result<Option<bool>, ForgeError> {
80        let setting: Option<EnabledResponse> = self
81            .optional(&switch_path(repository, "private-vulnerability-reporting"))
82            .await?;
83        Ok(setting.map(|setting| setting.enabled))
84    }
85
86    async fn set_automated_security_fixes(
87        &self,
88        repository: &RepoRef,
89        enabled: bool,
90    ) -> Result<(), ForgeError> {
91        self.put_switch(repository, "automated-security-fixes", enabled)
92            .await
93    }
94
95    async fn set_private_vulnerability_reporting(
96        &self,
97        repository: &RepoRef,
98        enabled: bool,
99    ) -> Result<(), ForgeError> {
100        self.put_switch(repository, "private-vulnerability-reporting", enabled)
101            .await
102    }
103
104    async fn set_vulnerability_alerts(
105        &self,
106        repository: &RepoRef,
107        enabled: bool,
108    ) -> Result<(), ForgeError> {
109        self.put_switch(repository, "vulnerability-alerts", enabled)
110            .await
111    }
112
113    async fn vulnerability_alerts(&self, repository: &RepoRef) -> Result<Option<bool>, ForgeError> {
114        match self
115            .empty(
116                Method::GET,
117                &switch_path(repository, "vulnerability-alerts"),
118                None,
119            )
120            .await
121        {
122            Ok(()) => Ok(Some(true)),
123            Err(error) if error.status() == Some(404) => Ok(Some(false)),
124            Err(error) => Err(error),
125        }
126    }
127}
128
129fn switch_path(repository: &RepoRef, name: &str) -> String {
130    repository.path(name)
131}