//! Opening the forge events stream. The name, subjects and why it exists are //! `swarm_wire::forge_events`. use swarm_wire::forge_events::{MAX_AGE, STREAM, subject_wildcard}; /// The config [`open_or_create`] creates the stream with. fn stream_config() -> async_nats::jetstream::stream::Config { async_nats::jetstream::stream::Config { name: STREAM.to_owned(), description: Some("Verified forge webhook deliveries".to_owned()), subjects: vec![subject_wildcard()], max_age: MAX_AGE, // A redelivered webhook carries the same `X-Forgejo-Delivery`, which // the controller sends as the message id. duplicate_window: std::time::Duration::from_hours(1), ..Default::default() } } /// Open the stream, creating it if nothing has yet. Called by the controller, /// which publishes into it, and by the router, which reads it: either may come /// up first. pub async fn open_or_create( client: &async_nats::Client, ) -> Result { let js = async_nats::jetstream::new(client.clone()); match js.get_stream(STREAM).await { Ok(stream) => Ok(stream), Err(e) => { tracing::info!(stream = STREAM, reason = %e, "forge events stream not available, creating it"); js.create_stream(stream_config()) .await .map_err(|source| crate::Error::CreateStream { stream: STREAM, source, }) } } } #[cfg(test)] mod tests { use swarm_wire::forge_events::{STREAM, subject}; use super::open_or_create; use crate::test_nats::TestNats; /// Either end may create the stream first, and a redelivered webhook is /// kept once. #[tokio::test] async fn the_stream_is_created_once_and_keeps_a_redelivery_once() { let nats = TestNats::start(); let client = nats.client().await; open_or_create(&client).await.expect("created"); open_or_create(&client).await.expect("opened as it is"); let js = async_nats::jetstream::new(client); for (event, id) in [ ("issues", "d-1"), ("issues", "d-1"), ("issue_assign", "d-2"), ] { js.send_publish( subject(event), async_nats::jetstream::message::PublishMessage::build() .payload("{}".into()) .message_id(id), ) .await .expect("published") .await .expect("acked"); } let mut stream = js.get_stream(STREAM).await.expect("the stream"); let info = stream.info().await.expect("info"); assert_eq!(info.state.messages, 2); } }