From 8347a6d600b11fe8d9e5d82217b9df56f9c800c8 Mon Sep 17 00:00:00 2001 From: Alexander Lyon Date: Thu, 6 Apr 2023 11:53:05 +0100 Subject: [PATCH] feat: use codegen version of WebhookEvent rather than overriding manually --- examples/webhook-actix.rs | 4 +- openapi/src/codegen.rs | 4 ++ src/resources.rs | 1 + src/resources/generated/event.rs | 4 +- src/resources/webhook_events.rs | 79 +++++++++++--------------------- 5 files changed, 37 insertions(+), 55 deletions(-) diff --git a/examples/webhook-actix.rs b/examples/webhook-actix.rs index 1feaa4938..434f18fca 100644 --- a/examples/webhook-actix.rs +++ b/examples/webhook-actix.rs @@ -36,7 +36,7 @@ pub fn handle_webhook(req: HttpRequest, payload: web::Bytes) -> Result<(), Webho let stripe_signature = get_header_value(&req, "Stripe-Signature").unwrap_or_default(); if let Ok(event) = Webhook::construct_event(payload_str, stripe_signature, "whsec_xxxxx") { - match event.event_type { + match event.type_ { EventType::AccountUpdated => { if let EventObject::Account(account) = event.data.object { handle_account_updated(account)?; @@ -48,7 +48,7 @@ pub fn handle_webhook(req: HttpRequest, payload: web::Bytes) -> Result<(), Webho } } _ => { - println!("Unknown event encountered in webhook: {:?}", event.event_type); + println!("Unknown event encountered in webhook: {:?}", event.type_); } } } else { diff --git a/openapi/src/codegen.rs b/openapi/src/codegen.rs index 3bbf633ff..4d3300a78 100644 --- a/openapi/src/codegen.rs +++ b/openapi/src/codegen.rs @@ -187,6 +187,7 @@ pub fn gen_generated_schemas( while let Some(schema_name) = state.generated_schemas.iter().find_map(|(k, &v)| if !v { Some(k) } else { None }).cloned() { + log::trace!("generating schema {}", schema_name); let struct_name = meta.schema_to_rust_type(&schema_name); out.push('\n'); out.push_str("#[derive(Clone, Debug, Default, Deserialize, Serialize)]\n"); @@ -1237,6 +1238,9 @@ pub fn gen_field_rust_type>( } else { "Timestamp".into() }; + } else if field_name == "type" && object == "event" { + state.use_resources.insert("EventType".into()); + return "EventType".into(); } let ty = gen_schema_or_ref_type( diff --git a/src/resources.rs b/src/resources.rs index 4b2e5e2c0..b063ac88c 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -160,6 +160,7 @@ pub use { #[cfg(feature = "events")] pub use { webhook_events::*, + webhook_events::NotificationEventData, generated::event::*, }; diff --git a/src/resources/generated/event.rs b/src/resources/generated/event.rs index 474be8670..ab2d0e79c 100644 --- a/src/resources/generated/event.rs +++ b/src/resources/generated/event.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use crate::client::{Client, Response}; use crate::ids::EventId; use crate::params::{Expand, List, Object, Paginable, RangeQuery, Timestamp}; -use crate::resources::NotificationEventData; +use crate::resources::{EventType, NotificationEventData}; /// The resource representing a Stripe "NotificationEvent". /// @@ -44,7 +44,7 @@ pub struct Event { /// Description of the event (e.g., `invoice.created` or `charge.refunded`). #[serde(rename = "type")] - pub type_: String, + pub type_: EventType, } impl Event { diff --git a/src/resources/webhook_events.rs b/src/resources/webhook_events.rs index 572365e20..656ab9c7f 100644 --- a/src/resources/webhook_events.rs +++ b/src/resources/webhook_events.rs @@ -4,12 +4,12 @@ use hmac::{Hmac, Mac}; use serde::{Deserialize, Serialize}; #[cfg(feature = "webhook-events")] use sha2::Sha256; +use smart_default::SmartDefault; use crate::error::WebhookError; -use crate::ids::EventId; -use crate::{resources::*, AccountId, Timestamp}; +use crate::resources::*; -#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)] +#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash, SmartDefault)] pub enum EventType { #[serde(rename = "account.application.authorized")] AccountApplicationAuthorized, @@ -391,47 +391,23 @@ pub enum EventType { TransferReversed, #[serde(rename = "transfer.updated")] TransferUpdated, + #[serde(other)] + #[default] + Unknown, } -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct WebhookEvent { - /// Unique identifier for the object. - pub id: EventId, - - /// Description of the event (e.g., `invoice.created` or `charge.refunded`). - #[serde(rename = "type")] - pub event_type: EventType, - - /// The connected account that originated the event. - #[serde(skip_serializing_if = "Option::is_none")] - pub account: Option, - - /// The Stripe API version used to render `data`. - /// - /// *Note: This property is populated only for events on or after October 31, 2014*. - #[serde(skip_serializing_if = "Option::is_none")] - pub api_version: Option, - - /// Time at which the object was created. - /// - /// Measured in seconds since the Unix epoch. - pub created: Timestamp, - - pub data: EventData, - - /// Has the value `true` if the object exists in live mode or the value `false` if the object exists in test mode. - pub livemode: bool, - - /// Number of webhooks that have yet to be successfully delivered (i.e., to return a 20x response) to the URLs you've specified. - pub pending_webhooks: i64, - - /// Information on the API request that instigated the event. - #[serde(skip_serializing_if = "Option::is_none")] - pub request: Option, +impl std::fmt::Display for EventType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&serde_json::to_string(self).unwrap()) + } } -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct EventData { +/// The resource representing a Stripe "NotificationEventData". +/// +/// note: this is a manual override of the generated code; +/// see notification_event_data.rs for the (broken) codegen +#[derive(Clone, Debug, Deserialize, Serialize, Default)] +pub struct NotificationEventData { pub object: EventObject, // previous_attributes: ... } @@ -496,6 +472,12 @@ pub enum EventObject { Transfer(Transfer), } +impl Default for EventObject { + fn default() -> Self { + EventObject::Account(Account::default()) + } +} + #[cfg(feature = "webhook-events")] pub struct Webhook { current_timestamp: i64, @@ -509,11 +491,7 @@ impl Webhook { /// - the provided signature is invalid /// - the provided secret is invalid /// - the signature timestamp is older than 5 minutes - pub fn construct_event( - payload: &str, - sig: &str, - secret: &str, - ) -> Result { + pub fn construct_event(payload: &str, sig: &str, secret: &str) -> Result { Self { current_timestamp: Utc::now().timestamp() }.do_construct_event(payload, sig, secret) } @@ -522,7 +500,7 @@ impl Webhook { payload: &str, sig: &str, secret: &str, - ) -> Result { + ) -> Result { // Get Stripe signature from header let signature = Signature::parse(sig)?; let signed_payload = format!("{}.{}", signature.t, payload); @@ -534,6 +512,7 @@ impl Webhook { mac.update(signed_payload.as_bytes()); let sig = hex::decode(signature.v1).map_err(|_| WebhookError::BadSignature)?; + mac.verify_slice(sig.as_slice()).map_err(|_| WebhookError::BadSignature)?; // Get current timestamp to compare to signature timestamp @@ -626,10 +605,8 @@ mod tests { "start": 1533204620, "end": 1533204620 }, - "plan": null, "proration": false, - "quantity": null, - "subscription": null + "quantity": 3 } }, "livemode": false, @@ -643,7 +620,7 @@ mod tests { "#; let event_timestamp = 1533204620; let secret = "webhook_secret".to_string(); - let signature = format!("t={},v1=f0bdba6d4eacbd8ad8a3bbadd7248e633ec1477f7899c124c51b39405fa36613,v0=63f3a72374a733066c4be69ed7f8e5ac85c22c9f0a6a612ab9a025a9e4ee7eef", event_timestamp); + let signature = format!("t={},v1=82216eca827bcb7b34b8055eb2d2d9e6bc13b9ac39ded14a61e69f70c565f53a,v0=63f3a72374a733066c4be69ed7f8e5ac85c22c9f0a6a612ab9a025a9e4ee7eef", event_timestamp); let webhook = super::Webhook { current_timestamp: event_timestamp }; @@ -651,7 +628,7 @@ mod tests { .do_construct_event(payload, &signature, &secret) .expect("Failed to construct event"); - assert_eq!(event.event_type, super::EventType::InvoiceItemCreated); + assert_eq!(event.type_, super::EventType::InvoiceItemCreated); assert_eq!(event.id, "evt_123".parse::().unwrap()); assert_eq!(event.account, "acct_123".parse().ok()); assert_eq!(event.created, 1533204620);