@@ -215,9 +215,6 @@ pub fn watchlist_entry_to_domain(
|
|||||||
/// Convert raw watchlist+movie row fields into a [`WatchlistWithMovie`].
|
/// Convert raw watchlist+movie row fields into a [`WatchlistWithMovie`].
|
||||||
///
|
///
|
||||||
/// Takes the watchlist entry fields and a pre-converted [`Movie`].
|
/// Takes the watchlist entry fields and a pre-converted [`Movie`].
|
||||||
pub fn watchlist_with_movie_to_domain(
|
pub fn watchlist_with_movie_to_domain(entry: WatchlistEntry, movie: Movie) -> WatchlistWithMovie {
|
||||||
entry: WatchlistEntry,
|
|
||||||
movie: Movie,
|
|
||||||
) -> WatchlistWithMovie {
|
|
||||||
WatchlistWithMovie { entry, movie }
|
WatchlistWithMovie { entry, movie }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use async_trait::async_trait;
|
|||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use k_ap::ActivityRepository;
|
use k_ap::ActivityRepository;
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use super::PostgresFederationRepository;
|
use super::PostgresFederationRepository;
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ActivityRepository for PostgresFederationRepository {
|
impl ActivityRepository for PostgresFederationRepository {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use chrono::Utc;
|
|||||||
use k_ap::{ActorRepository, RemoteActor};
|
use k_ap::{ActorRepository, RemoteActor};
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use super::{PG_ACTOR_COLS, PostgresFederationRepository, pg_remote_actor};
|
use super::{PG_ACTOR_COLS, PostgresFederationRepository, pg_remote_actor};
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ActorRepository for PostgresFederationRepository {
|
impl ActorRepository for PostgresFederationRepository {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ impl PostgresApContentQuery {
|
|||||||
|
|
||||||
// ── Local row types ──────────────────────────────────────────────────────────
|
// ── Local row types ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
use adapter_common::{parse_uuid, parse_datetime};
|
use adapter_common::{parse_datetime, parse_uuid};
|
||||||
|
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
struct MovieRow {
|
struct MovieRow {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use chrono::Utc;
|
|||||||
use k_ap::{BlockedDomain, BlocklistRepository};
|
use k_ap::{BlockedDomain, BlocklistRepository};
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use super::PostgresFederationRepository;
|
use super::PostgresFederationRepository;
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl BlocklistRepository for PostgresFederationRepository {
|
impl BlocklistRepository for PostgresFederationRepository {
|
||||||
|
|||||||
@@ -6,11 +6,10 @@ use k_ap::{
|
|||||||
};
|
};
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use super::{
|
use super::{
|
||||||
PG_ACTOR_COLS, PostgresFederationRepository, pg_remote_actor, status_to_str,
|
PG_ACTOR_COLS, PostgresFederationRepository, pg_remote_actor, status_to_str, str_to_status,
|
||||||
str_to_status,
|
|
||||||
};
|
};
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl FollowRepository for PostgresFederationRepository {
|
impl FollowRepository for PostgresFederationRepository {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ impl PostgresRemoteGoalRepository {
|
|||||||
pub fn new(pool: PgPool) -> Self {
|
pub fn new(pool: PgPool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use anyhow::{Result, anyhow};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use domain::models::{Review, ReviewSource};
|
use domain::models::{Review, ReviewSource};
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use super::PostgresFederationRepository;
|
use super::PostgresFederationRepository;
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl RemoteReviewRepository for PostgresFederationRepository {
|
impl RemoteReviewRepository for PostgresFederationRepository {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ impl PostgresDiaryRepository {
|
|||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn count_diary_entries(&self, movie_id: Option<&str>) -> Result<i64, DomainError> {
|
async fn count_diary_entries(&self, movie_id: Option<&str>) -> Result<i64, DomainError> {
|
||||||
match movie_id {
|
match movie_id {
|
||||||
None => sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM reviews")
|
None => sqlx::query_scalar::<_, i64>("SELECT COUNT(*) FROM reviews")
|
||||||
@@ -134,7 +133,9 @@ impl PostgresDiaryRepository {
|
|||||||
if has_search {
|
if has_search {
|
||||||
q = q.bind(search.unwrap());
|
q = q.bind(search.unwrap());
|
||||||
}
|
}
|
||||||
q.fetch_one(&self.pool).await.map_err(adapter_common::map_sqlx_error)
|
q.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_user_diary_rows(
|
async fn fetch_user_diary_rows(
|
||||||
@@ -371,7 +372,10 @@ impl DiaryQuery for PostgresDiaryRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let count_q = bind_filter_params!(sqlx::query_scalar::<_, i64>(&count_sql));
|
let count_q = bind_filter_params!(sqlx::query_scalar::<_, i64>(&count_sql));
|
||||||
let total = count_q.fetch_one(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let total = count_q
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let rows_q = bind_filter_params!(sqlx::query_as::<_, FeedRow>(&select_sql));
|
let rows_q = bind_filter_params!(sqlx::query_as::<_, FeedRow>(&select_sql));
|
||||||
let rows = rows_q
|
let rows = rows_q
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ impl PostgresGoalRepository {
|
|||||||
pub fn new(pool: PgPool) -> Self {
|
pub fn new(pool: PgPool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ impl PostgresImportProfileRepository {
|
|||||||
pub fn new(pool: PgPool) -> Self {
|
pub fn new(pool: PgPool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -202,7 +202,6 @@ impl PostgresImportSessionRepository {
|
|||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn serialize_session(
|
fn serialize_session(
|
||||||
s: &ImportSession,
|
s: &ImportSession,
|
||||||
) -> Result<(String, Option<String>, Option<String>), DomainError> {
|
) -> Result<(String, Option<String>, Option<String>), DomainError> {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use domain::{
|
|
||||||
errors::DomainError,
|
|
||||||
models::{DiaryEntry, FeedEntry, Movie, MovieSummary, Review},
|
|
||||||
};
|
|
||||||
use adapter_common::{
|
use adapter_common::{
|
||||||
movie_row_to_domain, movie_stats_to_domain, movie_summary_to_domain, review_row_to_domain,
|
movie_row_to_domain, movie_stats_to_domain, movie_summary_to_domain, review_row_to_domain,
|
||||||
user_summary_to_domain,
|
user_summary_to_domain,
|
||||||
};
|
};
|
||||||
|
use domain::{
|
||||||
|
errors::DomainError,
|
||||||
|
models::{DiaryEntry, FeedEntry, Movie, MovieSummary, Review},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
pub(crate) struct MovieRow {
|
pub(crate) struct MovieRow {
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ impl PostgresMovieRepository {
|
|||||||
pub fn new(pool: PgPool) -> Self {
|
pub fn new(pool: PgPool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ impl PostgresMovieDeduplicator {
|
|||||||
pub fn new(pool: PgPool) -> Self {
|
pub fn new(pool: PgPool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -32,7 +31,11 @@ impl MovieDeduplicator for PostgresMovieDeduplicator {
|
|||||||
let director = canonical.director().map(str::to_string);
|
let director = canonical.director().map(str::to_string);
|
||||||
let poster = canonical.poster_path().map(|p| p.value().to_string());
|
let poster = canonical.poster_path().map(|p| p.value().to_string());
|
||||||
|
|
||||||
let mut tx = self.pool.begin().await.map_err(adapter_common::map_sqlx_error)?;
|
let mut tx = self
|
||||||
|
.pool
|
||||||
|
.begin()
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
// 1. Upsert canonical movie record
|
// 1. Upsert canonical movie record
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ pub fn create_person_adapter(pool: PgPool) -> (Arc<dyn PersonCommand>, Arc<dyn P
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl PersonCommand for PostgresPersonAdapter {
|
impl PersonCommand for PostgresPersonAdapter {
|
||||||
async fn upsert_batch(&self, persons: &[Person]) -> Result<(), DomainError> {
|
async fn upsert_batch(&self, persons: &[Person]) -> Result<(), DomainError> {
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ impl PostgresMovieProfileRepository {
|
|||||||
pub fn new(pool: PgPool) -> Self {
|
pub fn new(pool: PgPool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -24,7 +23,11 @@ impl MovieProfileRepository for PostgresMovieProfileRepository {
|
|||||||
async fn upsert(&self, p: &MovieProfile) -> Result<(), DomainError> {
|
async fn upsert(&self, p: &MovieProfile) -> Result<(), DomainError> {
|
||||||
let movie_id = p.movie_id.value().to_string();
|
let movie_id = p.movie_id.value().to_string();
|
||||||
|
|
||||||
let mut tx = self.pool.begin().await.map_err(adapter_common::map_sqlx_error)?;
|
let mut tx = self
|
||||||
|
.pool
|
||||||
|
.begin()
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
r#"INSERT INTO movie_profiles
|
r#"INSERT INTO movie_profiles
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ impl PostgresRefreshSessionAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl RefreshSessionRepository for PostgresRefreshSessionAdapter {
|
impl RefreshSessionRepository for PostgresRefreshSessionAdapter {
|
||||||
async fn create(&self, session: &RefreshSession) -> Result<(), DomainError> {
|
async fn create(&self, session: &RefreshSession) -> Result<(), DomainError> {
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ use domain::{
|
|||||||
};
|
};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use crate::models::ReviewRow;
|
use crate::models::ReviewRow;
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
pub struct PostgresReviewRepository {
|
pub struct PostgresReviewRepository {
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
@@ -18,7 +18,6 @@ impl PostgresReviewRepository {
|
|||||||
pub fn new(pool: PgPool) -> Self {
|
pub fn new(pool: PgPool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ use domain::{
|
|||||||
};
|
};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use adapter_common::format_year_month;
|
|
||||||
use crate::models::{DirectorCountRow, MonthlyRatingRow, UserTotalsRow};
|
use crate::models::{DirectorCountRow, MonthlyRatingRow, UserTotalsRow};
|
||||||
|
use adapter_common::format_year_month;
|
||||||
|
|
||||||
pub struct PostgresStatsRepository {
|
pub struct PostgresStatsRepository {
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
@@ -19,7 +19,6 @@ impl PostgresStatsRepository {
|
|||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async fn fetch_user_totals(&self, user_id: &str) -> Result<UserTotalsRow, DomainError> {
|
async fn fetch_user_totals(&self, user_id: &str) -> Result<UserTotalsRow, DomainError> {
|
||||||
sqlx::query_as::<_, UserTotalsRow>(
|
sqlx::query_as::<_, UserTotalsRow>(
|
||||||
r#"SELECT COUNT(DISTINCT movie_id) AS total,
|
r#"SELECT COUNT(DISTINCT movie_id) AS total,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ impl PostgresUserSettingsRepository {
|
|||||||
pub fn new(pool: PgPool) -> Self {
|
pub fn new(pool: PgPool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ impl PostgresUserRepository {
|
|||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn parse_role(s: &str) -> UserRole {
|
fn parse_role(s: &str) -> UserRole {
|
||||||
match s {
|
match s {
|
||||||
"admin" => UserRole::Admin,
|
"admin" => UserRole::Admin,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ use sqlx::{PgPool, Row};
|
|||||||
|
|
||||||
use adapter_common::{parse_datetime, parse_uuid};
|
use adapter_common::{parse_datetime, parse_uuid};
|
||||||
|
|
||||||
|
|
||||||
// ── WatchEventRepository ──────────────────────────────────────────────────────
|
// ── WatchEventRepository ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
pub struct PostgresWatchEventRepository {
|
pub struct PostgresWatchEventRepository {
|
||||||
@@ -191,15 +190,33 @@ impl WatchEventQuery for PostgresWatchEventRepository {
|
|||||||
|
|
||||||
fn row_to_watch_event(row: &sqlx::postgres::PgRow) -> Result<WatchEvent, DomainError> {
|
fn row_to_watch_event(row: &sqlx::postgres::PgRow) -> Result<WatchEvent, DomainError> {
|
||||||
let id_str: String = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
let id_str: String = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
||||||
let user_id_str: String = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
let user_id_str: String = row
|
||||||
let movie_id_str: Option<String> = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("user_id")
|
||||||
let title: String = row.try_get("title").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let year: Option<i32> = row.try_get("year").map_err(adapter_common::map_sqlx_error)?;
|
let movie_id_str: Option<String> = row
|
||||||
let ext_id: Option<String> = row.try_get("external_metadata_id").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
let source_str: String = row.try_get("source").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let watched_at_str: String = row.try_get("watched_at").map_err(adapter_common::map_sqlx_error)?;
|
let title: String = row
|
||||||
let status_str: String = row.try_get("status").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("title")
|
||||||
let created_at_str: String = row.try_get("created_at").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let year: Option<i32> = row
|
||||||
|
.try_get("year")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let ext_id: Option<String> = row
|
||||||
|
.try_get("external_metadata_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let source_str: String = row
|
||||||
|
.try_get("source")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let watched_at_str: String = row
|
||||||
|
.try_get("watched_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let status_str: String = row
|
||||||
|
.try_get("status")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let created_at_str: String = row
|
||||||
|
.try_get("created_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let source: WatchEventSource = source_str
|
let source: WatchEventSource = source_str
|
||||||
.parse()
|
.parse()
|
||||||
@@ -330,12 +347,24 @@ impl WebhookTokenRepository for PostgresWebhookTokenRepository {
|
|||||||
|
|
||||||
fn row_to_webhook_token(row: &sqlx::postgres::PgRow) -> Result<WebhookToken, DomainError> {
|
fn row_to_webhook_token(row: &sqlx::postgres::PgRow) -> Result<WebhookToken, DomainError> {
|
||||||
let id_str: String = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
let id_str: String = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
||||||
let user_id_str: String = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
let user_id_str: String = row
|
||||||
let token_hash: String = row.try_get("token_hash").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("user_id")
|
||||||
let provider_str: String = row.try_get("provider").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let label: Option<String> = row.try_get("label").map_err(adapter_common::map_sqlx_error)?;
|
let token_hash: String = row
|
||||||
let created_at_str: String = row.try_get("created_at").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("token_hash")
|
||||||
let last_used_str: Option<String> = row.try_get("last_used_at").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let provider_str: String = row
|
||||||
|
.try_get("provider")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let label: Option<String> = row
|
||||||
|
.try_get("label")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let created_at_str: String = row
|
||||||
|
.try_get("created_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let last_used_str: Option<String> = row
|
||||||
|
.try_get("last_used_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let provider: WatchEventSource = provider_str
|
let provider: WatchEventSource = provider_str
|
||||||
.parse()
|
.parse()
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use domain::{
|
|||||||
};
|
};
|
||||||
use sqlx::{PgPool, Row};
|
use sqlx::{PgPool, Row};
|
||||||
|
|
||||||
use adapter_common::{parse_datetime, parse_uuid};
|
|
||||||
use crate::models::MovieRow;
|
use crate::models::MovieRow;
|
||||||
|
use adapter_common::{parse_datetime, parse_uuid};
|
||||||
|
|
||||||
pub struct PostgresWatchlistRepository {
|
pub struct PostgresWatchlistRepository {
|
||||||
pool: PgPool,
|
pool: PgPool,
|
||||||
@@ -21,7 +21,6 @@ impl PostgresWatchlistRepository {
|
|||||||
pub fn new(pool: PgPool) -> Self {
|
pub fn new(pool: PgPool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use adapter_common::{parse_datetime, parse_uuid};
|
use adapter_common::{parse_datetime, parse_uuid};
|
||||||
|
|
||||||
|
|
||||||
fn status_to_str(s: &WrapUpStatus) -> &'static str {
|
fn status_to_str(s: &WrapUpStatus) -> &'static str {
|
||||||
match s {
|
match s {
|
||||||
WrapUpStatus::Pending => "pending",
|
WrapUpStatus::Pending => "pending",
|
||||||
@@ -216,14 +215,30 @@ impl WrapUpRepository for PostgresWrapUpRepository {
|
|||||||
|
|
||||||
fn row_to_record(row: &sqlx::postgres::PgRow) -> Result<WrapUpRecord, DomainError> {
|
fn row_to_record(row: &sqlx::postgres::PgRow) -> Result<WrapUpRecord, DomainError> {
|
||||||
let id_str: String = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
let id_str: String = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
||||||
let user_id_str: Option<String> = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
let user_id_str: Option<String> = row
|
||||||
let start_date: NaiveDate = row.try_get("start_date").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("user_id")
|
||||||
let end_date: NaiveDate = row.try_get("end_date").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let status_str: String = row.try_get("status").map_err(adapter_common::map_sqlx_error)?;
|
let start_date: NaiveDate = row
|
||||||
let report_json: Option<String> = row.try_get("report_json").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("start_date")
|
||||||
let error_message: Option<String> = row.try_get("error_message").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let created_at_str: String = row.try_get("created_at").map_err(adapter_common::map_sqlx_error)?;
|
let end_date: NaiveDate = row
|
||||||
let completed_at_str: Option<String> = row.try_get("completed_at").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("end_date")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let status_str: String = row
|
||||||
|
.try_get("status")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let report_json: Option<String> = row
|
||||||
|
.try_get("report_json")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let error_message: Option<String> = row
|
||||||
|
.try_get("error_message")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let created_at_str: String = row
|
||||||
|
.try_get("created_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let completed_at_str: Option<String> = row
|
||||||
|
.try_get("completed_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let user_id = user_id_str.as_deref().map(parse_uuid).transpose()?;
|
let user_id = user_id_str.as_deref().map(parse_uuid).transpose()?;
|
||||||
|
|
||||||
@@ -288,7 +303,10 @@ impl WrapUpStatsQuery for PostgresWrapUpStatsQuery {
|
|||||||
q = q.bind(uid);
|
q = q.bind(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows = q.fetch_all(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let rows = q
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
if rows.is_empty() {
|
if rows.is_empty() {
|
||||||
return Ok(vec![]);
|
return Ok(vec![]);
|
||||||
@@ -298,7 +316,9 @@ impl WrapUpStatsQuery for PostgresWrapUpStatsQuery {
|
|||||||
let mut movie_ids: Vec<String> = Vec::new();
|
let mut movie_ids: Vec<String> = Vec::new();
|
||||||
let mut seen = std::collections::HashSet::new();
|
let mut seen = std::collections::HashSet::new();
|
||||||
for row in &rows {
|
for row in &rows {
|
||||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let mid: String = row
|
||||||
|
.try_get("movie_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
if seen.insert(mid.clone()) {
|
if seen.insert(mid.clone()) {
|
||||||
movie_ids.push(mid);
|
movie_ids.push(mid);
|
||||||
}
|
}
|
||||||
@@ -314,18 +334,39 @@ impl WrapUpStatsQuery for PostgresWrapUpStatsQuery {
|
|||||||
// 3) Build result
|
// 3) Build result
|
||||||
let mut result = Vec::with_capacity(rows.len());
|
let mut result = Vec::with_capacity(rows.len());
|
||||||
for row in &rows {
|
for row in &rows {
|
||||||
let movie_id_str: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let movie_id_str: String = row
|
||||||
let title: String = row.try_get("title").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
let release_year: i64 = row.try_get("release_year").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let director: Option<String> = row.try_get("director").map_err(adapter_common::map_sqlx_error)?;
|
let title: String = row
|
||||||
let poster_path: Option<String> = row.try_get("poster_path").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("title")
|
||||||
let rating: i64 = row.try_get("rating").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let watched_at_str: String = row.try_get("watched_at").map_err(adapter_common::map_sqlx_error)?;
|
let release_year: i64 = row
|
||||||
let user_id_str: String = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("release_year")
|
||||||
let runtime_minutes: Option<i32> = row.try_get("runtime_minutes").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let budget_usd: Option<i64> = row.try_get("budget_usd").map_err(adapter_common::map_sqlx_error)?;
|
let director: Option<String> = row
|
||||||
let original_language: Option<String> =
|
.try_get("director")
|
||||||
row.try_get("original_language").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let poster_path: Option<String> = row
|
||||||
|
.try_get("poster_path")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let rating: i64 = row
|
||||||
|
.try_get("rating")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let watched_at_str: String = row
|
||||||
|
.try_get("watched_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let user_id_str: String = row
|
||||||
|
.try_get("user_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let runtime_minutes: Option<i32> = row
|
||||||
|
.try_get("runtime_minutes")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let budget_usd: Option<i64> = row
|
||||||
|
.try_get("budget_usd")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let original_language: Option<String> = row
|
||||||
|
.try_get("original_language")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let genres = genres_map.get(&movie_id_str).cloned().unwrap_or_default();
|
let genres = genres_map.get(&movie_id_str).cloned().unwrap_or_default();
|
||||||
let keywords = keywords_map.get(&movie_id_str).cloned().unwrap_or_default();
|
let keywords = keywords_map.get(&movie_id_str).cloned().unwrap_or_default();
|
||||||
@@ -383,8 +424,12 @@ async fn fetch_genres_pg(
|
|||||||
|
|
||||||
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
for row in rows {
|
for row in rows {
|
||||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let mid: String = row
|
||||||
let name: String = row.try_get("name").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let name: String = row
|
||||||
|
.try_get("name")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
map.entry(mid).or_default().push(name);
|
map.entry(mid).or_default().push(name);
|
||||||
}
|
}
|
||||||
Ok(map)
|
Ok(map)
|
||||||
@@ -404,8 +449,12 @@ async fn fetch_keywords_pg(
|
|||||||
|
|
||||||
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
for row in rows {
|
for row in rows {
|
||||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let mid: String = row
|
||||||
let name: String = row.try_get("name").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let name: String = row
|
||||||
|
.try_get("name")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
map.entry(mid).or_default().push(name);
|
map.entry(mid).or_default().push(name);
|
||||||
}
|
}
|
||||||
Ok(map)
|
Ok(map)
|
||||||
@@ -428,11 +477,21 @@ async fn fetch_cast_pg(
|
|||||||
|
|
||||||
let mut map: HashMap<String, Vec<CastEntry>> = HashMap::new();
|
let mut map: HashMap<String, Vec<CastEntry>> = HashMap::new();
|
||||||
for row in rows {
|
for row in rows {
|
||||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let mid: String = row
|
||||||
let name: String = row.try_get("name").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
let billing_order: i32 = row.try_get("billing_order").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let tmdb_person_id: i64 = row.try_get("tmdb_person_id").map_err(adapter_common::map_sqlx_error)?;
|
let name: String = row
|
||||||
let profile_path: Option<String> = row.try_get("profile_path").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("name")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let billing_order: i32 = row
|
||||||
|
.try_get("billing_order")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let tmdb_person_id: i64 = row
|
||||||
|
.try_get("tmdb_person_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let profile_path: Option<String> = row
|
||||||
|
.try_get("profile_path")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
map.entry(mid).or_default().push(CastEntry {
|
map.entry(mid).or_default().push(CastEntry {
|
||||||
name,
|
name,
|
||||||
billing_order: billing_order as u32,
|
billing_order: billing_order as u32,
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use async_trait::async_trait;
|
|||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use k_ap::ActivityRepository;
|
use k_ap::ActivityRepository;
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use super::SqliteFederationRepository;
|
use super::SqliteFederationRepository;
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ActivityRepository for SqliteFederationRepository {
|
impl ActivityRepository for SqliteFederationRepository {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use chrono::Utc;
|
|||||||
use k_ap::{ActorRepository, RemoteActor};
|
use k_ap::{ActorRepository, RemoteActor};
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use super::{SqliteFederationRepository, remote_actor_from_row};
|
use super::{SqliteFederationRepository, remote_actor_from_row};
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl ActorRepository for SqliteFederationRepository {
|
impl ActorRepository for SqliteFederationRepository {
|
||||||
|
|||||||
@@ -21,12 +21,11 @@ impl SqliteApContentQuery {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Local row types ──────────────────────────────────────────────────────────
|
// ── Local row types ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
use adapter_common::{parse_uuid, parse_datetime};
|
use adapter_common::{parse_datetime, parse_uuid};
|
||||||
|
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
struct MovieRow {
|
struct MovieRow {
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ use chrono::Utc;
|
|||||||
use k_ap::{BlockedDomain, BlocklistRepository};
|
use k_ap::{BlockedDomain, BlocklistRepository};
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use super::SqliteFederationRepository;
|
use super::SqliteFederationRepository;
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl BlocklistRepository for SqliteFederationRepository {
|
impl BlocklistRepository for SqliteFederationRepository {
|
||||||
|
|||||||
@@ -6,11 +6,8 @@ use k_ap::{
|
|||||||
};
|
};
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
|
|
||||||
|
use super::{SqliteFederationRepository, remote_actor_from_row, status_to_str, str_to_status};
|
||||||
use adapter_common::datetime_to_str;
|
use adapter_common::datetime_to_str;
|
||||||
use super::{
|
|
||||||
SqliteFederationRepository, remote_actor_from_row, status_to_str,
|
|
||||||
str_to_status,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl FollowRepository for SqliteFederationRepository {
|
impl FollowRepository for SqliteFederationRepository {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ impl SqliteRemoteGoalRepository {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ use anyhow::{Result, anyhow};
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use domain::models::{Review, ReviewSource};
|
use domain::models::{Review, ReviewSource};
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use super::SqliteFederationRepository;
|
use super::SqliteFederationRepository;
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl RemoteReviewRepository for SqliteFederationRepository {
|
impl RemoteReviewRepository for SqliteFederationRepository {
|
||||||
|
|||||||
@@ -125,7 +125,9 @@ impl SqliteDiaryRepository {
|
|||||||
if has_search {
|
if has_search {
|
||||||
q = q.bind(search.unwrap());
|
q = q.bind(search.unwrap());
|
||||||
}
|
}
|
||||||
q.fetch_one(&self.pool).await.map_err(adapter_common::map_sqlx_error)
|
q.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn fetch_user_diary_rows(
|
async fn fetch_user_diary_rows(
|
||||||
@@ -335,7 +337,10 @@ impl DiaryQuery for SqliteDiaryRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let count_q = bind_filter_params!(sqlx::query_scalar::<_, i64>(&count_sql));
|
let count_q = bind_filter_params!(sqlx::query_scalar::<_, i64>(&count_sql));
|
||||||
let total = count_q.fetch_one(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let total = count_q
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let rows_q = bind_filter_params!(sqlx::query_as::<_, FeedRow>(&select_sql));
|
let rows_q = bind_filter_params!(sqlx::query_as::<_, FeedRow>(&select_sql));
|
||||||
let rows = rows_q
|
let rows = rows_q
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ impl SqliteGoalRepository {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use domain::{
|
|
||||||
errors::DomainError,
|
|
||||||
models::{DiaryEntry, FeedEntry, Movie, MovieSummary, Review, WatchlistWithMovie},
|
|
||||||
};
|
|
||||||
use adapter_common::{
|
use adapter_common::{
|
||||||
movie_row_to_domain, movie_stats_to_domain, movie_summary_to_domain, review_row_to_domain,
|
movie_row_to_domain, movie_stats_to_domain, movie_summary_to_domain, review_row_to_domain,
|
||||||
user_summary_to_domain, watchlist_entry_to_domain, watchlist_with_movie_to_domain,
|
user_summary_to_domain, watchlist_entry_to_domain, watchlist_with_movie_to_domain,
|
||||||
};
|
};
|
||||||
|
use domain::{
|
||||||
|
errors::DomainError,
|
||||||
|
models::{DiaryEntry, FeedEntry, Movie, MovieSummary, Review, WatchlistWithMovie},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(sqlx::FromRow)]
|
#[derive(sqlx::FromRow)]
|
||||||
pub(crate) struct MovieRow {
|
pub(crate) struct MovieRow {
|
||||||
@@ -280,12 +280,7 @@ pub(crate) struct WatchlistRow {
|
|||||||
|
|
||||||
impl WatchlistRow {
|
impl WatchlistRow {
|
||||||
pub fn into_domain(self) -> Result<WatchlistWithMovie, DomainError> {
|
pub fn into_domain(self) -> Result<WatchlistWithMovie, DomainError> {
|
||||||
let entry = watchlist_entry_to_domain(
|
let entry = watchlist_entry_to_domain(self.id, self.user_id, self.movie_id, self.added_at)?;
|
||||||
self.id,
|
|
||||||
self.user_id,
|
|
||||||
self.movie_id,
|
|
||||||
self.added_at,
|
|
||||||
)?;
|
|
||||||
let movie = movie_row_to_domain(
|
let movie = movie_row_to_domain(
|
||||||
self.m_id,
|
self.m_id,
|
||||||
self.external_metadata_id,
|
self.external_metadata_id,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ impl SqliteMovieRepository {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -137,7 +136,10 @@ impl MovieQuery for SqliteMovieRepository {
|
|||||||
for id in ids {
|
for id in ids {
|
||||||
q = q.bind(id.value().to_string());
|
q = q.bind(id.value().to_string());
|
||||||
}
|
}
|
||||||
let rows = q.fetch_all(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let rows = q
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
Ok(rows.into_iter().collect())
|
Ok(rows.into_iter().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +163,10 @@ impl MovieQuery for SqliteMovieRepository {
|
|||||||
for (t, y) in pairs {
|
for (t, y) in pairs {
|
||||||
q = q.bind(t.value().to_string()).bind(y.value() as i64);
|
q = q.bind(t.value().to_string()).bind(y.value() as i64);
|
||||||
}
|
}
|
||||||
let rows = q.fetch_all(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let rows = q
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
Ok(rows
|
Ok(rows
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|r| {
|
.map(|r| {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ impl SqliteMovieDeduplicator {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -32,7 +31,11 @@ impl MovieDeduplicator for SqliteMovieDeduplicator {
|
|||||||
let director = canonical.director().map(str::to_string);
|
let director = canonical.director().map(str::to_string);
|
||||||
let poster = canonical.poster_path().map(|p| p.value().to_string());
|
let poster = canonical.poster_path().map(|p| p.value().to_string());
|
||||||
|
|
||||||
let mut tx = self.pool.begin().await.map_err(adapter_common::map_sqlx_error)?;
|
let mut tx = self
|
||||||
|
.pool
|
||||||
|
.begin()
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
// 1. Upsert canonical movie record
|
// 1. Upsert canonical movie record
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ impl SqliteMovieProfileRepository {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@@ -25,7 +24,11 @@ impl MovieProfileRepository for SqliteMovieProfileRepository {
|
|||||||
let movie_id = p.movie_id.value().to_string();
|
let movie_id = p.movie_id.value().to_string();
|
||||||
let enriched_at = p.enriched_at.to_rfc3339();
|
let enriched_at = p.enriched_at.to_rfc3339();
|
||||||
|
|
||||||
let mut tx = self.pool.begin().await.map_err(adapter_common::map_sqlx_error)?;
|
let mut tx = self
|
||||||
|
.pool
|
||||||
|
.begin()
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
sqlx::query(
|
sqlx::query(
|
||||||
r#"INSERT INTO movie_profiles
|
r#"INSERT INTO movie_profiles
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ impl SqliteProfileFieldsRepository {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ use domain::{
|
|||||||
};
|
};
|
||||||
use sqlx::SqlitePool;
|
use sqlx::SqlitePool;
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use crate::models::ReviewRow;
|
use crate::models::ReviewRow;
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
pub struct SqliteReviewRepository {
|
pub struct SqliteReviewRepository {
|
||||||
pool: SqlitePool,
|
pool: SqlitePool,
|
||||||
@@ -18,7 +18,6 @@ impl SqliteReviewRepository {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ impl SqliteUserSettingsRepository {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -100,7 +100,10 @@ impl WatchEventCommand for SqliteWatchEventRepository {
|
|||||||
for id in ids {
|
for id in ids {
|
||||||
q = q.bind(id.value().to_string());
|
q = q.bind(id.value().to_string());
|
||||||
}
|
}
|
||||||
let result = q.execute(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let result = q
|
||||||
|
.execute(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
Ok(result.rows_affected())
|
Ok(result.rows_affected())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +173,10 @@ impl WatchEventQuery for SqliteWatchEventRepository {
|
|||||||
for id in ids {
|
for id in ids {
|
||||||
q = q.bind(id.value().to_string());
|
q = q.bind(id.value().to_string());
|
||||||
}
|
}
|
||||||
let rows = q.fetch_all(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let rows = q
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
rows.iter().map(row_to_watch_event).collect()
|
rows.iter().map(row_to_watch_event).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,15 +208,33 @@ impl WatchEventQuery for SqliteWatchEventRepository {
|
|||||||
|
|
||||||
fn row_to_watch_event(row: &sqlx::sqlite::SqliteRow) -> Result<WatchEvent, DomainError> {
|
fn row_to_watch_event(row: &sqlx::sqlite::SqliteRow) -> Result<WatchEvent, DomainError> {
|
||||||
let id_str: &str = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
let id_str: &str = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
||||||
let user_id_str: &str = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
let user_id_str: &str = row
|
||||||
let movie_id_str: Option<&str> = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("user_id")
|
||||||
let title: String = row.try_get("title").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let year: Option<i64> = row.try_get("year").map_err(adapter_common::map_sqlx_error)?;
|
let movie_id_str: Option<&str> = row
|
||||||
let ext_id: Option<String> = row.try_get("external_metadata_id").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
let source_str: String = row.try_get("source").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let watched_at_str: String = row.try_get("watched_at").map_err(adapter_common::map_sqlx_error)?;
|
let title: String = row
|
||||||
let status_str: String = row.try_get("status").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("title")
|
||||||
let created_at_str: String = row.try_get("created_at").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let year: Option<i64> = row
|
||||||
|
.try_get("year")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let ext_id: Option<String> = row
|
||||||
|
.try_get("external_metadata_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let source_str: String = row
|
||||||
|
.try_get("source")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let watched_at_str: String = row
|
||||||
|
.try_get("watched_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let status_str: String = row
|
||||||
|
.try_get("status")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let created_at_str: String = row
|
||||||
|
.try_get("created_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let source: WatchEventSource = source_str
|
let source: WatchEventSource = source_str
|
||||||
.parse()
|
.parse()
|
||||||
@@ -340,12 +364,24 @@ impl WebhookTokenRepository for SqliteWebhookTokenRepository {
|
|||||||
|
|
||||||
fn row_to_webhook_token(row: &sqlx::sqlite::SqliteRow) -> Result<WebhookToken, DomainError> {
|
fn row_to_webhook_token(row: &sqlx::sqlite::SqliteRow) -> Result<WebhookToken, DomainError> {
|
||||||
let id_str: &str = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
let id_str: &str = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
||||||
let user_id_str: &str = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
let user_id_str: &str = row
|
||||||
let token_hash: String = row.try_get("token_hash").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("user_id")
|
||||||
let provider_str: String = row.try_get("provider").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let label: Option<String> = row.try_get("label").map_err(adapter_common::map_sqlx_error)?;
|
let token_hash: String = row
|
||||||
let created_at_str: String = row.try_get("created_at").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("token_hash")
|
||||||
let last_used_str: Option<String> = row.try_get("last_used_at").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let provider_str: String = row
|
||||||
|
.try_get("provider")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let label: Option<String> = row
|
||||||
|
.try_get("label")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let created_at_str: String = row
|
||||||
|
.try_get("created_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let last_used_str: Option<String> = row
|
||||||
|
.try_get("last_used_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let provider: WatchEventSource = provider_str
|
let provider: WatchEventSource = provider_str
|
||||||
.parse()
|
.parse()
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ use domain::{
|
|||||||
};
|
};
|
||||||
use sqlx::{Row, SqlitePool};
|
use sqlx::{Row, SqlitePool};
|
||||||
|
|
||||||
use adapter_common::datetime_to_str;
|
|
||||||
use crate::models::WatchlistRow;
|
use crate::models::WatchlistRow;
|
||||||
|
use adapter_common::datetime_to_str;
|
||||||
|
|
||||||
pub struct SqliteWatchlistRepository {
|
pub struct SqliteWatchlistRepository {
|
||||||
pool: SqlitePool,
|
pool: SqlitePool,
|
||||||
@@ -21,7 +21,6 @@ impl SqliteWatchlistRepository {
|
|||||||
pub fn new(pool: SqlitePool) -> Self {
|
pub fn new(pool: SqlitePool) -> Self {
|
||||||
Self { pool }
|
Self { pool }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -226,14 +226,30 @@ impl WrapUpRepository for SqliteWrapUpRepository {
|
|||||||
|
|
||||||
fn row_to_record(row: &sqlx::sqlite::SqliteRow) -> Result<WrapUpRecord, DomainError> {
|
fn row_to_record(row: &sqlx::sqlite::SqliteRow) -> Result<WrapUpRecord, DomainError> {
|
||||||
let id_str: String = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
let id_str: String = row.try_get("id").map_err(adapter_common::map_sqlx_error)?;
|
||||||
let user_id_str: Option<String> = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
let user_id_str: Option<String> = row
|
||||||
let start_date_str: String = row.try_get("start_date").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("user_id")
|
||||||
let end_date_str: String = row.try_get("end_date").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let status_str: String = row.try_get("status").map_err(adapter_common::map_sqlx_error)?;
|
let start_date_str: String = row
|
||||||
let report_json: Option<String> = row.try_get("report_json").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("start_date")
|
||||||
let error_message: Option<String> = row.try_get("error_message").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let created_at_str: String = row.try_get("created_at").map_err(adapter_common::map_sqlx_error)?;
|
let end_date_str: String = row
|
||||||
let completed_at_str: Option<String> = row.try_get("completed_at").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("end_date")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let status_str: String = row
|
||||||
|
.try_get("status")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let report_json: Option<String> = row
|
||||||
|
.try_get("report_json")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let error_message: Option<String> = row
|
||||||
|
.try_get("error_message")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let created_at_str: String = row
|
||||||
|
.try_get("created_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let completed_at_str: Option<String> = row
|
||||||
|
.try_get("completed_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let user_id = user_id_str.as_deref().map(parse_uuid).transpose()?;
|
let user_id = user_id_str.as_deref().map(parse_uuid).transpose()?;
|
||||||
|
|
||||||
@@ -299,7 +315,10 @@ impl WrapUpStatsQuery for SqliteWrapUpStatsQuery {
|
|||||||
q = q.bind(uid);
|
q = q.bind(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
let rows = q.fetch_all(&self.pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let rows = q
|
||||||
|
.fetch_all(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
if rows.is_empty() {
|
if rows.is_empty() {
|
||||||
return Ok(vec![]);
|
return Ok(vec![]);
|
||||||
@@ -309,7 +328,9 @@ impl WrapUpStatsQuery for SqliteWrapUpStatsQuery {
|
|||||||
let mut movie_ids: Vec<String> = Vec::new();
|
let mut movie_ids: Vec<String> = Vec::new();
|
||||||
let mut seen = std::collections::HashSet::new();
|
let mut seen = std::collections::HashSet::new();
|
||||||
for row in &rows {
|
for row in &rows {
|
||||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let mid: String = row
|
||||||
|
.try_get("movie_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
if seen.insert(mid.clone()) {
|
if seen.insert(mid.clone()) {
|
||||||
movie_ids.push(mid);
|
movie_ids.push(mid);
|
||||||
}
|
}
|
||||||
@@ -325,18 +346,39 @@ impl WrapUpStatsQuery for SqliteWrapUpStatsQuery {
|
|||||||
// 3) Build result
|
// 3) Build result
|
||||||
let mut result = Vec::with_capacity(rows.len());
|
let mut result = Vec::with_capacity(rows.len());
|
||||||
for row in &rows {
|
for row in &rows {
|
||||||
let movie_id_str: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let movie_id_str: String = row
|
||||||
let title: String = row.try_get("title").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
let release_year: i64 = row.try_get("release_year").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let director: Option<String> = row.try_get("director").map_err(adapter_common::map_sqlx_error)?;
|
let title: String = row
|
||||||
let poster_path: Option<String> = row.try_get("poster_path").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("title")
|
||||||
let rating: i64 = row.try_get("rating").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let watched_at_str: String = row.try_get("watched_at").map_err(adapter_common::map_sqlx_error)?;
|
let release_year: i64 = row
|
||||||
let user_id_str: String = row.try_get("user_id").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("release_year")
|
||||||
let runtime_minutes: Option<i32> = row.try_get("runtime_minutes").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let budget_usd: Option<i64> = row.try_get("budget_usd").map_err(adapter_common::map_sqlx_error)?;
|
let director: Option<String> = row
|
||||||
let original_language: Option<String> =
|
.try_get("director")
|
||||||
row.try_get("original_language").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let poster_path: Option<String> = row
|
||||||
|
.try_get("poster_path")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let rating: i64 = row
|
||||||
|
.try_get("rating")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let watched_at_str: String = row
|
||||||
|
.try_get("watched_at")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let user_id_str: String = row
|
||||||
|
.try_get("user_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let runtime_minutes: Option<i32> = row
|
||||||
|
.try_get("runtime_minutes")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let budget_usd: Option<i64> = row
|
||||||
|
.try_get("budget_usd")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let original_language: Option<String> = row
|
||||||
|
.try_get("original_language")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let genres = genres_map.get(&movie_id_str).cloned().unwrap_or_default();
|
let genres = genres_map.get(&movie_id_str).cloned().unwrap_or_default();
|
||||||
let keywords = keywords_map.get(&movie_id_str).cloned().unwrap_or_default();
|
let keywords = keywords_map.get(&movie_id_str).cloned().unwrap_or_default();
|
||||||
@@ -406,12 +448,19 @@ async fn fetch_genres_sqlite(
|
|||||||
for id in movie_ids {
|
for id in movie_ids {
|
||||||
q = q.bind(id);
|
q = q.bind(id);
|
||||||
}
|
}
|
||||||
let rows = q.fetch_all(pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let rows = q
|
||||||
|
.fetch_all(pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
for row in rows {
|
for row in rows {
|
||||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let mid: String = row
|
||||||
let name: String = row.try_get("name").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let name: String = row
|
||||||
|
.try_get("name")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
map.entry(mid).or_default().push(name);
|
map.entry(mid).or_default().push(name);
|
||||||
}
|
}
|
||||||
Ok(map)
|
Ok(map)
|
||||||
@@ -432,12 +481,19 @@ async fn fetch_keywords_sqlite(
|
|||||||
for id in movie_ids {
|
for id in movie_ids {
|
||||||
q = q.bind(id);
|
q = q.bind(id);
|
||||||
}
|
}
|
||||||
let rows = q.fetch_all(pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let rows = q
|
||||||
|
.fetch_all(pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
let mut map: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
for row in rows {
|
for row in rows {
|
||||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let mid: String = row
|
||||||
let name: String = row.try_get("name").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let name: String = row
|
||||||
|
.try_get("name")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
map.entry(mid).or_default().push(name);
|
map.entry(mid).or_default().push(name);
|
||||||
}
|
}
|
||||||
Ok(map)
|
Ok(map)
|
||||||
@@ -461,15 +517,28 @@ async fn fetch_cast_sqlite(
|
|||||||
for id in movie_ids {
|
for id in movie_ids {
|
||||||
q = q.bind(id);
|
q = q.bind(id);
|
||||||
}
|
}
|
||||||
let rows = q.fetch_all(pool).await.map_err(adapter_common::map_sqlx_error)?;
|
let rows = q
|
||||||
|
.fetch_all(pool)
|
||||||
|
.await
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
|
||||||
let mut map: HashMap<String, Vec<CastEntry>> = HashMap::new();
|
let mut map: HashMap<String, Vec<CastEntry>> = HashMap::new();
|
||||||
for row in rows {
|
for row in rows {
|
||||||
let mid: String = row.try_get("movie_id").map_err(adapter_common::map_sqlx_error)?;
|
let mid: String = row
|
||||||
let name: String = row.try_get("name").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("movie_id")
|
||||||
let billing_order: i32 = row.try_get("billing_order").map_err(adapter_common::map_sqlx_error)?;
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
let tmdb_person_id: i64 = row.try_get("tmdb_person_id").map_err(adapter_common::map_sqlx_error)?;
|
let name: String = row
|
||||||
let profile_path: Option<String> = row.try_get("profile_path").map_err(adapter_common::map_sqlx_error)?;
|
.try_get("name")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let billing_order: i32 = row
|
||||||
|
.try_get("billing_order")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let tmdb_person_id: i64 = row
|
||||||
|
.try_get("tmdb_person_id")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
|
let profile_path: Option<String> = row
|
||||||
|
.try_get("profile_path")
|
||||||
|
.map_err(adapter_common::map_sqlx_error)?;
|
||||||
map.entry(mid).or_default().push(CastEntry {
|
map.entry(mid).or_default().push(CastEntry {
|
||||||
name,
|
name,
|
||||||
billing_order: billing_order as u32,
|
billing_order: billing_order as u32,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::ports::{
|
use domain::ports::{
|
||||||
DiaryQuery, EventPublisher, MovieCommand, MovieProfileRepository, MovieQuery,
|
DiaryQuery, EventPublisher, MovieCommand, MovieProfileRepository, MovieQuery, ReviewRepository,
|
||||||
ReviewRepository, SocialQueryPort,
|
SocialQueryPort,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::config::AppConfig;
|
use crate::config::AppConfig;
|
||||||
|
|||||||
@@ -13,7 +13,10 @@ pub async fn execute(
|
|||||||
) -> Result<GoalWithProgress, DomainError> {
|
) -> Result<GoalWithProgress, DomainError> {
|
||||||
let user_id = UserId::from_uuid(cmd.user_id);
|
let user_id = UserId::from_uuid(cmd.user_id);
|
||||||
|
|
||||||
let existing = deps.goal_query.find_by_user_and_year(&user_id, cmd.year).await?;
|
let existing = deps
|
||||||
|
.goal_query
|
||||||
|
.find_by_user_and_year(&user_id, cmd.year)
|
||||||
|
.await?;
|
||||||
if existing.is_some() {
|
if existing.is_some() {
|
||||||
return Err(DomainError::ValidationError(
|
return Err(DomainError::ValidationError(
|
||||||
"Goal already exists for this year".into(),
|
"Goal already exists for this year".into(),
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ use domain::testing::{
|
|||||||
};
|
};
|
||||||
use domain::{
|
use domain::{
|
||||||
ports::{
|
ports::{
|
||||||
AuthService, DiaryExporter, DiaryQuery, DocumentParser, EventPublisher,
|
AuthService, DiaryExporter, DiaryQuery, DocumentParser, EventPublisher, GoalCommand,
|
||||||
GoalCommand, GoalQuery, ImportProfileRepository, ImportSessionRepository, MetadataClient,
|
GoalQuery, ImportProfileRepository, ImportSessionRepository, MetadataClient, MovieCommand,
|
||||||
MovieCommand, MovieProfileRepository, MovieQuery, ObjectStorage, PasswordHasher,
|
MovieProfileRepository, MovieQuery, ObjectStorage, PasswordHasher, PersonCommand,
|
||||||
PersonCommand, PersonQuery, PosterFetcherClient, RefreshSessionRepository,
|
PersonQuery, PosterFetcherClient, RefreshSessionRepository, ReviewRepository,
|
||||||
ReviewRepository, SearchCommand, SearchPort, StatsRepository, UserProfileFieldsRepository,
|
SearchCommand, SearchPort, StatsRepository, UserProfileFieldsRepository, UserRepository,
|
||||||
UserRepository, UserSettingsRepository, WatchEventCommand, WatchEventQuery,
|
UserSettingsRepository, WatchEventCommand, WatchEventQuery, WatchlistRepository,
|
||||||
WatchlistRepository, WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
|
WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
|
||||||
},
|
},
|
||||||
testing::{
|
testing::{
|
||||||
FakeAuthService, FakeDiaryQuery, FakeDocumentParser, FakeMetadataClient,
|
FakeAuthService, FakeDiaryQuery, FakeDocumentParser, FakeMetadataClient,
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::ports::{
|
use domain::ports::{
|
||||||
DiaryQuery, EventPublisher, ObjectStorage, SocialQueryPort, StatsRepository,
|
DiaryQuery, EventPublisher, ObjectStorage, SocialQueryPort, StatsRepository, UserRepository,
|
||||||
UserRepository,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct GetProfileDeps {
|
pub struct GetProfileDeps {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ pub use watch_event::{
|
|||||||
pub use watchlist::{WatchlistEntry, WatchlistWithMovie};
|
pub use watchlist::{WatchlistEntry, WatchlistWithMovie};
|
||||||
pub use wrapup::*;
|
pub use wrapup::*;
|
||||||
|
|
||||||
|
pub use crate::value_objects::PersonId;
|
||||||
pub use import::{
|
pub use import::{
|
||||||
AnnotatedRow, DomainField, FieldMapping, FileFormat, ImportError, ImportRow, ParsedFile,
|
AnnotatedRow, DomainField, FieldMapping, FileFormat, ImportError, ImportRow, ParsedFile,
|
||||||
RowResult, Transform,
|
RowResult, Transform,
|
||||||
@@ -49,7 +50,6 @@ pub use import_session::ImportSession;
|
|||||||
pub use person::{
|
pub use person::{
|
||||||
CastCredit, CrewCredit, ExternalPersonId, Person, PersonCredits, PersonEnrichmentData,
|
CastCredit, CrewCredit, ExternalPersonId, Person, PersonCredits, PersonEnrichmentData,
|
||||||
};
|
};
|
||||||
pub use crate::value_objects::PersonId;
|
|
||||||
pub use refresh_session::{GeneratedToken, RefreshSession};
|
pub use refresh_session::{GeneratedToken, RefreshSession};
|
||||||
pub use search::{
|
pub use search::{
|
||||||
EntityType, IndexableDocument, MovieSearchHit, PersonSearchHit, SearchFilters, SearchQuery,
|
EntityType, IndexableDocument, MovieSearchHit, PersonSearchHit, SearchFilters, SearchQuery,
|
||||||
|
|||||||
@@ -39,10 +39,7 @@ pub struct NoopSocialQueryPort;
|
|||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl super::SocialQueryPort for NoopSocialQueryPort {
|
impl super::SocialQueryPort for NoopSocialQueryPort {
|
||||||
async fn get_accepted_following_urls(
|
async fn get_accepted_following_urls(&self, _: &UserId) -> Result<Vec<String>, DomainError> {
|
||||||
&self,
|
|
||||||
_: &UserId,
|
|
||||||
) -> Result<Vec<String>, DomainError> {
|
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
async fn list_all_followed_remote_actors(
|
async fn list_all_followed_remote_actors(
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ impl PersonId {
|
|||||||
/// Deterministic UUIDv5 from an external person ID string.
|
/// Deterministic UUIDv5 from an external person ID string.
|
||||||
/// "tmdb:12345" always maps to the same PersonId.
|
/// "tmdb:12345" always maps to the same PersonId.
|
||||||
pub fn from_external(external_id: &crate::models::person::ExternalPersonId) -> Self {
|
pub fn from_external(external_id: &crate::models::person::ExternalPersonId) -> Self {
|
||||||
Self(Uuid::new_v5(&Uuid::NAMESPACE_URL, external_id.value().as_bytes()))
|
Self(Uuid::new_v5(
|
||||||
|
&Uuid::NAMESPACE_URL,
|
||||||
|
external_id.value().as_bytes(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use domain::ports::{
|
use domain::ports::{
|
||||||
AuthService, DiaryExporter, DiaryQuery, DocumentParser, EventPublisher,
|
AuthService, DiaryExporter, DiaryQuery, DocumentParser, EventPublisher, FederatedProfileQuery,
|
||||||
FederatedProfileQuery, GoalCommand, GoalQuery, ImportProfileRepository, ImportSessionRepository,
|
GoalCommand, GoalQuery, ImportProfileRepository, ImportSessionRepository, MetadataClient,
|
||||||
MetadataClient, MovieCommand, MovieProfileRepository, MovieQuery, ObjectStorage,
|
MovieCommand, MovieProfileRepository, MovieQuery, ObjectStorage, PasswordHasher, PersonCommand,
|
||||||
PasswordHasher, PersonCommand, PersonEnrichmentClient, PersonQuery, PosterFetcherClient,
|
PersonEnrichmentClient, PersonQuery, PosterFetcherClient, RefreshSessionRepository,
|
||||||
RefreshSessionRepository, RemoteGoalRepository, RemoteWatchlistRepository, ReviewRepository,
|
RemoteGoalRepository, RemoteWatchlistRepository, ReviewRepository, SearchCommand, SearchPort,
|
||||||
SearchCommand, SearchPort, SocialQueryPort, StatsRepository, UserProfileFieldsRepository,
|
SocialQueryPort, StatsRepository, UserProfileFieldsRepository, UserRepository,
|
||||||
UserRepository, UserSettingsRepository, WatchEventCommand, WatchEventQuery,
|
UserSettingsRepository, WatchEventCommand, WatchEventQuery, WatchlistRepository,
|
||||||
WatchlistRepository, WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
|
WebhookTokenRepository, WrapUpRepository, WrapUpStatsQuery,
|
||||||
};
|
};
|
||||||
|
|
||||||
use application::config::AppConfig;
|
use application::config::AppConfig;
|
||||||
|
|||||||
@@ -612,9 +612,7 @@ async fn fetch_profile_goals(
|
|||||||
goal_query: state.app_ctx.repos.goal_query.clone(),
|
goal_query: state.app_ctx.repos.goal_query.clone(),
|
||||||
stats: state.app_ctx.repos.stats.clone(),
|
stats: state.app_ctx.repos.stats.clone(),
|
||||||
},
|
},
|
||||||
application::goals::queries::ListGoalsQuery {
|
application::goals::queries::ListGoalsQuery { user_id },
|
||||||
user_id,
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|||||||
Reference in New Issue
Block a user