From e092d9476d11998683a4eac4a3eb60939d4446e3 Mon Sep 17 00:00:00 2001 From: Gerry Agbobada <10496163+gagbo@users.noreply.github.com> Date: Wed, 12 Jul 2023 12:45:31 +0200 Subject: [PATCH] Add installation_id in Webhook events (#408) * add pretty_assertions for tests * Add support for installation_id in webhook payloads This is a breaking change in the models::events::Event structure to have good access to the supposedly common fields in all received events whichever the payload is. Fixes #407 * Extract org, repo, sender webhook common fields All fields are options because they might not be set for events that are not webhook events (or might be webhook events triggered for a unique repository and not the complete organization for example) --- Cargo.toml | 3 +- src/models/events.rs | 37 ++++++++++++++++--- src/models/events/payload.rs | 19 +++++++++- src/models/events/payload/commit_comment.rs | 4 +- src/models/events/payload/create.rs | 4 +- src/models/events/payload/delete.rs | 4 +- src/models/events/payload/fork.rs | 3 +- src/models/events/payload/gollum.rs | 4 +- src/models/events/payload/issue_comment.rs | 4 +- src/models/events/payload/issues.rs | 4 +- src/models/events/payload/member.rs | 4 +- src/models/events/payload/pull_request.rs | 4 +- .../events/payload/pull_request_review.rs | 4 +- .../payload/pull_request_review_comment.rs | 4 +- src/models/events/payload/push.rs | 2 +- src/models/events/payload/workflow_run.rs | 13 ++++--- 16 files changed, 91 insertions(+), 26 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b21ba325..ba434806 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "octocrab" -version = "0.25.1" +version = "0.26.0" authors = ["XAMPPRocky "] edition = "2018" readme = "README.md" @@ -62,6 +62,7 @@ tokio-test = "0.4.2" wiremock = "0.5.3" crypto_box = { version = "0.8.2", features = ["seal"] } base64 = "0.21.2" +pretty_assertions = "1.4.0" [features] default = ["rustls", "timeout", "tracing", "retry"] diff --git a/src/models/events.rs b/src/models/events.rs index 84667af9..c60f8fe3 100644 --- a/src/models/events.rs +++ b/src/models/events.rs @@ -1,10 +1,12 @@ pub mod payload; +use crate::models::events::payload::EventInstallationPayload; + use self::payload::{ CommitCommentEventPayload, CreateEventPayload, DeleteEventPayload, EventPayload, ForkEventPayload, GollumEventPayload, IssueCommentEventPayload, IssuesEventPayload, PullRequestEventPayload, PullRequestReviewCommentEventPayload, PullRequestReviewEventPayload, - PushEventPayload, WorkflowRunEventPayload, + PushEventPayload, WorkflowRunEventPayload, WrappedEventPayload, }; use super::{ActorId, OrgId, RepositoryId}; use chrono::{DateTime, Utc}; @@ -22,7 +24,7 @@ pub struct Event { pub repo: Repository, pub public: bool, pub created_at: DateTime, - pub payload: Option, + pub payload: Option, pub org: Option, } @@ -133,12 +135,32 @@ impl<'de> Deserialize<'de> for Event { public: bool, created_at: DateTime, org: Option, - payload: Option, + payload: Option, + } + #[derive(Deserialize)] + struct IntermediatePayload { + installation: Option, + organization: Option, + repository: Option, + sender: Option, + #[serde(flatten)] + specific: Option, } let intermediate = Intermediate::deserialize(deserializer)?; let event_type = deserialize_event_type(intermediate.typ.as_ref()); let payload = intermediate.payload.map_or(Ok(None), |data| { - deserialize_payload(&event_type, data).map_err(|e| Error::custom(e.to_string())) + let specific = deserialize_payload( + &event_type, + data.specific.unwrap_or(serde_json::Value::Null), + ) + .map_err(|e| Error::custom(e.to_string()))?; + Ok(Some(WrappedEventPayload { + installation: data.installation, + organization: data.organization, + repository: data.repository, + sender: data.sender, + specific, + })) })?; let event = Event { id: intermediate.id, @@ -157,6 +179,7 @@ impl<'de> Deserialize<'de> for Event { #[cfg(test)] mod test { use super::{Event, EventPayload, EventType}; + use pretty_assertions::assert_eq; use url::Url; #[test] @@ -206,6 +229,10 @@ mod test { let json = include_str!("../../tests/resources/workflow_run_event.json"); 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) + ) } #[test] @@ -268,7 +295,7 @@ mod test { let event: Event = serde_json::from_str(json).unwrap(); assert!(event.payload.is_some()); let payload = event.payload.unwrap(); - match payload { + match payload.specific.unwrap() { EventPayload::UnknownEvent(json) => { assert!(json.is_object()); let map = json.as_object().unwrap(); diff --git a/src/models/events/payload.rs b/src/models/events/payload.rs index 8ffb520f..ddd81806 100644 --- a/src/models/events/payload.rs +++ b/src/models/events/payload.rs @@ -12,7 +12,7 @@ mod pull_request_review_comment; mod push; mod workflow_run; -use crate::models::repos::CommitAuthor; +use crate::models::{repos::CommitAuthor, InstallationId}; pub use commit_comment::*; pub use create::*; pub use delete::*; @@ -30,6 +30,23 @@ pub use workflow_run::*; use serde::{Deserialize, Serialize}; use url::Url; +use crate::models::{orgs::Organization, Author, Repository}; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct EventInstallationPayload { + pub id: InstallationId, +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct WrappedEventPayload { + pub installation: Option, + pub organization: Option, + pub repository: Option, + pub sender: Option, + #[serde(flatten)] + pub specific: Option, +} + /// The payload in an event type. /// /// Different event types have different payloads. Any event type not specifically part diff --git a/src/models/events/payload/commit_comment.rs b/src/models/events/payload/commit_comment.rs index 7f62e884..01a1c652 100644 --- a/src/models/events/payload/commit_comment.rs +++ b/src/models/events/payload/commit_comment.rs @@ -17,7 +17,9 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/commit_comment_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::CommitCommentEvent(payload)) = event.payload { + if let Some(EventPayload::CommitCommentEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.comment.id.0, 46377107); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); diff --git a/src/models/events/payload/create.rs b/src/models/events/payload/create.rs index a284b372..5a38ebb4 100644 --- a/src/models/events/payload/create.rs +++ b/src/models/events/payload/create.rs @@ -21,7 +21,7 @@ mod test { let json = include_str!("../../../../tests/resources/create_event.json"); let event: Event = serde_json::from_str(json).unwrap(); assert!(event.payload.is_some()); - let payload = event.payload.unwrap(); + let payload = event.payload.unwrap().specific.unwrap(); match payload { EventPayload::CreateEvent(payload) => { assert_eq!(payload.r#ref, Some("url-normalisation".to_string())); @@ -43,7 +43,7 @@ mod test { include_str!("../../../../tests/resources/create_event_with_null_description.json"); let event: Event = serde_json::from_str(json).unwrap(); assert!(event.payload.is_some()); - let payload = event.payload.unwrap(); + let payload = event.payload.unwrap().specific.unwrap(); match payload { EventPayload::CreateEvent(payload) => assert_eq!(payload.description, None), _ => panic!("unexpected event deserialized"), diff --git a/src/models/events/payload/delete.rs b/src/models/events/payload/delete.rs index 6de75238..3aaba0be 100644 --- a/src/models/events/payload/delete.rs +++ b/src/models/events/payload/delete.rs @@ -18,7 +18,9 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/delete_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::DeleteEvent(payload)) = event.payload { + if let Some(EventPayload::DeleteEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.r#ref, "test2"); assert_eq!(payload.ref_type, "branch"); } else { diff --git a/src/models/events/payload/fork.rs b/src/models/events/payload/fork.rs index a6b5078c..ea0adbf6 100644 --- a/src/models/events/payload/fork.rs +++ b/src/models/events/payload/fork.rs @@ -17,7 +17,8 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/fork_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::ForkEvent(payload)) = event.payload { + if let Some(EventPayload::ForkEvent(ref payload)) = event.payload.as_ref().unwrap().specific + { assert_eq!(payload.forkee.id.0, 334843423); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); diff --git a/src/models/events/payload/gollum.rs b/src/models/events/payload/gollum.rs index e7145c26..c0659ca1 100644 --- a/src/models/events/payload/gollum.rs +++ b/src/models/events/payload/gollum.rs @@ -55,7 +55,9 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/gollum_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::GollumEvent(payload)) = event.payload { + if let Some(EventPayload::GollumEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.pages[0].page_name, "Home"); assert_eq!(payload.pages[0].title, "Home"); assert_eq!(payload.pages[0].action, GollumEventPageAction::Created); diff --git a/src/models/events/payload/issue_comment.rs b/src/models/events/payload/issue_comment.rs index 0c91bffa..f4a6aa63 100644 --- a/src/models/events/payload/issue_comment.rs +++ b/src/models/events/payload/issue_comment.rs @@ -83,7 +83,9 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/issue_comment_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::IssueCommentEvent(payload)) = event.payload { + if let Some(EventPayload::IssueCommentEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.action, IssueCommentEventAction::Created); assert_eq!(payload.issue.id.0, 785981862); assert_eq!(payload.comment.id.0, 760203693); diff --git a/src/models/events/payload/issues.rs b/src/models/events/payload/issues.rs index c24d071b..2abbc2c8 100644 --- a/src/models/events/payload/issues.rs +++ b/src/models/events/payload/issues.rs @@ -120,7 +120,9 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/issues_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::IssuesEvent(payload)) = event.payload { + if let Some(EventPayload::IssuesEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.action, IssuesEventAction::Opened); assert_eq!(payload.issue.id.0, 786747990); } else { diff --git a/src/models/events/payload/member.rs b/src/models/events/payload/member.rs index 337458b7..a1333b47 100644 --- a/src/models/events/payload/member.rs +++ b/src/models/events/payload/member.rs @@ -83,7 +83,9 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/member_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::MemberEvent(payload)) = event.payload { + if let Some(EventPayload::MemberEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.action, MemberEventAction::Added); assert_eq!(payload.member.id.0, 58522265); } else { diff --git a/src/models/events/payload/pull_request.rs b/src/models/events/payload/pull_request.rs index aa1896fb..e29670cf 100644 --- a/src/models/events/payload/pull_request.rs +++ b/src/models/events/payload/pull_request.rs @@ -148,7 +148,9 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/pull_request_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::PullRequestEvent(payload)) = event.payload { + if let Some(EventPayload::PullRequestEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.action, PullRequestEventAction::Opened); assert_eq!(payload.number, 8); assert_eq!(payload.pull_request.id.0, 558121796); diff --git a/src/models/events/payload/pull_request_review.rs b/src/models/events/payload/pull_request_review.rs index 790c74d9..aa01d848 100644 --- a/src/models/events/payload/pull_request_review.rs +++ b/src/models/events/payload/pull_request_review.rs @@ -75,7 +75,9 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/pull_request_review_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::PullRequestReviewEvent(payload)) = event.payload { + if let Some(EventPayload::PullRequestReviewEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.pull_request.id.0, 1237933052); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); diff --git a/src/models/events/payload/pull_request_review_comment.rs b/src/models/events/payload/pull_request_review_comment.rs index c6873259..ac26c25f 100644 --- a/src/models/events/payload/pull_request_review_comment.rs +++ b/src/models/events/payload/pull_request_review_comment.rs @@ -84,7 +84,9 @@ mod test { let json = include_str!("../../../../tests/resources/pull_request_review_comment_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::PullRequestReviewCommentEvent(payload)) = event.payload { + if let Some(EventPayload::PullRequestReviewCommentEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.action, PullRequestReviewCommentEventAction::Created); assert_eq!(payload.pull_request.id.0, 558121796); assert_eq!(payload.comment.id.0, 560976245); diff --git a/src/models/events/payload/push.rs b/src/models/events/payload/push.rs index 0d2ff288..afe602ac 100644 --- a/src/models/events/payload/push.rs +++ b/src/models/events/payload/push.rs @@ -29,7 +29,7 @@ mod test { let json = include_str!("../../../../tests/resources/push_event.json"); let event: Event = serde_json::from_str(json).unwrap(); assert!(event.payload.is_some()); - let payload = event.payload.unwrap(); + let payload = event.payload.unwrap().specific.unwrap(); match payload { EventPayload::PushEvent(payload) => { assert_eq!(payload.push_id.0, 6080608029); diff --git a/src/models/events/payload/workflow_run.rs b/src/models/events/payload/workflow_run.rs index 2fe035b7..3bba8cc1 100644 --- a/src/models/events/payload/workflow_run.rs +++ b/src/models/events/payload/workflow_run.rs @@ -12,9 +12,6 @@ pub struct WorkflowRunEventPayload { pub action: WorkflowRunEventAction, pub workflow_run: Run, pub workflow: WorkFlow, - pub organization: Option, - pub repository: Repository, - pub sender: Author, } /// The action on a pull request this event corresponds to. @@ -34,7 +31,9 @@ mod test { fn should_deserialize_with_correct_payload() { let json = include_str!("../../../../tests/resources/workflow_run_event.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::WorkflowRunEvent(payload)) = event.payload { + if let Some(EventPayload::WorkflowRunEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.workflow_run.run_number, 1185); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); @@ -46,9 +45,11 @@ mod test { let json = include_str!("../../../../tests/resources/workflow_run_event_no_organization.json"); let event: Event = serde_json::from_str(json).unwrap(); - if let Some(EventPayload::WorkflowRunEvent(payload)) = event.payload { + if let Some(EventPayload::WorkflowRunEvent(ref payload)) = + event.payload.as_ref().unwrap().specific + { assert_eq!(payload.workflow_run.run_number, 1185); - assert_eq!(payload.organization, None); + assert_eq!(event.payload.as_ref().unwrap().organization, None); } else { panic!("unexpected event payload encountered: {:#?}", event.payload); }