watchlist backfill
This commit is contained in:
@@ -18,7 +18,9 @@ impl ApObjectHandler for CompositeObjectHandler {
|
||||
&self,
|
||||
user_id: uuid::Uuid,
|
||||
) -> anyhow::Result<Vec<(Url, serde_json::Value)>> {
|
||||
self.review.get_local_objects_for_user(user_id).await
|
||||
let mut results = self.review.get_local_objects_for_user(user_id).await?;
|
||||
results.extend(self.watchlist.get_local_objects_for_user(user_id).await?);
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
async fn get_local_objects_page(
|
||||
|
||||
@@ -3,7 +3,7 @@ use domain::ports::EventHandler;
|
||||
use domain::{
|
||||
errors::DomainError,
|
||||
events::DomainEvent,
|
||||
ports::{MovieRepository, ReviewRepository},
|
||||
ports::LocalApContentQuery,
|
||||
value_objects::{ReviewId, UserId},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
@@ -15,22 +15,19 @@ use crate::urls::{actor_url, review_url};
|
||||
|
||||
pub struct ActivityPubEventHandler {
|
||||
ap_service: Arc<ActivityPubService>,
|
||||
movie_repository: Arc<dyn MovieRepository>,
|
||||
review_repository: Arc<dyn ReviewRepository>,
|
||||
content_query: Arc<dyn LocalApContentQuery>,
|
||||
base_url: String,
|
||||
}
|
||||
|
||||
impl ActivityPubEventHandler {
|
||||
pub fn new(
|
||||
ap_service: Arc<ActivityPubService>,
|
||||
movie_repository: Arc<dyn MovieRepository>,
|
||||
review_repository: Arc<dyn ReviewRepository>,
|
||||
content_query: Arc<dyn LocalApContentQuery>,
|
||||
base_url: String,
|
||||
) -> Self {
|
||||
Self {
|
||||
ap_service,
|
||||
movie_repository,
|
||||
review_repository,
|
||||
content_query,
|
||||
base_url,
|
||||
}
|
||||
}
|
||||
@@ -90,7 +87,7 @@ impl EventHandler for ActivityPubEventHandler {
|
||||
|
||||
impl ActivityPubEventHandler {
|
||||
async fn on_review_logged(&self, user_id: &UserId, review_id: &ReviewId) -> anyhow::Result<()> {
|
||||
let review = match self.review_repository.get_review_by_id(review_id).await? {
|
||||
let review = match self.content_query.get_review_by_id(review_id).await? {
|
||||
Some(r) => r,
|
||||
None => return Ok(()),
|
||||
};
|
||||
@@ -99,7 +96,7 @@ impl ActivityPubEventHandler {
|
||||
let actor = actor_url(&self.base_url, user_id.value());
|
||||
|
||||
let movie = self
|
||||
.movie_repository
|
||||
.content_query
|
||||
.get_movie_by_id(review.movie_id())
|
||||
.await
|
||||
.ok()
|
||||
@@ -108,10 +105,7 @@ impl ActivityPubEventHandler {
|
||||
.as_ref()
|
||||
.map(|m| m.title().value().to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
let release_year = movie
|
||||
.as_ref()
|
||||
.map(|m| m.release_year().value())
|
||||
.unwrap_or(0);
|
||||
let release_year = movie.as_ref().map(|m| m.release_year().value()).unwrap_or(0);
|
||||
let poster_url = movie
|
||||
.as_ref()
|
||||
.and_then(|m| m.poster_path())
|
||||
@@ -140,7 +134,7 @@ impl ActivityPubEventHandler {
|
||||
user_id: &UserId,
|
||||
review_id: &ReviewId,
|
||||
) -> anyhow::Result<()> {
|
||||
let review = match self.review_repository.get_review_by_id(review_id).await? {
|
||||
let review = match self.content_query.get_review_by_id(review_id).await? {
|
||||
Some(r) => r,
|
||||
None => return Ok(()),
|
||||
};
|
||||
@@ -149,7 +143,7 @@ impl ActivityPubEventHandler {
|
||||
let actor = actor_url(&self.base_url, user_id.value());
|
||||
|
||||
let movie = self
|
||||
.movie_repository
|
||||
.content_query
|
||||
.get_movie_by_id(review.movie_id())
|
||||
.await
|
||||
.ok()
|
||||
@@ -158,10 +152,7 @@ impl ActivityPubEventHandler {
|
||||
.as_ref()
|
||||
.map(|m| m.title().value().to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
let release_year = movie
|
||||
.as_ref()
|
||||
.map(|m| m.release_year().value())
|
||||
.unwrap_or(0);
|
||||
let release_year = movie.as_ref().map(|m| m.release_year().value()).unwrap_or(0);
|
||||
let poster_url = movie
|
||||
.as_ref()
|
||||
.and_then(|m| m.poster_path())
|
||||
@@ -211,7 +202,7 @@ impl ActivityPubEventHandler {
|
||||
let actor = actor_url(&self.base_url, user_id.value());
|
||||
|
||||
let poster_url = self
|
||||
.movie_repository
|
||||
.content_query
|
||||
.get_movie_by_id(movie_id)
|
||||
.await
|
||||
.ok()
|
||||
|
||||
@@ -30,22 +30,21 @@ pub async fn wire(
|
||||
federation_repo: std::sync::Arc<dyn FederationRepository>,
|
||||
review_store: std::sync::Arc<dyn RemoteReviewRepository>,
|
||||
remote_watchlist_repo: std::sync::Arc<dyn domain::ports::RemoteWatchlistRepository>,
|
||||
local_ap_content: std::sync::Arc<dyn domain::ports::LocalApContentQuery>,
|
||||
user_repo: std::sync::Arc<dyn domain::ports::UserRepository>,
|
||||
movie_repo: std::sync::Arc<dyn domain::ports::MovieRepository>,
|
||||
review_repo: std::sync::Arc<dyn domain::ports::ReviewRepository>,
|
||||
diary_repo: std::sync::Arc<dyn domain::ports::DiaryRepository>,
|
||||
base_url: String,
|
||||
allow_registration: bool,
|
||||
_event_publisher: std::sync::Arc<dyn domain::ports::EventPublisher>,
|
||||
) -> anyhow::Result<ActivityPubWire> {
|
||||
let review_handler = std::sync::Arc::new(ReviewObjectHandler {
|
||||
movie_repository: std::sync::Arc::clone(&movie_repo),
|
||||
diary_repository: std::sync::Arc::clone(&diary_repo),
|
||||
content_query: std::sync::Arc::clone(&local_ap_content),
|
||||
review_store,
|
||||
base_url: base_url.clone(),
|
||||
});
|
||||
let watchlist_handler = std::sync::Arc::new(watchlist_handler::WatchlistObjectHandler {
|
||||
remote_watchlist_repo,
|
||||
content_query: std::sync::Arc::clone(&local_ap_content),
|
||||
base_url: base_url.clone(),
|
||||
});
|
||||
let composite = std::sync::Arc::new(composite_handler::CompositeObjectHandler {
|
||||
review: review_handler,
|
||||
@@ -80,8 +79,7 @@ pub async fn wire(
|
||||
let router = concrete.router();
|
||||
let event_handler = std::sync::Arc::new(ActivityPubEventHandler::new(
|
||||
std::sync::Arc::clone(&concrete),
|
||||
movie_repo,
|
||||
review_repo,
|
||||
local_ap_content,
|
||||
base_url,
|
||||
)) as std::sync::Arc<dyn domain::ports::EventHandler>;
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::sync::Arc;
|
||||
use k_ap::ApObjectHandler;
|
||||
use async_trait::async_trait;
|
||||
use domain::{
|
||||
models::{Review, ReviewSource},
|
||||
ports::{DiaryRepository, MovieRepository},
|
||||
models::ReviewSource,
|
||||
ports::LocalApContentQuery,
|
||||
value_objects::{Comment, MovieId, Rating, ReviewId, UserId},
|
||||
};
|
||||
use url::Url;
|
||||
@@ -14,8 +14,7 @@ use crate::remote_review_repository::RemoteReviewRepository;
|
||||
use crate::urls::{actor_url, review_url};
|
||||
|
||||
pub struct ReviewObjectHandler {
|
||||
pub movie_repository: Arc<dyn MovieRepository>,
|
||||
pub diary_repository: Arc<dyn DiaryRepository>,
|
||||
pub content_query: Arc<dyn LocalApContentQuery>,
|
||||
pub review_store: Arc<dyn RemoteReviewRepository>,
|
||||
pub base_url: String,
|
||||
}
|
||||
@@ -27,51 +26,33 @@ impl ApObjectHandler for ReviewObjectHandler {
|
||||
user_id: uuid::Uuid,
|
||||
) -> anyhow::Result<Vec<(Url, serde_json::Value)>> {
|
||||
let domain_user_id = UserId::from_uuid(user_id);
|
||||
let history = self
|
||||
.diary_repository
|
||||
.get_user_history(&domain_user_id)
|
||||
.await?;
|
||||
let entries = self
|
||||
.content_query
|
||||
.get_local_reviews_for_user(&domain_user_id)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
||||
|
||||
let actor = actor_url(&self.base_url, user_id);
|
||||
let mut results = Vec::new();
|
||||
for entry in history {
|
||||
for entry in entries {
|
||||
let review = entry.review();
|
||||
if !matches!(review.source(), ReviewSource::Local) {
|
||||
continue;
|
||||
}
|
||||
let movie = entry.movie();
|
||||
|
||||
let ap_id = review_url(&self.base_url, review.id());
|
||||
let actor_url = actor_url(&self.base_url, user_id);
|
||||
|
||||
let movie = self
|
||||
.movie_repository
|
||||
.get_movie_by_id(review.movie_id())
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
let movie_title = movie
|
||||
.as_ref()
|
||||
.map(|m| m.title().value().to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
let release_year = movie
|
||||
.as_ref()
|
||||
.map(|m| m.release_year().value())
|
||||
.unwrap_or(0);
|
||||
let poster_url = movie
|
||||
.as_ref()
|
||||
.and_then(|m| m.poster_path())
|
||||
.poster_path()
|
||||
.map(|p| format!("{}/images/{}", self.base_url, p.value()));
|
||||
|
||||
let obj = review_to_ap_object(
|
||||
review,
|
||||
ap_id.clone(),
|
||||
actor_url,
|
||||
movie_title,
|
||||
release_year,
|
||||
actor.clone(),
|
||||
movie.title().value().to_string(),
|
||||
movie.release_year().value(),
|
||||
poster_url,
|
||||
&self.base_url,
|
||||
);
|
||||
let json = serde_json::to_value(obj)?;
|
||||
results.push((ap_id, json));
|
||||
results.push((ap_id, serde_json::to_value(obj)?));
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
@@ -82,23 +63,18 @@ impl ApObjectHandler for ReviewObjectHandler {
|
||||
before: Option<chrono::DateTime<chrono::Utc>>,
|
||||
limit: usize,
|
||||
) -> anyhow::Result<Vec<(url::Url, serde_json::Value, chrono::DateTime<chrono::Utc>)>> {
|
||||
use domain::value_objects::UserId;
|
||||
|
||||
let domain_user_id = UserId::from_uuid(user_id);
|
||||
let history = self
|
||||
.diary_repository
|
||||
.get_user_history(&domain_user_id)
|
||||
.await?;
|
||||
let entries = self
|
||||
.content_query
|
||||
.get_local_reviews_for_user(&domain_user_id)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
||||
|
||||
let actor = actor_url(&self.base_url, user_id);
|
||||
let mut results = Vec::new();
|
||||
for entry in history {
|
||||
for entry in entries {
|
||||
let review = entry.review();
|
||||
if !matches!(review.source(), ReviewSource::Local) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let published =
|
||||
chrono::DateTime::from_naive_utc_and_offset(*review.watched_at(), chrono::Utc);
|
||||
let published = chrono::DateTime::from_naive_utc_and_offset(*review.watched_at(), chrono::Utc);
|
||||
|
||||
if let Some(cutoff) = before
|
||||
&& published >= cutoff
|
||||
@@ -106,39 +82,22 @@ impl ApObjectHandler for ReviewObjectHandler {
|
||||
continue;
|
||||
}
|
||||
|
||||
let movie = entry.movie();
|
||||
let ap_id = review_url(&self.base_url, review.id());
|
||||
let actor_url = actor_url(&self.base_url, user_id);
|
||||
|
||||
let movie = self
|
||||
.movie_repository
|
||||
.get_movie_by_id(review.movie_id())
|
||||
.await
|
||||
.ok()
|
||||
.flatten();
|
||||
let movie_title = movie
|
||||
.as_ref()
|
||||
.map(|m| m.title().value().to_string())
|
||||
.unwrap_or_else(|| "Unknown".to_string());
|
||||
let release_year = movie
|
||||
.as_ref()
|
||||
.map(|m| m.release_year().value())
|
||||
.unwrap_or(0);
|
||||
let poster_url = movie
|
||||
.as_ref()
|
||||
.and_then(|m| m.poster_path())
|
||||
.poster_path()
|
||||
.map(|p| format!("{}/images/{}", self.base_url, p.value()));
|
||||
|
||||
let obj = review_to_ap_object(
|
||||
review,
|
||||
ap_id.clone(),
|
||||
actor_url,
|
||||
movie_title,
|
||||
release_year,
|
||||
actor.clone(),
|
||||
movie.title().value().to_string(),
|
||||
movie.release_year().value(),
|
||||
poster_url,
|
||||
&self.base_url,
|
||||
);
|
||||
let json = serde_json::to_value(obj)?;
|
||||
results.push((ap_id, json, published));
|
||||
results.push((ap_id, serde_json::to_value(obj)?, published));
|
||||
|
||||
if results.len() >= limit {
|
||||
break;
|
||||
@@ -174,7 +133,7 @@ impl ApObjectHandler for ReviewObjectHandler {
|
||||
let rating = Rating::new(obj.rating.min(5))?;
|
||||
let comment = obj.comment.map(Comment::new).transpose()?;
|
||||
|
||||
let review = Review::from_persistence(
|
||||
let review = domain::models::Review::from_persistence(
|
||||
review_id,
|
||||
movie_id,
|
||||
user_id,
|
||||
@@ -242,7 +201,7 @@ impl ApObjectHandler for ReviewObjectHandler {
|
||||
}
|
||||
|
||||
async fn count_local_posts(&self) -> anyhow::Result<u64> {
|
||||
self.diary_repository
|
||||
self.content_query
|
||||
.count_local_posts()
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e.to_string()))
|
||||
|
||||
@@ -3,22 +3,61 @@ use std::sync::Arc;
|
||||
use k_ap::ApObjectHandler;
|
||||
use async_trait::async_trait;
|
||||
use chrono::Utc;
|
||||
use domain::{models::RemoteWatchlistEntry, ports::RemoteWatchlistRepository};
|
||||
use domain::{
|
||||
models::RemoteWatchlistEntry,
|
||||
ports::{LocalApContentQuery, RemoteWatchlistRepository},
|
||||
value_objects::UserId,
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
use crate::objects::WatchlistObject;
|
||||
use crate::{objects::{WatchlistObject, watchlist_to_ap_object}, urls::{actor_url, watchlist_entry_url}};
|
||||
|
||||
pub struct WatchlistObjectHandler {
|
||||
pub remote_watchlist_repo: Arc<dyn RemoteWatchlistRepository>,
|
||||
pub content_query: Arc<dyn LocalApContentQuery>,
|
||||
pub base_url: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ApObjectHandler for WatchlistObjectHandler {
|
||||
async fn get_local_objects_for_user(
|
||||
&self,
|
||||
_user_id: uuid::Uuid,
|
||||
user_id: uuid::Uuid,
|
||||
) -> anyhow::Result<Vec<(Url, serde_json::Value)>> {
|
||||
Ok(vec![])
|
||||
let domain_user_id = UserId::from_uuid(user_id);
|
||||
let entries = self
|
||||
.content_query
|
||||
.get_local_watchlist_for_user(&domain_user_id)
|
||||
.await
|
||||
.map_err(|e| anyhow::anyhow!(e.to_string()))?;
|
||||
|
||||
let actor = actor_url(&self.base_url, user_id);
|
||||
let mut results = Vec::new();
|
||||
for wm in entries {
|
||||
let movie_id = wm.entry.movie_id.value();
|
||||
let ap_id = watchlist_entry_url(&self.base_url, user_id, movie_id);
|
||||
let added_at = chrono::DateTime::from_naive_utc_and_offset(wm.entry.added_at, Utc);
|
||||
let external_metadata_id = wm
|
||||
.movie
|
||||
.external_metadata_id()
|
||||
.map(|id| id.value().to_string());
|
||||
let poster_url = wm
|
||||
.movie
|
||||
.poster_path()
|
||||
.map(|p| format!("{}/images/{}", self.base_url, p.value()));
|
||||
let obj = watchlist_to_ap_object(
|
||||
ap_id.clone(),
|
||||
actor.clone(),
|
||||
wm.movie.title().value().to_string(),
|
||||
wm.movie.release_year().value(),
|
||||
external_metadata_id,
|
||||
poster_url,
|
||||
added_at,
|
||||
&self.base_url,
|
||||
);
|
||||
results.push((ap_id, serde_json::to_value(obj)?));
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
async fn get_local_objects_page(
|
||||
|
||||
Reference in New Issue
Block a user