- Replace PosterStorage with generic ImageStorage port (IMAGE_STORAGE_BACKEND/PATH env vars)
- Rename poster-storage crate to image-storage; serve at /images/{*key}
- Add bio and avatar_path to User model (migration 0009_user_profile)
- update_profile use case with avatar upload, mime validation, old avatar cleanup
- GET/PUT /api/v1/profile and GET/POST /settings/profile HTML page
- Enrich AP Person actor with summary, icon, url, discoverable fields
- Store remote actor avatar_url (migration 0010_ap_remote_actor_avatar)
- Shared inbox delivery via collect_inboxes deduplication
- Broadcast Update(Person) to followers on UserUpdated event
- Paginated outbox: OrderedCollection + OrderedCollectionPage with cursor
- Announce/boost tracking in ap_announces table (migration 0011_ap_announces)
79 lines
2.4 KiB
Rust
79 lines
2.4 KiB
Rust
use domain::events::DomainEvent;
|
|
|
|
pub fn event_to_subject(prefix: &str, event: &DomainEvent) -> String {
|
|
let suffix = match event {
|
|
DomainEvent::ReviewLogged { .. } => "review.logged",
|
|
DomainEvent::ReviewUpdated { .. } => "review.updated",
|
|
DomainEvent::MovieDiscovered { .. } => "movie.discovered",
|
|
DomainEvent::MovieDeleted { .. } => "movie.deleted",
|
|
DomainEvent::UserUpdated { .. } => "user.updated",
|
|
};
|
|
format!("{prefix}.{suffix}")
|
|
}
|
|
|
|
pub fn consumer_subject_filter(prefix: &str) -> String {
|
|
format!("{prefix}.>")
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use chrono::NaiveDateTime;
|
|
use domain::value_objects::{ExternalMetadataId, MovieId, Rating, ReviewId, UserId};
|
|
use uuid::Uuid;
|
|
|
|
fn dt() -> NaiveDateTime {
|
|
chrono::DateTime::from_timestamp(1_700_000_000, 0).unwrap().naive_utc()
|
|
}
|
|
|
|
#[test]
|
|
fn review_logged_subject() {
|
|
let event = DomainEvent::ReviewLogged {
|
|
review_id: ReviewId::from_uuid(Uuid::new_v4()),
|
|
movie_id: MovieId::from_uuid(Uuid::new_v4()),
|
|
user_id: UserId::from_uuid(Uuid::new_v4()),
|
|
rating: Rating::new(3).unwrap(),
|
|
watched_at: dt(),
|
|
};
|
|
assert_eq!(
|
|
event_to_subject("movies-diary.events", &event),
|
|
"movies-diary.events.review.logged"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn review_updated_subject() {
|
|
let event = DomainEvent::ReviewUpdated {
|
|
review_id: ReviewId::from_uuid(Uuid::new_v4()),
|
|
movie_id: MovieId::from_uuid(Uuid::new_v4()),
|
|
user_id: UserId::from_uuid(Uuid::new_v4()),
|
|
rating: Rating::new(5).unwrap(),
|
|
watched_at: dt(),
|
|
};
|
|
assert_eq!(
|
|
event_to_subject("movies-diary.events", &event),
|
|
"movies-diary.events.review.updated"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn movie_discovered_subject() {
|
|
let event = DomainEvent::MovieDiscovered {
|
|
movie_id: MovieId::from_uuid(Uuid::new_v4()),
|
|
external_metadata_id: ExternalMetadataId::new("tt0000001".into()).unwrap(),
|
|
};
|
|
assert_eq!(
|
|
event_to_subject("movies-diary.events", &event),
|
|
"movies-diary.events.movie.discovered"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn consumer_subject_filter_appends_wildcard() {
|
|
assert_eq!(
|
|
consumer_subject_filter("movies-diary.events"),
|
|
"movies-diary.events.>"
|
|
);
|
|
}
|
|
}
|