Skip to content

Commit

Permalink
Add installation_id in Webhook events (#408)
Browse files Browse the repository at this point in the history
* 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)
  • Loading branch information
gagbo authored Jul 12, 2023
1 parent f2ce798 commit e092d94
Show file tree
Hide file tree
Showing 16 changed files with 91 additions and 26 deletions.
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "octocrab"
version = "0.25.1"
version = "0.26.0"
authors = ["XAMPPRocky <[email protected]>"]
edition = "2018"
readme = "README.md"
Expand Down Expand Up @@ -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"]
Expand Down
37 changes: 32 additions & 5 deletions src/models/events.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -22,7 +24,7 @@ pub struct Event {
pub repo: Repository,
pub public: bool,
pub created_at: DateTime<Utc>,
pub payload: Option<EventPayload>,
pub payload: Option<WrappedEventPayload>,
pub org: Option<Org>,
}

Expand Down Expand Up @@ -133,12 +135,32 @@ impl<'de> Deserialize<'de> for Event {
public: bool,
created_at: DateTime<Utc>,
org: Option<Org>,
payload: Option<serde_json::Value>,
payload: Option<IntermediatePayload>,
}
#[derive(Deserialize)]
struct IntermediatePayload {
installation: Option<EventInstallationPayload>,
organization: Option<crate::models::orgs::Organization>,
repository: Option<crate::models::Repository>,
sender: Option<crate::models::Author>,
#[serde(flatten)]
specific: Option<serde_json::Value>,
}
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,
Expand All @@ -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]
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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();
Expand Down
19 changes: 18 additions & 1 deletion src/models/events/payload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -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<EventInstallationPayload>,
pub organization: Option<Organization>,
pub repository: Option<Repository>,
pub sender: Option<Author>,
#[serde(flatten)]
pub specific: Option<EventPayload>,
}

/// The payload in an event type.
///
/// Different event types have different payloads. Any event type not specifically part
Expand Down
4 changes: 3 additions & 1 deletion src/models/events/payload/commit_comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/models/events/payload/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()));
Expand All @@ -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"),
Expand Down
4 changes: 3 additions & 1 deletion src/models/events/payload/delete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion src/models/events/payload/fork.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/models/events/payload/gollum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/models/events/payload/issue_comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/models/events/payload/issues.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion src/models/events/payload/member.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion src/models/events/payload/pull_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/models/events/payload/pull_request_review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 3 additions & 1 deletion src/models/events/payload/pull_request_review_comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/models/events/payload/push.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
13 changes: 7 additions & 6 deletions src/models/events/payload/workflow_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@ pub struct WorkflowRunEventPayload {
pub action: WorkflowRunEventAction,
pub workflow_run: Run,
pub workflow: WorkFlow,
pub organization: Option<Organization>,
pub repository: Repository,
pub sender: Author,
}

/// The action on a pull request this event corresponds to.
Expand All @@ -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);
Expand All @@ -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);
}
Expand Down

0 comments on commit e092d94

Please sign in to comment.