Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added handlers for Repo Dependabot API #714

Merged
merged 4 commits into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions examples/get_dependabot_alerts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use http::header::ACCEPT;
use octocrab::models::repos::dependabot::UpdateDependabotAlert;
use octocrab::Octocrab;

const OWNER: &str = "org";
const REPO: &str = "some-repo";

#[tokio::main]
async fn main() {
// example for Dependabot alerts API with OAuth GitHub App
let client_id = secrecy::Secret::from(std::env::var("GITHUB_CLIENT_ID").unwrap());
let crab = octocrab::Octocrab::builder()
.base_uri("https://github.com")
.unwrap()
.add_header(ACCEPT, "application/json".to_string())
.build()
.unwrap();

let codes = crab
.authenticate_as_device(&client_id, ["security_events"])
.await
.unwrap();
println!(
"Go to {} and enter code {}",
codes.verification_uri, codes.user_code
);
let auth = codes.poll_until_available(&crab, &client_id).await.unwrap();
println!(
"Auth: scope {:?}; token type {}",
auth.scope, auth.token_type
);
let octocrab = Octocrab::builder()
.oauth(auth)
.add_header(ACCEPT, "application/vnd.github+json".to_string())
.build()
.unwrap();
// Get all Dependabot alerts for a repo
let a = octocrab
.repos(OWNER, REPO)
.dependabot()
.direction("asc")
.get_alerts()
.await
.unwrap();
println!("{:?}", a);
// Get a single Dependabot alert
let single_alert = octocrab
.repos(OWNER, REPO)
.dependabot()
.get_alert(5)
.await
.unwrap();
println!("{:?}", single_alert);
// Update (dismiss) a Dependabot alert
let updated_alert = octocrab
.repos(OWNER, REPO)
.dependabot()
.update_alert(
5,
Some(&UpdateDependabotAlert {
state: "dismissed",
dismissed_reason: Some("no_bandwidth"),
dismissed_comment: Some("I don't have time to fix this right now"),
}),
)
.await
.unwrap();
println!("{:?}", updated_alert);
}
7 changes: 7 additions & 0 deletions src/api/repos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod branches;
mod collaborators;
mod commits;
mod contributors;
mod dependabot;
pub mod events;
mod file;
pub mod forks;
Expand All @@ -34,6 +35,7 @@ pub use branches::ListBranchesBuilder;
pub use collaborators::ListCollaboratorsBuilder;
pub use commits::ListCommitsBuilder;
pub use contributors::ListContributorsBuilder;
pub use dependabot::RepoDependabotAlertsHandler;
pub use file::{DeleteFileBuilder, GetContentBuilder, UpdateFileBuilder};
pub use generate::GenerateRepositoryBuilder;
pub use merges::MergeBranchBuilder;
Expand Down Expand Up @@ -748,6 +750,11 @@ impl<'octo> RepoHandler<'octo> {
RepoSecretsHandler::new(self)
}

/// Handle dependabot alerts on the repository
pub fn dependabot(&self) -> RepoDependabotAlertsHandler<'_> {
RepoDependabotAlertsHandler::new(self)
}

/// Creates a new Git commit object.
/// See https://docs.github.com/en/rest/git/commits?apiVersion=2022-11-28#create-a-commit
/// ```no_run
Expand Down
180 changes: 180 additions & 0 deletions src/api/repos/dependabot.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
use super::RepoHandler;

/// A client to GitHub's repository dependabot API.
///
/// Created with [`Octocrab::repos`].
pub struct RepoDependabotAlertsHandler<'octo> {
handler: &'octo RepoHandler<'octo>,
params: Params,
}

#[derive(serde::Serialize)]
struct Params {
#[serde(skip_serializing_if = "Option::is_none")]
per_page: Option<u8>,
#[serde(skip_serializing_if = "Option::is_none")]
page: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
state: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
severity: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
ecosystem: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
package: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
manifest: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
scope: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
sort: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
direction: Option<String>,
}

impl<'octo> RepoDependabotAlertsHandler<'octo> {
pub(crate) fn new(repo: &'octo RepoHandler<'octo>) -> Self {
Self {
handler: repo,
params: Params {
per_page: None,
page: None,
state: None,
severity: None,
ecosystem: None,
package: None,
manifest: None,
scope: None,
sort: None,
direction: None,
},
}
}

/// Lists all Dependabot Alerts available in a repository.
/// You must authenticate using an access token with the `repo` or `security_events` scope to use this endpoint.
/// ```no_run
/// # async fn run() -> octocrab::Result<()> {
/// # let octocrab = octocrab::Octocrab::default();
/// let all_secrets = octocrab.repos("owner", "repo")
/// .dependabot()
/// .direction("asc")
/// .get_alerts()
/// .await?;
/// # Ok(())
/// # }
pub async fn get_alerts(
&self,
) -> crate::Result<crate::Page<crate::models::repos::dependabot::DependabotAlert>> {
let route = format!("/{}/dependabot/alerts", self.handler.repo);
self.handler.crab.get(route, Some(&self.params)).await
}

/// Results per page (max 100).
pub fn per_page(mut self, per_page: impl Into<u8>) -> Self {
self.params.per_page = Some(per_page.into());
self
}

/// Page number of the results to fetch.
pub fn page(mut self, page: impl Into<u32>) -> Self {
self.params.page = Some(page.into());
self
}

/// Filter Dependabot Alerts by state.
pub fn state(mut self, state: impl Into<Vec<String>>) -> Self {
self.params.state = Some(state.into());
self
}

/// Filter Dependabot Alerts by severity.
pub fn severity(mut self, severity: impl Into<Vec<String>>) -> Self {
self.params.severity = Some(severity.into());
self
}

/// Filter Dependabot Alerts by ecosystem.
pub fn ecosystem(mut self, ecosystem: impl Into<Vec<String>>) -> Self {
self.params.ecosystem = Some(ecosystem.into());
self
}

/// Filter Dependabot Alerts by package.
pub fn package(mut self, package: impl Into<Vec<String>>) -> Self {
self.params.package = Some(package.into());
self
}

/// Filter Dependabot Alerts by manifest.
pub fn manifest(mut self, manifest: impl Into<Vec<String>>) -> Self {
self.params.manifest = Some(manifest.into());
self
}

/// Filter Dependabot Alerts by scope.
pub fn scope(mut self, scope: impl Into<String>) -> Self {
self.params.scope = Some(scope.into());
self
}

/// Sort Dependabot Alerts.
pub fn sort(mut self, sort: impl Into<String>) -> Self {
self.params.sort = Some(sort.into());
self
}

/// Sort direction of Dependabot Alerts.
pub fn direction(mut self, direction: impl Into<String>) -> Self {
self.params.direction = Some(direction.into());
self
}

/// Lists single Dependabot Alert for a repository.
/// You must authenticate using an access token with the `repo` or `security_events` scope to use this endpoint.
/// ```no_run
/// # async fn run() -> octocrab::Result<()> {
/// # let octocrab = octocrab::Octocrab::default();
/// let all_secrets = octocrab.repos("owner", "repo")
/// .dependabot()
/// .get_alert(5)
/// .await?;
/// # Ok(())
/// # }
pub async fn get_alert(
&self,
alert_number: u32,
) -> crate::Result<crate::models::repos::dependabot::DependabotAlert> {
let route = format!("/{}/dependabot/alerts/{}", self.handler.repo, alert_number);
self.handler.crab.get(route, None::<&()>).await
}

/// Updates a dependabot alert.
/// You must authenticate using an access token with the `security_events ` scope to use this endpoint.
/// ```no_run
/// # async fn run() -> octocrab::Result<()> {
/// # let octocrab = octocrab::Octocrab::default();
/// use octocrab::models::repos::dependabot::UpdateDependabotAlert;
///
/// let result = octocrab.repos("owner", "repo")
/// .dependabot()
/// .update_alert(
/// 5,
/// Some(&UpdateDependabotAlert {
/// state: "dismissed",
/// dismissed_reason: Some("no_bandwidth"),
/// dismissed_comment: Some("I don't have time to fix this right now"),
/// })
/// )
/// .await?;
/// # Ok(())
/// # }
pub async fn update_alert(
&self,
alert_number: u32,
alert_update: Option<&crate::models::repos::dependabot::UpdateDependabotAlert<'_>>,
) -> crate::Result<crate::models::repos::dependabot::DependabotAlert> {
let route = format!("/{}/dependabot/alerts/{}", self.handler.repo, alert_number);
self.handler.crab.patch(route, alert_update).await
}
}
1 change: 1 addition & 0 deletions src/models/repos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use hyper::Response;
use snafu::ResultExt;
use url::Url;

pub mod dependabot;
pub mod secrets;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
Expand Down
Loading
Loading