make sure 'OffsetDateTimeJsonSchemaWrapper' is serialised with legacy format
This commit is contained in:
Generated
+1
@@ -4017,6 +4017,7 @@ dependencies = [
|
||||
"nym-node-requests",
|
||||
"schemars",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tendermint",
|
||||
"time",
|
||||
"ts-rs",
|
||||
|
||||
@@ -27,6 +27,10 @@ nym-crypto = { path = "../../common/crypto", features = ["serde", "asymmetric"]
|
||||
nym-mixnet-contract-common = { path = "../../common/cosmwasm-smart-contracts/mixnet-contract" }
|
||||
nym-node-requests = { path = "../../nym-node/nym-node-requests", default-features = false }
|
||||
|
||||
|
||||
[dev-dependencies]
|
||||
serde_json.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
generate-ts = ["ts-rs", "nym-mixnet-contract-common/generate-ts"]
|
||||
|
||||
@@ -420,15 +420,110 @@ where
|
||||
Ok(time::serde::rfc3339::deserialize(deserializer).unwrap_or_else(|_| unix_epoch()))
|
||||
}
|
||||
|
||||
pub(crate) mod overengineered_offset_date_time_serde {
|
||||
use crate::models::unix_epoch;
|
||||
use serde::de::Visitor;
|
||||
use serde::ser::Error;
|
||||
use serde::{Deserializer, Serialize, Serializer};
|
||||
use std::fmt::Formatter;
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use time::format_description::{modifier, BorrowedFormatItem, Component};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
struct OffsetDateTimeVisitor;
|
||||
|
||||
// copied from time library because they keep it private -.-
|
||||
const DEFAULT_OFFSET_DATE_TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
|
||||
BorrowedFormatItem::Compound(DATE_FORMAT),
|
||||
BorrowedFormatItem::Literal(b" "),
|
||||
BorrowedFormatItem::Compound(TIME_FORMAT),
|
||||
BorrowedFormatItem::Literal(b" "),
|
||||
BorrowedFormatItem::Compound(UTC_OFFSET_FORMAT),
|
||||
];
|
||||
|
||||
const DATE_FORMAT: &[BorrowedFormatItem<'_>] = &[
|
||||
BorrowedFormatItem::Component(Component::Year(modifier::Year::default())),
|
||||
BorrowedFormatItem::Literal(b"-"),
|
||||
BorrowedFormatItem::Component(Component::Month(modifier::Month::default())),
|
||||
BorrowedFormatItem::Literal(b"-"),
|
||||
BorrowedFormatItem::Component(Component::Day(modifier::Day::default())),
|
||||
];
|
||||
|
||||
const TIME_FORMAT: &[BorrowedFormatItem<'_>] = &[
|
||||
BorrowedFormatItem::Component(Component::Hour(modifier::Hour::default())),
|
||||
BorrowedFormatItem::Literal(b":"),
|
||||
BorrowedFormatItem::Component(Component::Minute(modifier::Minute::default())),
|
||||
BorrowedFormatItem::Literal(b":"),
|
||||
BorrowedFormatItem::Component(Component::Second(modifier::Second::default())),
|
||||
BorrowedFormatItem::Literal(b"."),
|
||||
BorrowedFormatItem::Component(Component::Subsecond(modifier::Subsecond::default())),
|
||||
];
|
||||
|
||||
const UTC_OFFSET_FORMAT: &[BorrowedFormatItem<'_>] = &[
|
||||
BorrowedFormatItem::Component(Component::OffsetHour({
|
||||
let mut m = modifier::OffsetHour::default();
|
||||
m.sign_is_mandatory = true;
|
||||
m
|
||||
})),
|
||||
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
|
||||
BorrowedFormatItem::Literal(b":"),
|
||||
BorrowedFormatItem::Component(Component::OffsetMinute(
|
||||
modifier::OffsetMinute::default(),
|
||||
)),
|
||||
BorrowedFormatItem::Optional(&BorrowedFormatItem::Compound(&[
|
||||
BorrowedFormatItem::Literal(b":"),
|
||||
BorrowedFormatItem::Component(Component::OffsetSecond(
|
||||
modifier::OffsetSecond::default(),
|
||||
)),
|
||||
])),
|
||||
])),
|
||||
];
|
||||
|
||||
impl<'de> Visitor<'de> for OffsetDateTimeVisitor {
|
||||
type Value = OffsetDateTime;
|
||||
|
||||
fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("an rfc3339 or human-readable `OffsetDateTime`")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
// first try rfc3339, if that fails use default human-readable impl from time,
|
||||
// finally fallback to default unix epoch
|
||||
Ok(OffsetDateTime::parse(v, &Rfc3339).unwrap_or_else(|_| {
|
||||
OffsetDateTime::parse(v, &DEFAULT_OFFSET_DATE_TIME_FORMAT)
|
||||
.unwrap_or_else(|_| unix_epoch())
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<OffsetDateTime, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_str(OffsetDateTimeVisitor)
|
||||
}
|
||||
|
||||
pub(crate) fn serialize<S>(datetime: &OffsetDateTime, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
// serialize it with human-readable format for compatibility with eclipse and nutella clients
|
||||
// in the future change it back to rfc3339
|
||||
datetime
|
||||
.format(&DEFAULT_OFFSET_DATE_TIME_FORMAT)
|
||||
.map_err(S::Error::custom)?
|
||||
.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
// for all intents and purposes it's just OffsetDateTime, but we need JsonSchema...
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct OffsetDateTimeJsonSchemaWrapper(
|
||||
#[serde(
|
||||
default = "unix_epoch",
|
||||
serialize_with = "time::serde::rfc3339::serialize",
|
||||
deserialize_with = "de_rfc3339_or_default"
|
||||
)]
|
||||
pub OffsetDateTime,
|
||||
#[serde(default = "unix_epoch", with = "overengineered_offset_date_time_serde")]
|
||||
pub OffsetDateTime,
|
||||
);
|
||||
|
||||
impl Default for OffsetDateTimeJsonSchemaWrapper {
|
||||
@@ -599,3 +694,33 @@ pub struct PartialTestResult {
|
||||
|
||||
pub type MixnodeTestResultResponse = PaginatedResponse<PartialTestResult>;
|
||||
pub type GatewayTestResultResponse = PaginatedResponse<PartialTestResult>;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn offset_date_time_json_schema_wrapper_serde_backwards_compat() {
|
||||
let mut dummy = OffsetDateTimeJsonSchemaWrapper::default();
|
||||
dummy.0 += Duration::from_millis(1);
|
||||
let ser = serde_json::to_string(&dummy).unwrap();
|
||||
|
||||
assert_eq!("\"1970-01-01 00:00:00.001 +00:00:00\"", ser);
|
||||
|
||||
let human_readable = "\"2024-05-23 07:41:02.756283766 +00:00:00\"";
|
||||
let rfc3339 = "\"2002-10-02T15:00:00Z\"";
|
||||
let rfc3339_offset = "\"2002-10-02T10:00:00-05:00\"";
|
||||
|
||||
let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>(human_readable).unwrap();
|
||||
assert_eq!(de.0.unix_timestamp(), 1716450062);
|
||||
|
||||
let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>(rfc3339).unwrap();
|
||||
assert_eq!(de.0.unix_timestamp(), 1033570800);
|
||||
|
||||
let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>(rfc3339_offset).unwrap();
|
||||
assert_eq!(de.0.unix_timestamp(), 1033570800);
|
||||
|
||||
let de = serde_json::from_str::<OffsetDateTimeJsonSchemaWrapper>("\"nonsense\"").unwrap();
|
||||
assert_eq!(de.0.unix_timestamp(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user