]> Untitled Git - lemmy.git/commitdiff
improved community federation (wip)
authorFelix Ableitner <me@nutomic.com>
Wed, 11 Mar 2020 11:29:10 +0000 (12:29 +0100)
committerFelix Ableitner <me@nutomic.com>
Wed, 11 Mar 2020 11:29:10 +0000 (12:29 +0100)
server/src/apub/community.rs
server/src/apub/group_wrapper.rs [new file with mode: 0644]
server/src/apub/mod.rs
server/src/apub/puller.rs
server/src/websocket/server.rs

index ee3edc1f4a1db89c59ab952b013b30ad6c453e95..2a16eeae959b24f48d727b8123945f25c27c3355 100644 (file)
@@ -1,14 +1,14 @@
+use crate::apub::group_wrapper::GroupHelper;
 use crate::apub::make_apub_endpoint;
 use crate::db::community::Community;
 use crate::db::community_view::CommunityFollowerView;
 use crate::db::establish_unpooled_connection;
-use crate::to_datetime_utc;
 use activitypub::{actor::Group, collection::UnorderedCollection, context};
 use actix_web::body::Body;
 use actix_web::web::Path;
 use actix_web::HttpResponse;
 use serde::Deserialize;
-use serde_json::json;
+use serde_json::{Value};
 
 impl Community {
   pub fn as_group(&self) -> Group {
@@ -16,42 +16,18 @@ impl Community {
 
     let mut group = Group::default();
 
-    // TODO: why the hell is this code so awkward?
     group.object_props.set_context_object(context()).ok();
-    // TODO: id really needs to be a url
-    group.object_props.set_id_string(self.id.to_string()).ok();
-    group
-      .object_props
-      .set_name_string(self.title.to_owned())
-      .ok();
-    group
-      .object_props
-      .set_published_utctime(to_datetime_utc(self.published))
-      .ok();
-    group.object_props.attributed_to = Some(json!(self.creator_id.to_string()));
-    if let Some(updated) = self.updated {
-      group
-        .object_props
-        .set_updated_utctime(to_datetime_utc(updated))
-        .ok();
-    }
+    Group::set_id(&mut group, self.id);
+    Group::set_title(&mut group, &self.title);
+    Group::set_published(&mut group, self.published);
+    Group::set_updated(&mut group, self.updated);
+    Group::set_creator_id(&mut group, self.creator_id);
 
-    if let Some(description) = &self.description {
-      group.object_props.summary = Some(json!(description.to_string()));
-    }
+    Group::set_description(&mut group, &self.description);
 
-    group
-      .ap_actor_props
-      .set_inbox_string(format!("{}/inbox", &base_url))
-      .ok();
-    group
-      .ap_actor_props
-      .set_outbox_string(format!("{}/outbox", &base_url))
-      .ok();
-    group
-      .ap_actor_props
-      .set_followers_string(format!("{}/followers", &base_url))
-      .ok();
+    group.ap_actor_props.inbox = Value::String(format!("{}/inbox", &base_url));
+    group.ap_actor_props.outbox = Value::String(format!("{}/outbox", &base_url));
+    group.ap_actor_props.followers = Some(Value::String(format!("{}/followers", &base_url)));
 
     group
   }
@@ -65,7 +41,6 @@ impl Community {
 
     let connection = establish_unpooled_connection();
     //As we are an object, we validated that the community id was valid
-    // TODO: add a method that only returns count for better performance
     let community_followers = CommunityFollowerView::for_community(&connection, self.id).unwrap();
 
     collection
diff --git a/server/src/apub/group_wrapper.rs b/server/src/apub/group_wrapper.rs
new file mode 100644 (file)
index 0000000..dd140df
--- /dev/null
@@ -0,0 +1,91 @@
+use crate::to_datetime_utc;
+use activitypub::actor::Group;
+use chrono::{DateTime, NaiveDateTime};
+use failure::Error;
+use serde_json::Value;
+
+pub trait GroupHelper {
+  // TODO: id really needs to be a url
+  fn set_id(group: &mut Group, id: i32);
+  fn get_id(group: &Group) -> Result<i32, Error>;
+
+  fn set_title(group: &mut Group, title: &str);
+  fn get_title(group: &Group) -> Result<String, Error>;
+
+  fn set_description(group: &mut Group, description: &Option<String>);
+  fn get_description(group: &Group) -> Result<Option<String>, Error>;
+
+  // TODO: also needs to be changed to url
+  fn set_creator_id(group: &mut Group, creator_id: i32);
+  fn get_creator_id(group: &Group) -> Result<i32, Error>;
+
+  fn set_published(group: &mut Group, published: NaiveDateTime);
+  fn get_published(group: &Group) -> Result<NaiveDateTime, Error>;
+
+  fn set_updated(group: &mut Group, updated: Option<NaiveDateTime>);
+  fn get_updated(group: &Group) -> Result<Option<NaiveDateTime>, Error>;
+}
+
+// TODO: something is crashing and not reporting the error
+impl GroupHelper for Group {
+  fn set_id(group: &mut Group, id: i32) {
+    group.object_props.id = Some(Value::String(id.to_string()));
+  }
+  fn get_id(group: &Group) -> Result<i32, Error> {
+    Ok(get_string_value(group.clone().object_props.id).parse::<i32>()?)
+  }
+
+  fn set_title(group: &mut Group, title: &str) {
+    group.object_props.name = Some(Value::String(title.to_string()));
+  }
+  fn get_title(group: &Group) -> Result<String, Error> {
+    Ok(get_string_value(group.to_owned().object_props.name))
+  }
+
+  fn set_description(group: &mut Group, description: &Option<String>) {
+    group.object_props.summary = description.as_ref().map(|d| Value::String(d.to_string()));
+  }
+  fn get_description(group: &Group) -> Result<Option<String>, Error> {
+    Ok(get_string_value_opt(group.to_owned().object_props.summary))
+  }
+
+  fn set_creator_id(group: &mut Group, creator_id: i32) {
+    group.object_props.attributed_to = Some(Value::String(creator_id.to_string()));
+  }
+  fn get_creator_id(group: &Group) -> Result<i32, Error> {
+    Ok(get_string_value(group.clone().object_props.attributed_to).parse::<i32>()?)
+  }
+
+  fn set_published(group: &mut Group, published: NaiveDateTime) {
+    group.object_props.published = Some(Value::String(to_datetime_utc(published).to_string()))
+  }
+  fn get_published(group: &Group) -> Result<NaiveDateTime, Error> {
+    let str = get_string_value(group.to_owned().object_props.published);
+    // TODO: no idea which date format
+    let date = DateTime::parse_from_rfc2822(&str)?;
+    Ok(date.naive_local())
+  }
+
+  fn set_updated(group: &mut Group, updated: Option<NaiveDateTime>) {
+    group.object_props.updated = updated.map(|u| Value::String(u.to_string()));
+  }
+  fn get_updated(group: &Group) -> Result<Option<NaiveDateTime>, Error> {
+    let str = get_string_value_opt(group.to_owned().object_props.updated);
+    match str {
+      Some(s) => Ok(Some(DateTime::parse_from_rfc2822(&s)?.naive_local())),
+      None => Ok(None),
+    }
+  }
+}
+
+fn get_string_value_opt(value: Option<Value>) -> Option<String> {
+  value
+    .as_ref()
+    .map(Value::as_str)
+    .flatten()
+    .map(str::to_string)
+}
+
+fn get_string_value(value: Option<Value>) -> String {
+  get_string_value_opt(value).unwrap()
+}
index 9bac64a6e6efad57f821bf5d9fd56089eab52404..31dc3cedb0bcff52c34ae333a3ab0b9f53ddd50b 100644 (file)
@@ -1,4 +1,5 @@
 pub mod community;
+pub mod group_wrapper;
 pub mod post;
 pub mod puller;
 pub mod user;
index b6877982052e6f1bf78debfd48152d39de0b3e9c..ce9469fccb1e1b0296cdb98203075e61d3e5ef44 100644 (file)
@@ -3,11 +3,10 @@ extern crate reqwest;
 use self::reqwest::Error;
 use crate::api::community::{GetCommunityResponse, ListCommunitiesResponse};
 use crate::api::post::GetPosts;
+use crate::apub::group_wrapper::GroupHelper;
 use crate::db::community_view::CommunityView;
-use crate::naive_now;
 use crate::settings::Settings;
 use activitypub::actor::Group;
-use serde_json::Value;
 
 // TODO: right now all of the data is requested on demand, for production we will need to store
 //       things in the local database to not ruin the performance
@@ -43,22 +42,20 @@ pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse,
 
   // TODO: looks like a bunch of data is missing from the activitypub response
   // TODO: i dont think simple numeric ids are going to work, we probably need something like uuids
-  // TODO: why are the Group properties not typed?
   Ok(GetCommunityResponse {
     moderators: vec![],
     admins: vec![],
     community: CommunityView {
-      // TODO: why does the stupid library have everything stored as value without working autocomplete for methods???
       // TODO: we need to merge id and name into a single thing (stuff like @user@instance.com)
-      id: get_string_value(community.object_props.id).parse::<i32>()?,
+      id: Group::get_id(&community)?,
       name,
-      title: get_string_value(community.object_props.name),
-      description: get_string_value_opt(community.object_props.summary),
+      title: Group::get_title(&community)?,
+      description: Group::get_description(&community)?,
       category_id: -1,
-      creator_id: get_string_value(community.object_props.attributed_to).parse::<i32>()?,
+      creator_id: Group::get_creator_id(&community)?,
       removed: false,
-      published: naive_now(), // TODO: need to handle time conversion (or handle it in apub lib)
-      updated: Some(naive_now()), // TODO: community.object_props.updated
+      published: Group::get_published(&community)?,
+      updated: Group::get_updated(&community)?,
       deleted: false,
       nsfw: false,
       creator_name: "".to_string(),
@@ -69,24 +66,12 @@ pub fn get_remote_community(identifier: String) -> Result<GetCommunityResponse,
       number_of_comments: -1,
       hot_rank: -1,
       user_id: None,
-      subscribed: None, // TODO: looks like lemmy uses None/true for this value
+      subscribed: None,
     },
     online: 0,
   })
 }
 
-fn get_string_value_opt(value: Option<Value>) -> Option<String> {
-  value
-    .as_ref()
-    .map(Value::as_str)
-    .flatten()
-    .map(str::to_string)
-}
-
-fn get_string_value(value: Option<Value>) -> String {
-  get_string_value_opt(value).unwrap()
-}
-
 pub fn get_following_instances() -> Result<Vec<String>, Error> {
   let instance_list = match Settings::get().federated_instance.clone() {
     Some(f) => vec![f, Settings::get().hostname.clone()],
index 76a55540f887ea2b5fad8b444e48dcdaeb52d0d8..66b10cd91b7b37d50d6e669ffa9a51467117d00e 100644 (file)
@@ -557,8 +557,6 @@ fn parse_json_message(chat: &mut ChatServer, msg: StandardMessage) -> Result<Str
           if community_name.contains('@') {
             // TODO: need to support sort, filter etc for remote communities
             get_remote_community(community_name)?
-          // TODO what is this about
-          // get_community.name = Some(name.replace("!", ""));
           } else {
             Oper::new(get_community).perform(&conn)?
           }