use biscuit::CompactJson;
use monostate::MustBe;
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;
use crate::util::defaults::default_bool;
use crate::util::serde_date_time::SerdeDateTime;
use super::ToJsonLd;
use super::identity::Obj as Identity;
use super::verification::Obj as Verification;
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, TypedBuilder)]
#[serde(rename_all = "camelCase")]
#[serde(rename = "Assertion")]
pub struct Obj {
#[serde(rename = "@context")]
#[builder(default = MustBe!("https://w3id.org/openbadges/v2"))]
pub context: MustBe!("https://w3id.org/openbadges/v2"),
#[builder(default = MustBe!("Assertion"))]
pub r#type: MustBe!("Assertion"),
#[builder(setter(into))]
pub id: String,
#[builder(setter(into))]
pub badge: String,
pub recipient: Identity,
#[serde(alias = "verify")]
pub verification: Verification,
#[builder(setter(into))]
pub issued_on: SerdeDateTime,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
#[builder(setter(strip_option))]
#[builder(setter(into))]
pub image: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty")]
#[serde(default)]
#[builder(default)]
#[builder(setter(into))]
pub evidence: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
#[builder(setter(strip_option))]
#[builder(setter(into))]
pub narrative: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
#[builder(setter(strip_option))]
#[builder(setter(into))]
pub expires: Option<SerdeDateTime>,
#[serde(skip_serializing_if = "std::ops::Not::not")]
#[serde(default = "default_bool::<false>")]
#[builder(default = false)]
pub revoked: bool,
#[serde(skip_serializing_if = "Option::is_none")]
#[builder(default)]
#[builder(setter(strip_option))]
#[builder(setter(into))]
pub revocation_reason: Option<String>,
}
impl Obj {
pub fn new<S: Into<String>, D: Into<SerdeDateTime>>(
id: S,
badge: S,
recipient: Identity,
verification: Verification,
issued_on: D,
) -> Self {
Self {
context: MustBe!("https://w3id.org/openbadges/v2"),
r#type: MustBe!("Assertion"),
id: id.into(),
badge: badge.into(),
recipient,
verification,
issued_on: issued_on.into(),
image: None,
evidence: vec![],
narrative: None,
expires: None,
revoked: false,
revocation_reason: None,
}
}
}
impl CompactJson for Obj {}
impl ToJsonLd for Obj {}
#[cfg(test)]
mod tests {
use super::super::identity::Obj as Identity;
use super::super::identity::ObjType as IdentityType;
use super::super::verification::Obj as Verification;
use super::super::verification::ObjType as VerificationType;
use super::*;
use crate::constants;
use chrono::DateTime;
const EXP_JSON_LD_SIMPLE: &str = r#"{
"@context": "https://w3id.org/openbadges/v2",
"type": "Assertion",
"id": "https://raw.githubusercontent.com/hoijui/obadgen/master/res/ob-ents/badge-assertion-simple.json",
"badge": "https://raw.githubusercontent.com/hoijui/obadgen/master/res/ob-ents/badge-definition-simple.json",
"recipient": {
"type": "email",
"identity": "sha256$488842626ec74a0468d90ea17dc4e11c2d0e8e54e45c5075fbd1d2e767f44249",
"hashed": true
},
"verification": {
"type": "HostedBadge"
},
"issuedOn": "2022-06-17T23:59:59Z",
"expires": "2099-06-30T23:59:59Z"
}"#;
#[test]
fn test_new_hosted() -> Result<(), Box<dyn std::error::Error>> {
let mut obj = Obj::new(
constants::BADGE_ASSERTION_SIMPLE_ID,
constants::BADGE_DEFINITION_SIMPLE_ID,
Identity {
r#type: IdentityType::EMail,
hashed: true,
identity: constants::BADGE_ASSERTION_RECIPIENT_EMAIL_HASH_UNSALTED.clone(),
salt: None,
},
Verification::new(VerificationType::HostedBadge),
DateTime::parse_from_rfc3339(constants::DT_PAST)?,
);
obj.expires = Some(DateTime::parse_from_rfc3339(constants::DT_FAR_FUTURE)?.into());
let json_ld = obj.to_json_ld()?;
assert_eq!(&json_ld, EXP_JSON_LD_SIMPLE);
Ok(())
}
#[test]
fn test_builder_hosted() -> Result<(), Box<dyn std::error::Error>> {
let obj = Obj::builder()
.id(constants::BADGE_ASSERTION_SIMPLE_ID)
.badge(constants::BADGE_DEFINITION_SIMPLE_ID)
.recipient(
Identity::builder()
.r#type(IdentityType::EMail)
.hashed(true)
.identity(constants::BADGE_ASSERTION_RECIPIENT_EMAIL_HASH_UNSALTED.as_str())
.build(),
)
.verification(
Verification::builder()
.r#type(VerificationType::HostedBadge)
.build(),
)
.issued_on(DateTime::parse_from_rfc3339(constants::DT_PAST)?)
.expires(DateTime::parse_from_rfc3339(constants::DT_FAR_FUTURE)?)
.build();
let json_ld = obj.to_json_ld()?;
assert_eq!(&json_ld, EXP_JSON_LD_SIMPLE);
Ok(())
}
}