refactor(import): scoped Arc deps, ImportSessionCleanupJob
This commit is contained in:
@@ -8,7 +8,7 @@ use domain::{
|
||||
import::{ImportRow, ParsedFile, RowResult},
|
||||
},
|
||||
ports::{DocumentParser, MovieRepository},
|
||||
testing::InMemoryMovieRepository,
|
||||
testing::{InMemoryImportSessionRepository, InMemoryMovieRepository},
|
||||
value_objects::{ExternalMetadataId, MovieTitle, ReleaseYear},
|
||||
};
|
||||
|
||||
@@ -21,11 +21,13 @@ use crate::test_helpers::TestContextBuilder;
|
||||
|
||||
#[tokio::test]
|
||||
async fn applies_mapping_to_session() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let ctx = TestContextBuilder::new().build();
|
||||
let user_id = Uuid::new_v4();
|
||||
|
||||
let session = create_session::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
ctx.services.document_parser.clone(),
|
||||
CreateImportSessionCommand {
|
||||
user_id,
|
||||
bytes: b"title\nTest".to_vec(),
|
||||
@@ -36,7 +38,9 @@ async fn applies_mapping_to_session() {
|
||||
.unwrap();
|
||||
|
||||
let rows = apply_mapping::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
ctx.services.document_parser.clone(),
|
||||
ctx.repos.movie.clone(),
|
||||
ApplyImportMappingCommand {
|
||||
user_id,
|
||||
session_id: session.session_id.value(),
|
||||
@@ -51,10 +55,13 @@ async fn applies_mapping_to_session() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn fails_when_session_not_found() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let ctx = TestContextBuilder::new().build();
|
||||
|
||||
let result = apply_mapping::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
ctx.services.document_parser.clone(),
|
||||
ctx.repos.movie.clone(),
|
||||
ApplyImportMappingCommand {
|
||||
user_id: Uuid::new_v4(),
|
||||
session_id: Uuid::new_v4(),
|
||||
@@ -102,6 +109,7 @@ impl DocumentParser for DuplicateTestParser {
|
||||
#[tokio::test]
|
||||
async fn marks_duplicate_by_external_id() {
|
||||
let movies = InMemoryMovieRepository::new();
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
|
||||
let ext_id = ExternalMetadataId::new("tt1234567".into()).unwrap();
|
||||
let movie = Movie::new(
|
||||
@@ -113,23 +121,20 @@ async fn marks_duplicate_by_external_id() {
|
||||
);
|
||||
movies.upsert_movie(&movie).await.unwrap();
|
||||
|
||||
let parser = DuplicateTestParser {
|
||||
let parser = Arc::new(DuplicateTestParser {
|
||||
rows: vec![ImportRow {
|
||||
title: Some("Known Movie".into()),
|
||||
release_year: Some("2020".into()),
|
||||
external_metadata_id: Some("tt1234567".into()),
|
||||
..ImportRow::default()
|
||||
}],
|
||||
};
|
||||
});
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_movies(Arc::clone(&movies) as _)
|
||||
.with_document_parser(Arc::new(parser) as _)
|
||||
.build();
|
||||
let user_id = Uuid::new_v4();
|
||||
|
||||
let session = create_session::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::clone(&parser) as _,
|
||||
CreateImportSessionCommand {
|
||||
user_id,
|
||||
bytes: b"title\nKnown Movie".to_vec(),
|
||||
@@ -140,7 +145,9 @@ async fn marks_duplicate_by_external_id() {
|
||||
.unwrap();
|
||||
|
||||
let rows = apply_mapping::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::clone(&parser) as _,
|
||||
Arc::clone(&movies) as _,
|
||||
ApplyImportMappingCommand {
|
||||
user_id,
|
||||
session_id: session.session_id.value(),
|
||||
@@ -157,6 +164,7 @@ async fn marks_duplicate_by_external_id() {
|
||||
#[tokio::test]
|
||||
async fn marks_duplicate_by_title_and_year() {
|
||||
let movies = InMemoryMovieRepository::new();
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
|
||||
let movie = Movie::new(
|
||||
None,
|
||||
@@ -167,22 +175,19 @@ async fn marks_duplicate_by_title_and_year() {
|
||||
);
|
||||
movies.upsert_movie(&movie).await.unwrap();
|
||||
|
||||
let parser = DuplicateTestParser {
|
||||
let parser = Arc::new(DuplicateTestParser {
|
||||
rows: vec![ImportRow {
|
||||
title: Some("Duplicate Film".into()),
|
||||
release_year: Some("2022".into()),
|
||||
..ImportRow::default()
|
||||
}],
|
||||
};
|
||||
});
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_movies(Arc::clone(&movies) as _)
|
||||
.with_document_parser(Arc::new(parser) as _)
|
||||
.build();
|
||||
let user_id = Uuid::new_v4();
|
||||
|
||||
let session = create_session::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::clone(&parser) as _,
|
||||
CreateImportSessionCommand {
|
||||
user_id,
|
||||
bytes: b"title\nDuplicate Film".to_vec(),
|
||||
@@ -193,7 +198,9 @@ async fn marks_duplicate_by_title_and_year() {
|
||||
.unwrap();
|
||||
|
||||
let rows = apply_mapping::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::clone(&parser) as _,
|
||||
Arc::clone(&movies) as _,
|
||||
ApplyImportMappingCommand {
|
||||
user_id,
|
||||
session_id: session.session_id.value(),
|
||||
|
||||
@@ -3,19 +3,20 @@ use std::sync::Arc;
|
||||
use chrono::Utc;
|
||||
use domain::models::ImportProfile;
|
||||
use domain::ports::{ImportProfileRepository, ImportSessionRepository};
|
||||
use domain::testing::InMemoryImportProfileRepository;
|
||||
use domain::testing::{InMemoryImportProfileRepository, InMemoryImportSessionRepository};
|
||||
use domain::value_objects::{ImportProfileId, UserId};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::import::{apply_profile, commands::ApplyImportProfileCommand};
|
||||
use crate::test_helpers::TestContextBuilder;
|
||||
|
||||
#[tokio::test]
|
||||
async fn fails_when_profile_not_found() {
|
||||
let ctx = TestContextBuilder::new().build();
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
|
||||
let result = apply_profile::execute(
|
||||
&ctx,
|
||||
Arc::clone(&profiles) as _,
|
||||
Arc::clone(&sessions) as _,
|
||||
ApplyImportProfileCommand {
|
||||
user_id: Uuid::new_v4(),
|
||||
session_id: Uuid::new_v4(),
|
||||
@@ -30,6 +31,7 @@ async fn fails_when_profile_not_found() {
|
||||
#[tokio::test]
|
||||
async fn fails_when_session_not_found() {
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let user_id = Uuid::new_v4();
|
||||
|
||||
let profile = ImportProfile::new(
|
||||
@@ -42,12 +44,9 @@ async fn fails_when_session_not_found() {
|
||||
let profile_id = profile.id.clone();
|
||||
profiles.save(&profile).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_profiles(Arc::clone(&profiles) as _)
|
||||
.build();
|
||||
|
||||
let result = apply_profile::execute(
|
||||
&ctx,
|
||||
Arc::clone(&profiles) as _,
|
||||
Arc::clone(&sessions) as _,
|
||||
ApplyImportProfileCommand {
|
||||
user_id,
|
||||
session_id: Uuid::new_v4(),
|
||||
@@ -87,13 +86,9 @@ async fn applies_profile_mappings_to_session() {
|
||||
let session_id = session.id.clone();
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_profiles(Arc::clone(&profiles) as _)
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
apply_profile::execute(
|
||||
&ctx,
|
||||
Arc::clone(&profiles) as _,
|
||||
Arc::clone(&sessions) as _,
|
||||
ApplyImportProfileCommand {
|
||||
user_id,
|
||||
session_id: session_id.value(),
|
||||
|
||||
@@ -3,16 +3,12 @@ use std::sync::Arc;
|
||||
use domain::testing::InMemoryImportSessionRepository;
|
||||
|
||||
use crate::import::cleanup;
|
||||
use crate::test_helpers::TestContextBuilder;
|
||||
|
||||
#[tokio::test]
|
||||
async fn returns_zero_when_nothing_expired() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = cleanup::execute(&ctx).await.unwrap();
|
||||
let result = cleanup::execute(Arc::clone(&sessions) as _).await.unwrap();
|
||||
|
||||
assert_eq!(result, 0);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use domain::testing::InMemoryImportSessionRepository;
|
||||
|
||||
use crate::import::{commands::CreateImportSessionCommand, create_session};
|
||||
use crate::test_helpers::TestContextBuilder;
|
||||
|
||||
#[tokio::test]
|
||||
async fn creates_session_with_parsed_file() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let ctx = TestContextBuilder::new().build();
|
||||
|
||||
let result = create_session::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
ctx.services.document_parser.clone(),
|
||||
CreateImportSessionCommand {
|
||||
user_id: Uuid::new_v4(),
|
||||
bytes: b"col1\nval1".to_vec(),
|
||||
|
||||
@@ -4,17 +4,13 @@ use domain::testing::InMemoryImportProfileRepository;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::import::{commands::DeleteImportProfileCommand, delete_profile};
|
||||
use crate::test_helpers::TestContextBuilder;
|
||||
|
||||
#[tokio::test]
|
||||
async fn fails_when_profile_not_found() {
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_profiles(Arc::clone(&profiles) as _)
|
||||
.build();
|
||||
|
||||
let result = delete_profile::execute(
|
||||
&ctx,
|
||||
Arc::clone(&profiles) as _,
|
||||
DeleteImportProfileCommand {
|
||||
user_id: Uuid::new_v4(),
|
||||
profile_id: Uuid::new_v4(),
|
||||
|
||||
@@ -9,7 +9,7 @@ use uuid::Uuid;
|
||||
|
||||
use crate::import::commands::ExecuteImportCommand;
|
||||
use crate::import::execute;
|
||||
use crate::test_helpers::TestContextBuilder;
|
||||
use crate::test_helpers::NoopReviewLogger;
|
||||
|
||||
fn make_session_with_rows(user_id: UserId, session_id: ImportSessionId) -> ImportSession {
|
||||
let now = Utc::now().naive_utc();
|
||||
@@ -52,12 +52,9 @@ async fn imports_confirmed_rows() {
|
||||
let session = make_session_with_rows(UserId::from_uuid(uid), sid.clone());
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -81,12 +78,9 @@ async fn skips_unconfirmed_rows() {
|
||||
let session = make_session_with_rows(UserId::from_uuid(uid), sid.clone());
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -102,9 +96,11 @@ async fn skips_unconfirmed_rows() {
|
||||
|
||||
#[tokio::test]
|
||||
async fn fails_when_session_not_found() {
|
||||
let ctx = TestContextBuilder::new().build();
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: Uuid::new_v4(),
|
||||
session_id: Uuid::new_v4(),
|
||||
@@ -138,12 +134,9 @@ async fn handles_datetime_format() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -179,12 +172,9 @@ async fn fails_on_invalid_rating() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -220,12 +210,9 @@ async fn fails_on_missing_watched_at() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -261,12 +248,9 @@ async fn imports_row_with_external_metadata_id() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -302,12 +286,9 @@ async fn imports_row_with_director_and_comment() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -343,12 +324,9 @@ async fn handles_space_separated_datetime_format() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -379,12 +357,9 @@ async fn reports_invalid_row_result_errors() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -422,12 +397,9 @@ async fn fails_on_missing_rating() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -464,12 +436,9 @@ async fn fails_on_unparseable_date() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -506,12 +475,9 @@ async fn imports_row_without_release_year() {
|
||||
}]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
@@ -535,12 +501,9 @@ async fn deletes_session_after_import() {
|
||||
sessions.create(&session).await.unwrap();
|
||||
assert_eq!(sessions.count(), 1);
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
execute::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::new(NoopReviewLogger),
|
||||
ExecuteImportCommand {
|
||||
user_id: uid,
|
||||
session_id: sid.value(),
|
||||
|
||||
@@ -5,17 +5,13 @@ use domain::value_objects::UserId;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::import::list_profiles;
|
||||
use crate::test_helpers::TestContextBuilder;
|
||||
|
||||
#[tokio::test]
|
||||
async fn returns_empty_when_no_profiles() {
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_profiles(Arc::clone(&profiles) as _)
|
||||
.build();
|
||||
|
||||
let user_id = UserId::from_uuid(Uuid::new_v4());
|
||||
let result = list_profiles::execute(&ctx, &user_id).await.unwrap();
|
||||
let result = list_profiles::execute(Arc::clone(&profiles) as _, &user_id).await.unwrap();
|
||||
|
||||
assert!(result.is_empty());
|
||||
}
|
||||
|
||||
@@ -3,22 +3,20 @@ use std::sync::Arc;
|
||||
use chrono::Utc;
|
||||
use domain::models::ImportSession;
|
||||
use domain::ports::ImportSessionRepository;
|
||||
use domain::testing::InMemoryImportSessionRepository;
|
||||
use domain::testing::{InMemoryImportProfileRepository, InMemoryImportSessionRepository};
|
||||
use domain::value_objects::{ImportSessionId, UserId};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::import::{commands::SaveImportProfileCommand, save_profile};
|
||||
use crate::test_helpers::TestContextBuilder;
|
||||
|
||||
#[tokio::test]
|
||||
async fn fails_when_session_not_found() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
|
||||
let result = save_profile::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::clone(&profiles) as _,
|
||||
SaveImportProfileCommand {
|
||||
user_id: Uuid::new_v4(),
|
||||
session_id: Uuid::new_v4(),
|
||||
@@ -33,6 +31,7 @@ async fn fails_when_session_not_found() {
|
||||
#[tokio::test]
|
||||
async fn saves_profile_from_session() {
|
||||
let sessions = InMemoryImportSessionRepository::new();
|
||||
let profiles = InMemoryImportProfileRepository::new();
|
||||
let user_id = Uuid::new_v4();
|
||||
let sid = ImportSessionId::generate();
|
||||
|
||||
@@ -44,12 +43,9 @@ async fn saves_profile_from_session() {
|
||||
session.field_mappings = Some(vec![]);
|
||||
sessions.create(&session).await.unwrap();
|
||||
|
||||
let ctx = TestContextBuilder::new()
|
||||
.with_import_sessions(Arc::clone(&sessions) as _)
|
||||
.build();
|
||||
|
||||
let result = save_profile::execute(
|
||||
&ctx,
|
||||
Arc::clone(&sessions) as _,
|
||||
Arc::clone(&profiles) as _,
|
||||
SaveImportProfileCommand {
|
||||
user_id,
|
||||
session_id: sid.value(),
|
||||
|
||||
Reference in New Issue
Block a user