summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerry Agbobada <10496163+gagbo@users.noreply.github.com>2023-07-19 14:43:01 +0200
committerGitHub <noreply@github.com>2023-07-19 14:43:01 +0200
commit01e8024a228ed98daac41bfd93715c3914d09ad2 (patch)
tree78790998f38f6bdc52e04720c843db6963c13dfa
parentcb585d8ee3cf6c023dbe1469db85268b577da075 (diff)
Add installation related webhook app events (#420)v0.28.0
Breaking: the installation stored in WrappedEventPayload now contains 2 variants, depending on whether the event comes from an installation_ related webhook event, or something else.
-rw-r--r--Cargo.toml2
-rw-r--r--src/models/events.rs17
-rw-r--r--src/models/events/payload.rs102
-rw-r--r--src/models/events/payload/installation.rs39
-rw-r--r--src/models/events/payload/installation_repositories.rs45
-rw-r--r--src/models/events/payload/installation_target.rs36
-rw-r--r--tests/resources/installation_event.json100
7 files changed, 332 insertions, 9 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 4c78bea..663f5c2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "octocrab"
-version = "0.27.0"
+version = "0.28.0"
authors = ["XAMPPRocky <xampprocky@gmail.com>"]
edition = "2018"
readme = "README.md"
diff --git a/src/models/events.rs b/src/models/events.rs
index c60f8fe..79b26a4 100644
--- a/src/models/events.rs
+++ b/src/models/events.rs
@@ -1,6 +1,6 @@
pub mod payload;
-use crate::models::events::payload::EventInstallationPayload;
+use crate::models::events::payload::EventInstallation;
use self::payload::{
CommitCommentEventPayload, CreateEventPayload, DeleteEventPayload, EventPayload,
@@ -15,6 +15,10 @@ use serde::{de::Error, Deserialize, Serialize};
use url::Url;
/// A GitHub event.
+///
+/// If you want to deserialize a webhook payload received in a Github Application, you
+/// must directly deserialize the body into a [`WrappedEventPayload`](WrappedEventPayload).
+/// For webhooks, the event type is stored in the `X-GitHub-Event` header.
#[derive(Debug, Clone, PartialEq, Serialize)]
#[non_exhaustive]
pub struct Event {
@@ -139,7 +143,7 @@ impl<'de> Deserialize<'de> for Event {
}
#[derive(Deserialize)]
struct IntermediatePayload {
- installation: Option<EventInstallationPayload>,
+ installation: Option<EventInstallation>,
organization: Option<crate::models::orgs::Organization>,
repository: Option<crate::models::Repository>,
sender: Option<crate::models::Author>,
@@ -230,8 +234,13 @@ mod test {
let event: Event = serde_json::from_str(json).unwrap();
assert_eq!(event.r#type, EventType::WorkflowRunEvent);
assert_eq!(
- event.payload.unwrap().installation.unwrap().id,
- crate::models::InstallationId(18995746)
+ event.payload.unwrap().installation.unwrap(),
+ crate::models::events::payload::EventInstallation::Minimal(Box::new(
+ crate::models::events::payload::EventInstallationId {
+ id: 18995746.into(),
+ node_id: "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uMTg5OTU3NDY=".to_string()
+ }
+ ))
)
}
diff --git a/src/models/events/payload.rs b/src/models/events/payload.rs
index ddd8180..58f2d8a 100644
--- a/src/models/events/payload.rs
+++ b/src/models/events/payload.rs
@@ -3,6 +3,9 @@ mod create;
mod delete;
mod fork;
mod gollum;
+mod installation;
+mod installation_repositories;
+mod installation_target;
mod issue_comment;
mod issues;
mod member;
@@ -12,12 +15,14 @@ mod pull_request_review_comment;
mod push;
mod workflow_run;
-use crate::models::{repos::CommitAuthor, InstallationId};
pub use commit_comment::*;
pub use create::*;
pub use delete::*;
pub use fork::*;
pub use gollum::*;
+pub use installation::*;
+pub use installation_repositories::*;
+pub use installation_target::*;
pub use issue_comment::*;
pub use issues::*;
pub use member::*;
@@ -30,16 +35,29 @@ pub use workflow_run::*;
use serde::{Deserialize, Serialize};
use url::Url;
-use crate::models::{orgs::Organization, Author, Repository};
+use crate::models::{
+ orgs::Organization, repos::CommitAuthor, Author, Installation, InstallationId, Repository,
+ RepositoryId,
+};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
-pub struct EventInstallationPayload {
+#[serde(untagged)]
+pub enum EventInstallation {
+ /// A full installation object which is present for `Installation*` related webhook events.
+ Full(Box<Installation>),
+ /// The minimal installation object is present for all other event types.
+ Minimal(Box<EventInstallationId>),
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+pub struct EventInstallationId {
pub id: InstallationId,
+ pub node_id: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WrappedEventPayload {
- pub installation: Option<EventInstallationPayload>,
+ pub installation: Option<EventInstallation>,
pub organization: Option<Organization>,
pub repository: Option<Repository>,
pub sender: Option<Author>,
@@ -59,6 +77,9 @@ pub enum EventPayload {
PushEvent(Box<PushEventPayload>),
CreateEvent(Box<CreateEventPayload>),
DeleteEvent(Box<DeleteEventPayload>),
+ InstallationEvent(Box<InstallationEventPayload>),
+ InstallationRepositoriesEvent(Box<InstallationRepositoriesEventPayload>),
+ InstallationTargetEvent(Box<InstallationTargetEventPayload>),
IssuesEvent(Box<IssuesEventPayload>),
IssueCommentEvent(Box<IssueCommentEventPayload>),
CommitCommentEvent(Box<CommitCommentEventPayload>),
@@ -82,3 +103,76 @@ pub struct Commit {
pub distinct: bool,
pub url: Url,
}
+
+/// A repository in installation related webhook events.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[non_exhaustive]
+pub struct InstallationEventRepository {
+ pub id: RepositoryId,
+ pub node_id: String,
+ pub name: String,
+ pub full_name: String,
+ pub private: bool,
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn should_deserialize_installation_event() {
+ // The payload has been extracted as the `payload` key from a webhook installation event.
+ let json = include_str!("../../../tests/resources/installation_event.json");
+ let event: WrappedEventPayload = serde_json::from_str(json).unwrap();
+
+ let installation = event.installation.unwrap();
+ let specific = event.specific.unwrap();
+
+ match installation {
+ EventInstallation::Full(install) => {
+ assert_eq!(install.id, 7777777.into());
+ assert_eq!(install.repository_selection.unwrap(), "all");
+ }
+ EventInstallation::Minimal(_) => {
+ panic!("expected a Full installation payload for the event.")
+ }
+ };
+
+ match specific {
+ EventPayload::InstallationEvent(install) => {
+ let repos = install.repositories;
+ assert_eq!(repos.len(), 3);
+ assert!(
+ repos.iter().any(|repo| repo.name == "ViscoElRebound"),
+ "ViscoElRebound should be in the list of repositories"
+ );
+ assert!(
+ repos.iter().any(|repo| repo.name == "OSSU"),
+ "OSSU should be in the list of repositories"
+ );
+ assert!(
+ repos.iter().any(|repo| repo.name == "octocrab"),
+ "octocrab should be in the list of repositories"
+ );
+ }
+ EventPayload::PushEvent(_)
+ | EventPayload::CreateEvent(_)
+ | EventPayload::DeleteEvent(_)
+ | EventPayload::InstallationRepositoriesEvent(_)
+ | EventPayload::InstallationTargetEvent(_)
+ | EventPayload::IssuesEvent(_)
+ | EventPayload::IssueCommentEvent(_)
+ | EventPayload::CommitCommentEvent(_)
+ | EventPayload::ForkEvent(_)
+ | EventPayload::GollumEvent(_)
+ | EventPayload::MemberEvent(_)
+ | EventPayload::PullRequestEvent(_)
+ | EventPayload::PullRequestReviewEvent(_)
+ | EventPayload::PullRequestReviewCommentEvent(_)
+ | EventPayload::WorkflowRunEvent(_)
+ | EventPayload::UnknownEvent(_) => {
+ panic!("Expected an installation event, got {:?}", specific)
+ }
+ }
+ }
+}
diff --git a/src/models/events/payload/installation.rs b/src/models/events/payload/installation.rs
new file mode 100644
index 0000000..b665b83
--- /dev/null
+++ b/src/models/events/payload/installation.rs
@@ -0,0 +1,39 @@
+//! This event occurs when there is activity relating to a GitHub App
+//! installation. All GitHub Apps receive this event by default. You cannot
+//! manually subscribe to this event.
+
+use serde::{Deserialize, Serialize};
+
+use super::InstallationEventRepository;
+use crate::models::Author;
+
+/// The payload in a webhook installation event type.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[non_exhaustive]
+pub struct InstallationEventPayload {
+ /// The action this event represents.
+ pub action: InstallationEventAction,
+ /// An enterprise on GitHub
+ pub enterprise: Option<serde_json::Value>,
+ /// An array of repositories that the installation can access
+ pub repositories: Vec<InstallationEventRepository>,
+ /// The initiator of the request, mainly for the [`created`](InstallationAction::Created) action
+ pub requester: Option<Author>,
+}
+
+/// The action on an installation this event corresponds to.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum InstallationEventAction {
+ /// Someone installed a GitHub App on a user or organization account.
+ Created,
+ /// Someone uninstalled a GitHub App on a user or organization account.
+ Deleted,
+ /// Someone granted new permissions to a GitHub App.
+ NewPermissionsAccepted,
+ /// Someone blocked access by a GitHub App to their user or organization account.
+ Suspend,
+ /// A GitHub App that was blocked from accessing a user or organization account was given access the account again.
+ Unsuspend,
+}
diff --git a/src/models/events/payload/installation_repositories.rs b/src/models/events/payload/installation_repositories.rs
new file mode 100644
index 0000000..a9ffce3
--- /dev/null
+++ b/src/models/events/payload/installation_repositories.rs
@@ -0,0 +1,45 @@
+//! This event occurs when there is activity relating to which repositories a
+//! GitHub App installation can access. All GitHub Apps receive this event by
+//! default. You cannot manually subscribe to this event.
+
+use serde::{Deserialize, Serialize};
+
+use super::InstallationEventRepository;
+use crate::models::Author;
+
+/// The payload in a webhook installation_repositories event type.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[non_exhaustive]
+pub struct InstallationRepositoriesEventPayload {
+ /// The action this event represents.
+ pub action: InstallationRepositoriesEventAction,
+ /// An enterprise on GitHub
+ pub enterprise: Option<serde_json::Value>,
+ /// An array of repositories, which were added to the installation
+ pub repositories_added: Vec<InstallationEventRepository>,
+ /// An array of repositories, which were removed from the installation
+ pub repositories_removed: Vec<InstallationEventRepository>,
+ /// Describe whether all repositories have been selected or there's a selection involved
+ pub repository_selection: InstallationRepositoriesEventSelection,
+ /// The initiator of the request, mainly for the [`created`](InstallationAction::Created) action
+ pub requester: Option<Author>,
+}
+
+/// The action on an installation this event corresponds to.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum InstallationRepositoriesEventAction {
+ /// A GitHub App installation was granted access to one or more repositories.
+ Added,
+ /// Access to one or more repositories was revoked for a GitHub App installation.
+ Removed,
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+#[non_exhaustive]
+pub enum InstallationRepositoriesEventSelection {
+ All,
+ Selected,
+}
diff --git a/src/models/events/payload/installation_target.rs b/src/models/events/payload/installation_target.rs
new file mode 100644
index 0000000..3216d74
--- /dev/null
+++ b/src/models/events/payload/installation_target.rs
@@ -0,0 +1,36 @@
+//! This event occurs when there is activity relating to the user or
+//! organization account that a GitHub App is installed on.
+
+use serde::{Deserialize, Serialize};
+
+use crate::models::orgs::Organization;
+
+/// The payload in a webhook installation_target event type.
+///
+/// Somebody renamed the user or organization account that a GitHub App is installed on.
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[non_exhaustive]
+pub struct InstallationTargetEventPayload {
+ pub account: Organization,
+ pub changes: InstallationTargetChanges,
+ pub target_type: String,
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[non_exhaustive]
+pub struct InstallationTargetChanges {
+ pub login: InstallationTargetLoginChanges,
+ pub slug: InstallationTargetSlugChanges,
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[non_exhaustive]
+pub struct InstallationTargetLoginChanges {
+ pub from: String,
+}
+
+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
+#[non_exhaustive]
+pub struct InstallationTargetSlugChanges {
+ pub from: String,
+}
diff --git a/tests/resources/installation_event.json b/tests/resources/installation_event.json
new file mode 100644
index 0000000..0032073
--- /dev/null
+++ b/tests/resources/installation_event.json
@@ -0,0 +1,100 @@
+{
+ "action": "created",
+ "installation": {
+ "id": 7777777,
+ "account": {
+ "login": "gagbo",
+ "id": 88888,
+ "node_id": "PLACEHOLDER_NODE_ID",
+ "avatar_url": "https://avatars.githubusercontent.com/u/88888?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/gagbo",
+ "html_url": "https://github.com/gagbo",
+ "followers_url": "https://api.github.com/users/gagbo/followers",
+ "following_url": "https://api.github.com/users/gagbo/following{/other_user}",
+ "gists_url": "https://api.github.com/users/gagbo/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/gagbo/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/gagbo/subscriptions",
+ "organizations_url": "https://api.github.com/users/gagbo/orgs",
+ "repos_url": "https://api.github.com/users/gagbo/repos",
+ "events_url": "https://api.github.com/users/gagbo/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/gagbo/received_events",
+ "type": "User",
+ "site_admin": false
+ },
+ "repository_selection": "all",
+ "access_tokens_url": "https://api.github.com/app/installations/39593520/access_tokens",
+ "repositories_url": "https://api.github.com/installation/repositories",
+ "html_url": "https://github.com/settings/installations/39593520",
+ "app_id": 360617,
+ "app_slug": "gagbo-test-app",
+ "target_id": 88888,
+ "target_type": "User",
+ "permissions": {
+ "issues": "write",
+ "actions": "write",
+ "metadata": "read",
+ "pull_requests": "write"
+ },
+ "events": [
+ "issues",
+ "issue_comment",
+ "pull_request",
+ "pull_request_review",
+ "pull_request_review_comment",
+ "pull_request_review_thread",
+ "repository"
+ ],
+ "created_at": "2023-07-13T11:35:31.000+02:00",
+ "updated_at": "2023-07-13T11:35:32.000+02:00",
+ "single_file_name": null,
+ "has_multiple_single_files": false,
+ "single_file_paths": [],
+ "suspended_by": null,
+ "suspended_at": null
+ },
+ "repositories": [
+ {
+ "id": 29128586,
+ "node_id": "MDEwOlJlcG9zaXRvcnkyOTEyODU4Ng==",
+ "name": "ViscoElRebound",
+ "full_name": "gagbo/ViscoElRebound",
+ "private": false
+ },
+ {
+ "id": 109778911,
+ "node_id": "MDEwOlJlcG9zaXRvcnkxMDk3Nzg5MTE=",
+ "name": "OSSU",
+ "full_name": "gagbo/OSSU",
+ "private": true
+ },
+ {
+ "id": 665086759,
+ "node_id": "R_kgDOJ6RrJw",
+ "name": "octocrab",
+ "full_name": "gagbo/octocrab",
+ "private": false
+ }
+ ],
+ "requester": null,
+ "sender": {
+ "login": "gagbo",
+ "id": 88888,
+ "node_id": "PLACEHOLDER_NODE_ID",
+ "avatar_url": "https://avatars.githubusercontent.com/u/88888?v=4",
+ "gravatar_id": "",
+ "url": "https://api.github.com/users/gagbo",
+ "html_url": "https://github.com/gagbo",
+ "followers_url": "https://api.github.com/users/gagbo/followers",
+ "following_url": "https://api.github.com/users/gagbo/following{/other_user}",
+ "gists_url": "https://api.github.com/users/gagbo/gists{/gist_id}",
+ "starred_url": "https://api.github.com/users/gagbo/starred{/owner}{/repo}",
+ "subscriptions_url": "https://api.github.com/users/gagbo/subscriptions",
+ "organizations_url": "https://api.github.com/users/gagbo/orgs",
+ "repos_url": "https://api.github.com/users/gagbo/repos",
+ "events_url": "https://api.github.com/users/gagbo/events{/privacy}",
+ "received_events_url": "https://api.github.com/users/gagbo/received_events",
+ "type": "User",
+ "site_admin": false
+ }
+}