You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

79 lines
2.5 KiB
Rust

use json_ld::{
syntax::{Parse, Value},
Expand, IndexedObject, RemoteDocument,
};
use locspan::Span;
use rdf_types::vocabulary::BlankIdIndex;
use rdf_types::{vocabulary::IriIndex, IndexVocabulary, Vocabulary};
use crate::ap::{loader::CachedLoader, vocab::Ids};
use crate::core::*;
use crate::state::AppState;
/// Main API for handling ActivityPub ingress, called by [`crate::job::inbox::InboxWorker`].
pub async fn process_document(state: &AppState, raw: &str) -> Result<()> {
let mut vocab: IndexVocabulary = IndexVocabulary::new();
let indices = Ids::populate(&mut vocab);
let document = Value::parse_str(raw, |span| span)
.map_err(|e| Error::MalformedApub(format!("Could not parse document: {e}")))?;
let rd = RemoteDocument::new(
None,
Some("application/activity+json".parse().unwrap()),
document,
);
let mut loader = CachedLoader::new(state.clone());
let rd = rd.expand_with(&mut vocab, &mut loader).await.unwrap();
let vocab = vocab;
// this loop will usually only run once (one object per request)
for object in rd.into_value() {
if let Err(e) = process_object(object, &vocab, &indices).await {
error!("Error in remote document: {e}");
}
}
Ok(())
}
async fn process_object(
obj: IndexedObject<IriIndex, BlankIdIndex, Span>,
vocab: &impl Vocabulary<Iri = IriIndex>,
ids: &Ids,
) -> Result<()> {
let id = obj
.id()
.and_then(|id| id.as_iri())
.and_then(|index| vocab.iri(index))
.ok_or_else(apub_err("Object does not have an id"))?;
let typ = obj
.types()
.next()
.ok_or_else(apub_err("Object does not have a type"))?
.as_iri()
.and_then(|index| vocab.iri(index))
.ok_or_else(apub_err("Object has invalid type"))?;
// just for testing: extract and print `object.content`
// to see if the expansion actually works as expected
let properties = obj
.as_node()
.ok_or_else(apub_err("Object is not a node"))?
.properties();
let object = properties
.get_any(&ids.apub.property.object)
.and_then(|prop| prop.as_node())
.and_then(|node| node.properties().get_any(&ids.apub.property.content))
.and_then(|prop| prop.as_value())
.and_then(|val| val.as_str())
.unwrap();
debug!("content = \"{object}\"");
Ok(())
}
const fn apub_err(msg: impl Into<String>) -> impl FnOnce() -> Error {
move || Error::MalformedApub(msg.into())
}