Radish alpha
r
rad:zwTxygwuz5LDGBq255RA2CbNGrz8
Radicle CI broker
Radicle
Git
fix: add a time stamp to RSS feed items
Merged liw opened 1 year ago

Signed-off-by: Lars Wirzenius liw@liw.fi

2 files changed +52 -5 d7bccbfc b8027442
modified src/pages.rs
@@ -35,6 +35,7 @@ use crate::{
    msg::{RunId, RunResult},
    notif::NotificationReceiver,
    run::{Run, RunState, Whence},
+
    util::{parse_timestamp, rfc822_timestamp},
};

const BROKER_RSS: &str = "index.rss";
@@ -68,6 +69,9 @@ pub enum PageError {

    #[error("failed to represent status page data as JSON")]
    StatusToJson(#[source] serde_json::Error),
+

+
    #[error("failed to create RSS time stamp from CI run timestamp: {0}")]
+
    RssTimestamp(String, #[source] crate::util::UtilError),
}

fn now() -> Result<String, time::error::Format> {
@@ -540,7 +544,7 @@ impl PageData {
            .link("FIXME:link");
        for (_alias, repo_id) in self.repos() {
            for run in self.runs(repo_id) {
-
                channel.item(Self::rss_item_from_run(run));
+
                channel.item(Self::rss_item_from_run(run)?);
            }
        }
        Ok(channel.build())
@@ -555,14 +559,14 @@ impl PageData {
        for (_alias, repo_id) in self.repos() {
            for run in self.runs(repo_id) {
                if run.state() == RunState::Finished && run.result() == Some(&RunResult::Failure) {
-
                    channel.item(Self::rss_item_from_run(run));
+
                    channel.item(Self::rss_item_from_run(run)?);
                }
            }
        }
        Ok(channel.build())
    }

-
    fn rss_item_from_run(run: &Run) -> Item {
+
    fn rss_item_from_run(run: &Run) -> Result<Item, PageError> {
        let mut guid = Guid::default();
        guid.set_value(run.broker_run_id().to_string());

@@ -576,16 +580,22 @@ impl PageData {
        };
        let title = format!("{state}: {} run {}", run.repo_alias(), run.broker_run_id());

+
        let ts = run.timestamp().to_string();
+
        let parsed =
+
            parse_timestamp(&ts).map_err(|err| PageError::RssTimestamp(ts.clone(), err))?;
+
        let ts = rfc822_timestamp(parsed).map_err(|err| PageError::RssTimestamp(ts, err))?;
+

        let mut item = ItemBuilder::default()
            .title(Some(title))
            .guid(Some(guid))
+
            .pub_date(Some(ts))
            .build();

        if let Some(url) = run.adapter_info_url() {
            item.set_link(Some(url.into()));
        };

-
        item
+
        Ok(item)
    }
}

modified src/util.rs
@@ -1,6 +1,11 @@
use std::str::FromStr;

-
use time::{macros::format_description, OffsetDateTime};
+
use time::{
+
    format_description::{well_known::Rfc2822, FormatItem},
+
    macros::format_description,
+
    parsing::Parsable,
+
    OffsetDateTime, PrimitiveDateTime,
+
};

use radicle::{
    prelude::{NodeId, RepoId},
@@ -88,6 +93,35 @@ pub fn now() -> Result<String, UtilError> {
        .map_err(UtilError::TimeFormat)
}

+
pub fn parse_timestamp(timestamp: &str) -> Result<OffsetDateTime, UtilError> {
+
    const SIMPLIFIED_ISO9601_WITH_Z: &[FormatItem<'static>] =
+
        format_description!("[year]-[month]-[day] [hour]:[minute]:[second]Z");
+

+
    fn parse_one(
+
        timestamp: &str,
+
        fmt: &(impl Parsable + ?Sized),
+
    ) -> Result<OffsetDateTime, time::error::Parse> {
+
        let r = PrimitiveDateTime::parse(timestamp, fmt);
+
        if let Ok(t) = r {
+
            Ok(t.assume_utc())
+
        } else {
+
            #[allow(clippy::unwrap_used)]
+
            Err(r.err().unwrap())
+
        }
+
    }
+

+
    if let Ok(t) = parse_one(timestamp, SIMPLIFIED_ISO9601_WITH_Z) {
+
        Ok(t)
+
    } else {
+
        Err(UtilError::TimestampParse(timestamp.into()))
+
    }
+
}
+

+
pub fn rfc822_timestamp(ts: OffsetDateTime) -> Result<String, UtilError> {
+
    let ts = ts.format(&Rfc2822).map_err(UtilError::TimeFormat)?;
+
    Ok(ts.to_string())
+
}
+

#[derive(Debug, thiserror::Error)]
pub enum UtilError {
    #[error("failed to look up node profile")]
@@ -116,4 +150,7 @@ pub enum UtilError {

    #[error("failed to format time stamp")]
    TimeFormat(#[source] time::error::Format),
+

+
    #[error("failed to parse timestamp {0:?}")]
+
    TimestampParse(String),
}