@@ -12,13 +12,60 @@ use crate::Generation;
1212use crate :: WebhookDelivery ;
1313use chrono:: { DateTime , Utc } ;
1414use db_macros:: Resource ;
15+ use nexus_types:: external_api:: views;
1516use omicron_common:: api:: external:: Error ;
16- use omicron_uuid_kinds:: { WebhookReceiverKind , WebhookReceiverUuid } ;
17+ use omicron_uuid_kinds:: {
18+ WebhookReceiverKind , WebhookReceiverUuid , WebhookSecretKind ,
19+ WebhookSecretUuid ,
20+ } ;
1721use serde:: { Deserialize , Serialize } ;
1822use std:: str:: FromStr ;
1923use uuid:: Uuid ;
2024
21- /// A webhook receiver configuration.
25+ /// The full configuration of a webhook receiver, including the
26+ /// [`WebhookReceiver`] itself and its subscriptions and secrets.
27+ pub struct WebhookReceiverConfig {
28+ pub rx : WebhookReceiver ,
29+ pub secrets : Vec < WebhookRxSecret > ,
30+ pub events : Vec < WebhookSubscriptionKind > ,
31+ }
32+
33+ impl TryFrom < WebhookReceiverConfig > for views:: Webhook {
34+ type Error = Error ;
35+ fn try_from (
36+ WebhookReceiverConfig { rx, secrets, events } : WebhookReceiverConfig ,
37+ ) -> Result < views:: Webhook , Self :: Error > {
38+ let secrets = secrets
39+ . iter ( )
40+ . map ( |WebhookRxSecret { signature_id, .. } | {
41+ views:: WebhookSecretId { id : signature_id. to_string ( ) }
42+ } )
43+ . collect ( ) ;
44+ let events = events
45+ . into_iter ( )
46+ . map ( WebhookSubscriptionKind :: into_event_class_string)
47+ . collect ( ) ;
48+ let WebhookReceiver { identity, endpoint, probes_enabled, rcgen : _ } =
49+ rx;
50+ let WebhookReceiverIdentity { id, name, description, .. } = identity;
51+ let endpoint = endpoint. parse ( ) . map_err ( |e| Error :: InternalError {
52+ // This is an internal error, as we should not have ever allowed
53+ // an invalid URL to be inserted into the database...
54+ internal_message : format ! ( "invalid webhook URL {endpoint:?}: {e}" , ) ,
55+ } ) ?;
56+ Ok ( views:: Webhook {
57+ id : id. into ( ) ,
58+ name : name. to_string ( ) ,
59+ description,
60+ endpoint,
61+ secrets,
62+ events,
63+ disable_probes : !probes_enabled,
64+ } )
65+ }
66+ }
67+
68+ /// A row in the `webhook_rx` table.
2269#[ derive(
2370 Clone ,
2471 Debug ,
@@ -78,12 +125,24 @@ impl DatastoreCollectionConfig<WebhookDelivery> for WebhookReceiver {
78125#[ diesel( table_name = webhook_rx_secret) ]
79126pub struct WebhookRxSecret {
80127 pub rx_id : DbTypedUuid < WebhookReceiverKind > ,
81- pub signature_id : String ,
82- pub secret : Vec < u8 > ,
128+ pub signature_id : DbTypedUuid < WebhookSecretKind > ,
129+ pub secret : String ,
83130 pub time_created : DateTime < Utc > ,
84131 pub time_deleted : Option < DateTime < Utc > > ,
85132}
86133
134+ impl WebhookRxSecret {
135+ pub fn new ( rx_id : WebhookReceiverUuid , secret : String ) -> Self {
136+ Self {
137+ rx_id : rx_id. into ( ) ,
138+ signature_id : WebhookSecretUuid :: new_v4 ( ) . into ( ) ,
139+ secret,
140+ time_created : Utc :: now ( ) ,
141+ time_deleted : None ,
142+ }
143+ }
144+ }
145+
87146#[ derive(
88147 Clone , Debug , Queryable , Selectable , Insertable , Serialize , Deserialize ,
89148) ]
@@ -132,6 +191,13 @@ impl WebhookSubscriptionKind {
132191 Ok ( Self :: Exact ( value) )
133192 }
134193 }
194+
195+ fn into_event_class_string ( self ) -> String {
196+ match self {
197+ Self :: Exact ( class) => class,
198+ Self :: Glob ( WebhookGlob { glob, .. } ) => glob,
199+ }
200+ }
135201}
136202
137203#[ derive(
0 commit comments