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
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())
|
|
}
|