@@ -13,6 +13,7 @@ use trogon_source_gitlab::config::GitLabWebhookSecret;
1313use trogon_source_incidentio:: config:: IncidentioConfig as IncidentioSourceConfig ;
1414use trogon_source_incidentio:: incidentio_signing_secret:: IncidentioSigningSecret ;
1515use trogon_source_linear:: config:: LinearWebhookSecret ;
16+ use trogon_source_notion:: NotionVerificationToken ;
1617use trogon_source_slack:: config:: SlackSigningSecret ;
1718use trogon_source_telegram:: config:: TelegramWebhookSecret ;
1819use trogon_std:: { NonZeroDuration , ZeroDuration } ;
@@ -148,6 +149,8 @@ struct SourcesConfig {
148149 incidentio : IncidentioConfig ,
149150 #[ config( nested) ]
150151 linear : LinearConfig ,
152+ #[ config( nested) ]
153+ notion : NotionConfig ,
151154}
152155
153156#[ derive( Config ) ]
@@ -265,6 +268,20 @@ struct IncidentioConfig {
265268 timestamp_tolerance_secs : u64 ,
266269}
267270
271+ #[ derive( Config ) ]
272+ struct NotionConfig {
273+ #[ config( env = "TROGON_SOURCE_NOTION_VERIFICATION_TOKEN" ) ]
274+ verification_token : Option < String > ,
275+ #[ config( env = "TROGON_SOURCE_NOTION_SUBJECT_PREFIX" , default = "notion" ) ]
276+ subject_prefix : String ,
277+ #[ config( env = "TROGON_SOURCE_NOTION_STREAM_NAME" , default = "NOTION" ) ]
278+ stream_name : String ,
279+ #[ config( env = "TROGON_SOURCE_NOTION_STREAM_MAX_AGE_SECS" , default = 604_800 ) ]
280+ stream_max_age_secs : u64 ,
281+ #[ config( env = "TROGON_SOURCE_NOTION_NATS_ACK_TIMEOUT_SECS" , default = 10 ) ]
282+ nats_ack_timeout_secs : u64 ,
283+ }
284+
268285pub struct ResolvedHttpServerConfig {
269286 pub port : u16 ,
270287}
@@ -279,6 +296,7 @@ pub struct ResolvedConfig {
279296 pub gitlab : Option < trogon_source_gitlab:: GitlabConfig > ,
280297 pub incidentio : Option < trogon_source_incidentio:: IncidentioConfig > ,
281298 pub linear : Option < trogon_source_linear:: LinearConfig > ,
299+ pub notion : Option < trogon_source_notion:: NotionConfig > ,
282300}
283301
284302impl ResolvedConfig {
@@ -290,6 +308,7 @@ impl ResolvedConfig {
290308 || self . gitlab . is_some ( )
291309 || self . incidentio . is_some ( )
292310 || self . linear . is_some ( )
311+ || self . notion . is_some ( )
293312 }
294313}
295314
@@ -317,6 +336,7 @@ fn resolve(cfg: GatewayConfig, nats_overrides: &NatsArgs) -> Result<ResolvedConf
317336 let gitlab = resolve_gitlab ( cfg. sources . gitlab , & mut errors) ;
318337 let incidentio = resolve_incidentio ( cfg. sources . incidentio , & mut errors) ;
319338 let linear = resolve_linear ( cfg. sources . linear , & mut errors) ;
339+ let notion = resolve_notion ( cfg. sources . notion , & mut errors) ;
320340
321341 if !errors. is_empty ( ) {
322342 return Err ( ConfigError :: Validation ( errors) ) ;
@@ -334,6 +354,7 @@ fn resolve(cfg: GatewayConfig, nats_overrides: &NatsArgs) -> Result<ResolvedConf
334354 gitlab,
335355 incidentio,
336356 linear,
357+ notion,
337358 } )
338359}
339360
@@ -891,6 +912,86 @@ fn resolve_incidentio(
891912 } )
892913}
893914
915+ fn resolve_notion (
916+ section : NotionConfig ,
917+ errors : & mut Vec < ConfigValidationError > ,
918+ ) -> Option < trogon_source_notion:: NotionConfig > {
919+ let verification_token = match section. verification_token {
920+ Some ( token) => token,
921+ None => {
922+ return None ;
923+ }
924+ } ;
925+
926+ let verification_token = match NotionVerificationToken :: new ( verification_token) {
927+ Ok ( token) => token,
928+ Err ( err) => {
929+ errors. push ( ConfigValidationError :: invalid (
930+ "notion" ,
931+ "verification_token" ,
932+ err,
933+ ) ) ;
934+ return None ;
935+ }
936+ } ;
937+
938+ let subject_prefix = match NatsToken :: new ( section. subject_prefix ) {
939+ Ok ( token) => token,
940+ Err ( err) => {
941+ errors. push ( ConfigValidationError :: invalid_subject_token (
942+ "notion" ,
943+ "subject_prefix" ,
944+ err,
945+ ) ) ;
946+ return None ;
947+ }
948+ } ;
949+
950+ let stream_name = match NatsToken :: new ( section. stream_name ) {
951+ Ok ( token) => token,
952+ Err ( err) => {
953+ errors. push ( ConfigValidationError :: invalid_subject_token (
954+ "notion" ,
955+ "stream_name" ,
956+ err,
957+ ) ) ;
958+ return None ;
959+ }
960+ } ;
961+
962+ let nats_ack_timeout = match NonZeroDuration :: from_secs ( section. nats_ack_timeout_secs ) {
963+ Ok ( duration) => duration,
964+ Err ( err) => {
965+ errors. push ( ConfigValidationError :: invalid (
966+ "notion" ,
967+ "nats_ack_timeout_secs" ,
968+ err,
969+ ) ) ;
970+ return None ;
971+ }
972+ } ;
973+
974+ let stream_max_age = match StreamMaxAge :: from_secs ( section. stream_max_age_secs ) {
975+ Ok ( age) => age,
976+ Err ( err) => {
977+ errors. push ( ConfigValidationError :: invalid (
978+ "notion" ,
979+ "stream_max_age_secs" ,
980+ err,
981+ ) ) ;
982+ return None ;
983+ }
984+ } ;
985+
986+ Some ( trogon_source_notion:: NotionConfig {
987+ verification_token,
988+ subject_prefix,
989+ stream_name,
990+ stream_max_age,
991+ nats_ack_timeout,
992+ } )
993+ }
994+
894995#[ cfg( test) ]
895996mod tests {
896997 use super :: * ;
@@ -976,6 +1077,15 @@ signing_secret = "{secret}"
9761077 )
9771078 }
9781079
1080+ fn notion_toml ( token : & str ) -> String {
1081+ format ! (
1082+ r#"
1083+ [sources.notion]
1084+ verification_token = "{token}"
1085+ "#
1086+ )
1087+ }
1088+
9791089 fn incidentio_valid_test_secret ( ) -> String {
9801090 [ "whsec_" , "dGVzdC1zZWNyZXQ=" ] . concat ( )
9811091 }
@@ -1134,6 +1244,23 @@ bot_token = ""
11341244 assert ! ( cfg. incidentio. is_some( ) ) ;
11351245 }
11361246
1247+ #[ test]
1248+ fn notion_resolves_with_valid_token ( ) {
1249+ let f = write_toml ( & notion_toml ( "notion-verification-token-example" ) ) ;
1250+ let cfg = load ( Some ( f. path ( ) ) ) . expect ( "load failed" ) ;
1251+ assert ! ( cfg. notion. is_some( ) ) ;
1252+ }
1253+
1254+ #[ test]
1255+ fn notion_missing_token_returns_none ( ) {
1256+ let toml = r#"
1257+ [sources.notion]
1258+ "# ;
1259+ let f = write_toml ( toml) ;
1260+ let cfg = load ( Some ( f. path ( ) ) ) . expect ( "load failed" ) ;
1261+ assert ! ( cfg. notion. is_none( ) ) ;
1262+ }
1263+
11371264 #[ test]
11381265 fn linear_with_zero_timestamp_tolerance ( ) {
11391266 let toml = r#"
@@ -1643,6 +1770,15 @@ port = 9090
16431770 ) ;
16441771 }
16451772
1773+ #[ test]
1774+ fn notion_empty_verification_token_is_invalid ( ) {
1775+ let f = write_toml ( & notion_toml ( "" ) ) ;
1776+ let result = load ( Some ( f. path ( ) ) ) ;
1777+ assert ! (
1778+ matches!( result, Err ( ConfigError :: Validation ( ref errs) ) if errs. iter( ) . any( |e| e. contains( "notion: invalid verification_token" ) ) )
1779+ ) ;
1780+ }
1781+
16461782 #[ test]
16471783 fn incidentio_invalid_secret_is_invalid ( ) {
16481784 let f = write_toml ( & incidentio_toml ( "whsec_not-base64!" ) ) ;
@@ -1762,6 +1898,20 @@ subject_prefix = "has.dots"
17621898 ) ;
17631899 }
17641900
1901+ #[ test]
1902+ fn notion_invalid_subject_prefix ( ) {
1903+ let toml = r#"
1904+ [sources.notion]
1905+ verification_token = "notion-verification-token-example"
1906+ subject_prefix = "has.dots"
1907+ "# ;
1908+ let f = write_toml ( toml) ;
1909+ let result = load ( Some ( f. path ( ) ) ) ;
1910+ assert ! (
1911+ matches!( result, Err ( ConfigError :: Validation ( ref errs) ) if errs. iter( ) . any( |e| e. contains( "notion: invalid subject_prefix" ) ) )
1912+ ) ;
1913+ }
1914+
17651915 #[ test]
17661916 fn slack_invalid_stream_name ( ) {
17671917 let toml = r#"
@@ -1835,6 +1985,20 @@ stream_name = "has.dots"
18351985 ) ;
18361986 }
18371987
1988+ #[ test]
1989+ fn notion_invalid_stream_name ( ) {
1990+ let toml = r#"
1991+ [sources.notion]
1992+ verification_token = "notion-verification-token-example"
1993+ stream_name = "has.dots"
1994+ "# ;
1995+ let f = write_toml ( toml) ;
1996+ let result = load ( Some ( f. path ( ) ) ) ;
1997+ assert ! (
1998+ matches!( result, Err ( ConfigError :: Validation ( ref errs) ) if errs. iter( ) . any( |e| e. contains( "notion: invalid stream_name" ) ) )
1999+ ) ;
2000+ }
2001+
18382002 #[ test]
18392003 fn incidentio_zero_nats_ack_timeout_is_error ( ) {
18402004 let toml = format ! (
@@ -1869,6 +2033,34 @@ stream_max_age_secs = 0
18692033 ) ;
18702034 }
18712035
2036+ #[ test]
2037+ fn notion_zero_nats_ack_timeout_is_error ( ) {
2038+ let toml = r#"
2039+ [sources.notion]
2040+ verification_token = "notion-verification-token-example"
2041+ nats_ack_timeout_secs = 0
2042+ "# ;
2043+ let f = write_toml ( toml) ;
2044+ let result = load ( Some ( f. path ( ) ) ) ;
2045+ assert ! (
2046+ matches!( result, Err ( ConfigError :: Validation ( ref errs) ) if errs. iter( ) . any( |e| e. contains( "notion: nats_ack_timeout_secs must not be zero" ) ) )
2047+ ) ;
2048+ }
2049+
2050+ #[ test]
2051+ fn notion_zero_stream_max_age_is_error ( ) {
2052+ let toml = r#"
2053+ [sources.notion]
2054+ verification_token = "notion-verification-token-example"
2055+ stream_max_age_secs = 0
2056+ "# ;
2057+ let f = write_toml ( toml) ;
2058+ let result = load ( Some ( f. path ( ) ) ) ;
2059+ assert ! (
2060+ matches!( result, Err ( ConfigError :: Validation ( ref errs) ) if errs. iter( ) . any( |e| e. contains( "notion: stream_max_age_secs must not be zero" ) ) )
2061+ ) ;
2062+ }
2063+
18722064 #[ test]
18732065 fn incidentio_zero_timestamp_tolerance_is_error ( ) {
18742066 let toml = format ! (
0 commit comments