diff --git a/crates/sos24-presentation/src/error/convert_error.rs b/crates/sos24-presentation/src/error/convert_error.rs index 6bb8db62..40c73a49 100644 --- a/crates/sos24-presentation/src/error/convert_error.rs +++ b/crates/sos24-presentation/src/error/convert_error.rs @@ -1,15 +1,5 @@ use axum::http::StatusCode; -use sos24_domain::entity::common::datetime::DateTimeError; -use sos24_domain::entity::file_data::FileIdError; -use sos24_domain::entity::form::{FormError, FormIdError, FormItemIdError}; -use sos24_domain::entity::form_answer::FormAnswerIdError; -use sos24_domain::entity::project::BoundedStringError; -use sos24_domain::repository::file_data::FileDataRepositoryError; -use sos24_domain::repository::file_object::FileObjectRepositoryError; -use sos24_domain::repository::form::FormRepositoryError; -use sos24_domain::repository::form_answer::FormAnswerRepositoryError; -use sos24_domain::service::verify_form_answer::VerifyFormAnswerError; use sos24_domain::{ entity::{ common::email::EmailError, @@ -23,9 +13,16 @@ use sos24_domain::{ news::NewsRepositoryError, project::ProjectRepositoryError, user::UserRepositoryError, }, }; -use sos24_use_case::interactor::file::FileUseCaseError; -use sos24_use_case::interactor::form::FormUseCaseError; -use sos24_use_case::interactor::form_answer::FormAnswerUseCaseError; +use sos24_domain::entity::common::datetime::DateTimeError; +use sos24_domain::entity::file_data::FileIdError; +use sos24_domain::entity::form::{FormError, FormIdError, FormItemIdError}; +use sos24_domain::entity::form_answer::FormAnswerIdError; +use sos24_domain::entity::project::BoundedStringError; +use sos24_domain::repository::file_data::FileDataRepositoryError; +use sos24_domain::repository::file_object::FileObjectRepositoryError; +use sos24_domain::repository::form::FormRepositoryError; +use sos24_domain::repository::form_answer::FormAnswerRepositoryError; +use sos24_domain::service::verify_form_answer::VerifyFormAnswerError; use sos24_use_case::{ context::ContextError, interactor::{ @@ -33,6 +30,9 @@ use sos24_use_case::{ user::UserUseCaseError, }, }; +use sos24_use_case::interactor::file::FileUseCaseError; +use sos24_use_case::interactor::form::FormUseCaseError; +use sos24_use_case::interactor::form_answer::FormAnswerUseCaseError; use super::AppError; @@ -144,6 +144,11 @@ impl From for AppError { "invitation/already-owner-or-subowner".to_string(), message, ), + InvitationUseCaseError::UserNotFound(_) => AppError::new( + StatusCode::NOT_FOUND, + "invitation/user-not-found".to_string(), + message, + ), InvitationUseCaseError::ProjectError(e) => e.into(), InvitationUseCaseError::InvitationError(e) => e.into(), InvitationUseCaseError::InvitationIdError(e) => e.into(), @@ -260,12 +265,18 @@ impl From for AppError { "project/applications-not-accepted".to_string(), message, ), + ProjectUseCaseError::UserNotFound(_) => AppError::new( + StatusCode::NOT_FOUND, + "project/user-not-found".to_string(), + message, + ), ProjectUseCaseError::ContextError(e) => e.into(), ProjectUseCaseError::ProjectRepositoryError(e) => e.into(), ProjectUseCaseError::ProjectIdError(e) => e.into(), ProjectUseCaseError::PermissionDeniedError(e) => e.into(), ProjectUseCaseError::InternalError(e) => e.into(), ProjectUseCaseError::BoundedStringError(e) => e.into(), + ProjectUseCaseError::UserRepositoryError(e) => e.into(), } } } diff --git a/crates/sos24-presentation/src/model/form_answer.rs b/crates/sos24-presentation/src/model/form_answer.rs index 37827520..fb4575c0 100644 --- a/crates/sos24-presentation/src/model/form_answer.rs +++ b/crates/sos24-presentation/src/model/form_answer.rs @@ -62,7 +62,9 @@ pub struct FormAnswerQuery { pub struct FormAnswer { id: String, project_id: String, + project_title: String, form_id: String, + form_title: String, items: Vec, created_at: String, updated_at: String, @@ -74,7 +76,9 @@ impl From for FormAnswer { FormAnswer { id: form_answer_dto.id, project_id: form_answer_dto.project_id, + project_title: form_answer_dto.project_title, form_id: form_answer_dto.form_id, + form_title: form_answer_dto.form_title, items: form_answer_dto .items .into_iter() diff --git a/crates/sos24-presentation/src/model/invitation.rs b/crates/sos24-presentation/src/model/invitation.rs index f68ea990..f3a141c1 100644 --- a/crates/sos24-presentation/src/model/invitation.rs +++ b/crates/sos24-presentation/src/model/invitation.rs @@ -32,7 +32,9 @@ pub struct CreatedInvitation { pub struct Invitation { id: String, inviter: String, + inviter_name: String, project_id: String, + project_title: String, position: InvitationPosition, used_by: Option, created_at: String, @@ -45,7 +47,9 @@ impl From for Invitation { Self { id: dto.id, inviter: dto.inviter, + inviter_name: dto.inviter_name, project_id: dto.project_id, + project_title: dto.project_title, position: InvitationPosition::from(dto.position), used_by: dto.used_by, created_at: dto.created_at.to_rfc3339(), diff --git a/crates/sos24-presentation/src/model/project.rs b/crates/sos24-presentation/src/model/project.rs index 987554d5..2ca2e4eb 100644 --- a/crates/sos24-presentation/src/model/project.rs +++ b/crates/sos24-presentation/src/model/project.rs @@ -81,49 +81,6 @@ impl ConvertToUpdateProjectDto for (UpdateProject, String) { #[derive(Debug, Serialize, Deserialize)] pub struct Project { - id: String, - index: i32, - title: String, - kana_title: String, - group_name: String, - kana_group_name: String, - category: ProjectCategory, - attributes: Vec, - owner_id: String, - sub_owner_id: Option, - remarks: Option, - created_at: String, - updated_at: String, - deleted_at: Option, -} - -impl From for Project { - fn from(project: ProjectDto) -> Self { - Project { - id: project.id, - index: project.index, - title: project.title, - kana_title: project.kana_title, - group_name: project.group_name, - kana_group_name: project.kana_group_name, - category: ProjectCategory::from(project.category), - attributes: project - .attributes - .into_iter() - .map(ProjectAttribute::from) - .collect(), - owner_id: project.owner_id, - sub_owner_id: project.sub_owner_id, - remarks: project.remarks, - created_at: project.created_at.to_rfc3339(), - updated_at: project.updated_at.to_rfc3339(), - deleted_at: project.deleted_at.map(|it| it.to_rfc3339()), - } - } -} - -#[derive(Debug, Serialize)] -pub struct ProjectWithUser { id: String, index: i32, title: String, @@ -144,9 +101,9 @@ pub struct ProjectWithUser { deleted_at: Option, } -impl From<(ProjectDto, UserDto, Option)> for ProjectWithUser { - fn from((project, owner, sub_owner): (ProjectDto, UserDto, Option)) -> Self { - ProjectWithUser { +impl From for Project { + fn from(project: ProjectDto) -> Self { + Project { id: project.id, index: project.index, title: project.title, @@ -160,11 +117,11 @@ impl From<(ProjectDto, UserDto, Option)> for ProjectWithUser { .map(ProjectAttribute::from) .collect(), owner_id: project.owner_id, - owner_name: owner.name, - owner_email: owner.email, + owner_name: project.owner_name, + owner_email: project.owner_email, sub_owner_id: project.sub_owner_id, - sub_owner_name: sub_owner.as_ref().map(|it| it.name.clone()), - sub_owner_email: sub_owner.map(|it| it.email), + sub_owner_name: project.sub_owner_name, + sub_owner_email: project.sub_owner_email, remarks: project.remarks, created_at: project.created_at.to_rfc3339(), updated_at: project.updated_at.to_rfc3339(), diff --git a/crates/sos24-presentation/src/route/project.rs b/crates/sos24-presentation/src/route/project.rs index 87235364..ba84a30b 100644 --- a/crates/sos24-presentation/src/route/project.rs +++ b/crates/sos24-presentation/src/route/project.rs @@ -12,7 +12,7 @@ use csv::Writer; use sos24_use_case::context::Context; use crate::error::AppError; -use crate::model::project::{CreatedProject, ProjectToBeExported, ProjectWithUser}; +use crate::model::project::{CreatedProject, ProjectToBeExported}; use crate::{ model::project::{ ConvertToCreateProjectDto, ConvertToUpdateProjectDto, CreateProject, Project, @@ -177,41 +177,12 @@ pub async fn handle_get_id( State(modules): State>, ) -> Result { let raw_project = modules.project_use_case().find_by_id(&ctx, id).await; - match raw_project { - Ok(raw_project) => { - let owner = match modules - .user_use_case() - .find_by_id(&ctx, raw_project.owner_id.clone()) - .await - { - Ok(user) => user, - Err(err) => { - tracing::error!("Failed to find user: {err:?}"); - return Err(err.into()); - } - }; - let sub_owner = match raw_project.sub_owner_id.clone() { - Some(sub_owner_id) => { - match modules.user_use_case().find_by_id(&ctx, sub_owner_id).await { - Ok(user) => Some(user), - Err(err) => { - tracing::error!("Failed to find user: {err:?}"); - return Err(err.into()); - } - } - } - None => None, - }; - Ok(( - StatusCode::OK, - Json(ProjectWithUser::from((raw_project, owner, sub_owner))), - )) - } - Err(err) => { + raw_project + .map(|raw_project| (StatusCode::OK, Json(Project::from(raw_project)))) + .map_err(|err| { tracing::error!("Failed to find project: {err:?}"); - Err(err.into()) - } - } + err.into() + }) } pub async fn handle_delete_id( diff --git a/crates/sos24-use-case/src/dto/form_answer.rs b/crates/sos24-use-case/src/dto/form_answer.rs index f59bc9cf..366382b5 100644 --- a/crates/sos24-use-case/src/dto/form_answer.rs +++ b/crates/sos24-use-case/src/dto/form_answer.rs @@ -1,4 +1,6 @@ use sos24_domain::entity::file_data::FileId; +use sos24_domain::entity::form::Form; +use sos24_domain::entity::project::Project; use sos24_domain::entity::{ common::date::WithDate, form::{FormId, FormItemId}, @@ -57,7 +59,9 @@ impl UpdateFormAnswerDto { pub struct FormAnswerDto { pub id: String, pub project_id: String, + pub project_title: String, pub form_id: String, + pub form_title: String, pub items: Vec, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, @@ -65,21 +69,26 @@ pub struct FormAnswerDto { } impl FromEntity for FormAnswerDto { - type Entity = WithDate; - fn from_entity(entity: Self::Entity) -> Self { - let form_answer = entity.value.destruct(); + type Entity = (WithDate, WithDate, WithDate
); + fn from_entity((form_answer_entity, project_entity, form_entity): Self::Entity) -> Self { + let form_answer = form_answer_entity.value.destruct(); + let project = project_entity.value.destruct(); + let form = form_entity.value.destruct(); + Self { id: form_answer.id.value().to_string(), project_id: form_answer.project_id.value().to_string(), + project_title: project.title.value().to_string(), form_id: form_answer.form_id.value().to_string(), + form_title: form.title.value().to_string(), items: form_answer .items .into_iter() .map(FormAnswerItemDto::from_entity) .collect(), - created_at: entity.created_at, - updated_at: entity.updated_at, - deleted_at: entity.deleted_at, + created_at: form_answer_entity.created_at, + updated_at: form_answer_entity.updated_at, + deleted_at: form_answer_entity.deleted_at, } } } diff --git a/crates/sos24-use-case/src/dto/invitation.rs b/crates/sos24-use-case/src/dto/invitation.rs index c5518f85..5379fe1a 100644 --- a/crates/sos24-use-case/src/dto/invitation.rs +++ b/crates/sos24-use-case/src/dto/invitation.rs @@ -4,6 +4,8 @@ use sos24_domain::entity::{ project::ProjectId, user::UserId, }; +use sos24_domain::entity::project::Project; +use sos24_domain::entity::user::User; use crate::interactor::invitation::InvitationUseCaseError; @@ -32,7 +34,9 @@ impl ToEntity for CreateInvitationDto { pub struct InvitationDto { pub id: String, pub inviter: String, + pub inviter_name: String, pub project_id: String, + pub project_title: String, pub position: InvitationPositionDto, pub used_by: Option, pub created_at: chrono::DateTime, @@ -41,18 +45,23 @@ pub struct InvitationDto { } impl FromEntity for InvitationDto { - type Entity = WithDate; - fn from_entity(entity: Self::Entity) -> Self { - let invitation = entity.value.destruct(); + type Entity = (WithDate, WithDate, WithDate); + fn from_entity((invitation_entity, user_entity, project_entity): Self::Entity) -> Self { + let invitation = invitation_entity.value.destruct(); + let inviter = user_entity.value.destruct(); + let project = project_entity.value.destruct(); + Self { id: invitation.id.value().to_string(), inviter: invitation.inviter.value().to_string(), + inviter_name: inviter.name.value().to_string(), project_id: invitation.project_id.value().to_string(), + project_title: project.title.value().to_string(), position: InvitationPositionDto::from_entity(invitation.position), used_by: invitation.used_by.map(|id| id.value().to_string()), - created_at: entity.created_at, - updated_at: entity.updated_at, - deleted_at: entity.deleted_at, + created_at: invitation_entity.created_at, + updated_at: invitation_entity.updated_at, + deleted_at: invitation_entity.deleted_at, } } } diff --git a/crates/sos24-use-case/src/dto/project.rs b/crates/sos24-use-case/src/dto/project.rs index 81fd0070..30a4290c 100644 --- a/crates/sos24-use-case/src/dto/project.rs +++ b/crates/sos24-use-case/src/dto/project.rs @@ -1,6 +1,7 @@ use std::fmt; use std::fmt::Formatter; +use sos24_domain::entity::user::User; use sos24_domain::entity::{ common::date::WithDate, project::{ @@ -111,7 +112,11 @@ pub struct ProjectDto { pub category: ProjectCategoryDto, pub attributes: Vec, pub owner_id: String, + pub owner_name: String, + pub owner_email: String, pub sub_owner_id: Option, + pub sub_owner_name: Option, + pub sub_owner_email: Option, pub remarks: Option, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, @@ -119,9 +124,17 @@ pub struct ProjectDto { } impl FromEntity for ProjectDto { - type Entity = WithDate; + type Entity = (WithDate, WithDate, Option>); fn from_entity(entity: Self::Entity) -> Self { - let project = entity.value.destruct(); + let (project_entity, owner_entity, sub_owner_entity) = entity; + let project = project_entity.value.destruct(); + let owner = owner_entity.value.destruct(); + let sub_owner = sub_owner_entity.map(|it| it.value.destruct()); + let (sub_owner_name, sub_owner_email) = match sub_owner { + Some(user) => (Some(user.name.value()), Some(user.email.value())), + None => (None, None), + }; + Self { id: project.id.value().to_string(), index: project.index.value(), @@ -132,11 +145,15 @@ impl FromEntity for ProjectDto { category: ProjectCategoryDto::from_entity(project.category), attributes: Vec::from_entity(project.attributes), owner_id: project.owner_id.value(), + owner_name: owner.name.value(), + owner_email: owner.email.value(), sub_owner_id: project.sub_owner_id.map(|id| id.value()), + sub_owner_name, + sub_owner_email, remarks: project.remarks.map(|it| it.value()), - created_at: entity.created_at, - updated_at: entity.updated_at, - deleted_at: entity.deleted_at, + created_at: project_entity.created_at, + updated_at: project_entity.updated_at, + deleted_at: project_entity.deleted_at, } } } diff --git a/crates/sos24-use-case/src/interactor/form_answer/find_by_form_id.rs b/crates/sos24-use-case/src/interactor/form_answer/find_by_form_id.rs index 89eb35ee..26cebf7e 100644 --- a/crates/sos24-use-case/src/interactor/form_answer/find_by_form_id.rs +++ b/crates/sos24-use-case/src/interactor/form_answer/find_by_form_id.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use sos24_domain::repository::project::ProjectRepository; use sos24_domain::{ ensure, entity::{form::FormId, permission::Permissions}, @@ -23,7 +24,7 @@ impl FormAnswerUseCase { ensure!(actor.has_permission(Permissions::READ_FORM_ANSWER_ALL)); let form_id = FormId::try_from(form_id)?; - let _form = self + let raw_form = self .repositories .form_repository() .find_by_id(form_id.clone()) @@ -36,10 +37,23 @@ impl FormAnswerUseCase { .find_by_form_id(form_id.clone()) .await?; - let form_answer_list = raw_form_answer_list - .into_iter() - .map(FormAnswerDto::from_entity); - Ok(form_answer_list.collect()) + let mut form_answer_list = Vec::new(); + for raw_form_answer in raw_form_answer_list { + let project_id = raw_form_answer.value.project_id(); + let raw_project = self + .repositories + .project_repository() + .find_by_id(project_id.clone()) + .await? + .ok_or(FormAnswerUseCaseError::ProjectNotFound(project_id.clone()))?; + form_answer_list.push(FormAnswerDto::from_entity(( + raw_form_answer, + raw_project, + raw_form.clone(), + ))); + } + + Ok(form_answer_list) } } diff --git a/crates/sos24-use-case/src/interactor/form_answer/find_by_id.rs b/crates/sos24-use-case/src/interactor/form_answer/find_by_id.rs index 8a05dc3c..cdc7d210 100644 --- a/crates/sos24-use-case/src/interactor/form_answer/find_by_id.rs +++ b/crates/sos24-use-case/src/interactor/form_answer/find_by_id.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use sos24_domain::repository::form::FormRepository; use sos24_domain::{ ensure, entity::form_answer::FormAnswerId, @@ -30,15 +31,27 @@ impl FormAnswerUseCase { .ok_or(FormAnswerUseCaseError::NotFound(id))?; let project_id = form_answer.value.project_id(); - let project = self + let raw_project = self .repositories .project_repository() .find_by_id(project_id.clone()) .await? .ok_or(FormAnswerUseCaseError::ProjectNotFound(project_id.clone()))?; - ensure!(project.value.is_visible_to(&actor)); + ensure!(raw_project.value.is_visible_to(&actor)); - Ok(FormAnswerDto::from_entity(form_answer)) + let form_id = form_answer.value.form_id(); + let raw_form = self + .repositories + .form_repository() + .find_by_id(form_id.clone()) + .await? + .ok_or(FormAnswerUseCaseError::FormNotFound(form_id.clone()))?; + + Ok(FormAnswerDto::from_entity(( + form_answer, + raw_project, + raw_form, + ))) } } @@ -75,6 +88,10 @@ mod tests { fixture::user::id1(), )))) }); + repositories + .form_repository_mut() + .expect_find_by_id() + .returning(|_| Ok(Some(fixture::date::with(fixture::form::form1())))); let use_case = FormAnswerUseCase::new(Arc::new(repositories)); let ctx = Context::with_actor(fixture::actor::actor1(UserRole::General)); @@ -136,6 +153,10 @@ mod tests { fixture::user::id2(), )))) }); + repositories + .form_repository_mut() + .expect_find_by_id() + .returning(|_| Ok(Some(fixture::date::with(fixture::form::form1())))); let use_case = FormAnswerUseCase::new(Arc::new(repositories)); let ctx = Context::with_actor(fixture::actor::actor1(UserRole::Committee)); diff --git a/crates/sos24-use-case/src/interactor/form_answer/find_by_project_id.rs b/crates/sos24-use-case/src/interactor/form_answer/find_by_project_id.rs index a5d69b78..ad3eda37 100644 --- a/crates/sos24-use-case/src/interactor/form_answer/find_by_project_id.rs +++ b/crates/sos24-use-case/src/interactor/form_answer/find_by_project_id.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use sos24_domain::repository::form::FormRepository; use sos24_domain::{ ensure, entity::project::ProjectId, @@ -36,10 +37,23 @@ impl FormAnswerUseCase { .find_by_project_id(project_id.clone()) .await?; - let form_answer_list = raw_form_answer_list - .into_iter() - .map(FormAnswerDto::from_entity); - Ok(form_answer_list.collect()) + let mut form_answer_list = Vec::new(); + for raw_form_answer in raw_form_answer_list { + let form_id = raw_form_answer.value.form_id(); + let raw_form = self + .repositories + .form_repository() + .find_by_id(form_id.clone()) + .await? + .ok_or(FormAnswerUseCaseError::FormNotFound(form_id.clone()))?; + form_answer_list.push(FormAnswerDto::from_entity(( + raw_form_answer, + project.clone(), + raw_form, + ))); + } + + Ok(form_answer_list) } } diff --git a/crates/sos24-use-case/src/interactor/form_answer/list.rs b/crates/sos24-use-case/src/interactor/form_answer/list.rs index 0bcc7af8..cf45ac77 100644 --- a/crates/sos24-use-case/src/interactor/form_answer/list.rs +++ b/crates/sos24-use-case/src/interactor/form_answer/list.rs @@ -1,5 +1,7 @@ use std::sync::Arc; +use sos24_domain::repository::form::FormRepository; +use sos24_domain::repository::project::ProjectRepository; use sos24_domain::{ ensure, entity::permission::Permissions, @@ -19,10 +21,33 @@ impl FormAnswerUseCase { ensure!(actor.has_permission(Permissions::READ_FORM_ANSWER_ALL)); let raw_form_answer_list = self.repositories.form_answer_repository().list().await?; - let form_answer_list = raw_form_answer_list - .into_iter() - .map(FormAnswerDto::from_entity); - Ok(form_answer_list.collect()) + + let mut form_answer_list = Vec::new(); + for raw_form_answer in raw_form_answer_list { + let project_id = raw_form_answer.value.project_id(); + let raw_project = self + .repositories + .project_repository() + .find_by_id(project_id.clone()) + .await? + .ok_or(FormAnswerUseCaseError::ProjectNotFound(project_id.clone()))?; + + let form_id = raw_form_answer.value.form_id(); + let raw_form = self + .repositories + .form_repository() + .find_by_id(form_id.clone()) + .await? + .ok_or(FormAnswerUseCaseError::FormNotFound(form_id.clone()))?; + + form_answer_list.push(FormAnswerDto::from_entity(( + raw_form_answer, + raw_project, + raw_form, + ))); + } + + Ok(form_answer_list) } } diff --git a/crates/sos24-use-case/src/interactor/invitation.rs b/crates/sos24-use-case/src/interactor/invitation.rs index 86b1f2cf..98783254 100644 --- a/crates/sos24-use-case/src/interactor/invitation.rs +++ b/crates/sos24-use-case/src/interactor/invitation.rs @@ -35,6 +35,8 @@ pub enum InvitationUseCaseError { ProjectNotFound(ProjectId), #[error("Already owner or sub-owner")] AlreadyOwnerOrSubOwner, + #[error("User not found: {0:?}")] + UserNotFound(UserId), #[error(transparent)] ProjectError(#[from] ProjectError), diff --git a/crates/sos24-use-case/src/interactor/invitation/find_by_id.rs b/crates/sos24-use-case/src/interactor/invitation/find_by_id.rs index bb1c1c54..56b4f502 100644 --- a/crates/sos24-use-case/src/interactor/invitation/find_by_id.rs +++ b/crates/sos24-use-case/src/interactor/invitation/find_by_id.rs @@ -1,5 +1,7 @@ use std::sync::Arc; +use sos24_domain::repository::project::ProjectRepository; +use sos24_domain::repository::user::UserRepository; use sos24_domain::{ ensure, entity::invitation::InvitationId, @@ -31,7 +33,27 @@ impl InvitationUseCase { ensure!(raw_invitation.value.is_visible_to(&actor)); - Ok(InvitationDto::from_entity(raw_invitation)) + let inviter_id = raw_invitation.value.inviter(); + let raw_inviter = self + .repositories + .user_repository() + .find_by_id(inviter_id.clone()) + .await? + .ok_or(InvitationUseCaseError::UserNotFound(inviter_id.clone()))?; + + let project_id = raw_invitation.value.project_id(); + let raw_project = self + .repositories + .project_repository() + .find_by_id(project_id.clone()) + .await? + .ok_or(InvitationUseCaseError::ProjectNotFound(project_id.clone()))?; + + Ok(InvitationDto::from_entity(( + raw_invitation, + raw_inviter, + raw_project, + ))) } } @@ -62,6 +84,22 @@ mod tests { InvitationPosition::SubOwner, )))) }); + repositories + .user_repository_mut() + .expect_find_by_id() + .returning(|_| { + Ok(Some(fixture::date::with(fixture::user::user1( + UserRole::General, + )))) + }); + repositories + .project_repository_mut() + .expect_find_by_id() + .returning(|_| { + Ok(Some(fixture::date::with(fixture::project::project1( + fixture::user::id1(), + )))) + }); let use_case = InvitationUseCase::new_for_test(repositories); let ctx = Context::with_actor(fixture::actor::actor1(UserRole::General)); @@ -111,6 +149,22 @@ mod tests { InvitationPosition::SubOwner, )))) }); + repositories + .user_repository_mut() + .expect_find_by_id() + .returning(|_| { + Ok(Some(fixture::date::with(fixture::user::user1( + UserRole::General, + )))) + }); + repositories + .project_repository_mut() + .expect_find_by_id() + .returning(|_| { + Ok(Some(fixture::date::with(fixture::project::project1( + fixture::user::id1(), + )))) + }); let use_case = InvitationUseCase::new_for_test(repositories); let ctx = Context::with_actor(fixture::actor::actor1(UserRole::Committee)); diff --git a/crates/sos24-use-case/src/interactor/invitation/list.rs b/crates/sos24-use-case/src/interactor/invitation/list.rs index 481e4900..6fbdbc49 100644 --- a/crates/sos24-use-case/src/interactor/invitation/list.rs +++ b/crates/sos24-use-case/src/interactor/invitation/list.rs @@ -1,5 +1,7 @@ use std::sync::Arc; +use sos24_domain::repository::project::ProjectRepository; +use sos24_domain::repository::user::UserRepository; use sos24_domain::{ ensure, entity::permission::Permissions, @@ -19,10 +21,33 @@ impl InvitationUseCase { ensure!(actor.has_permission(Permissions::READ_INVITATION_ALL)); let raw_invitation_list = self.repositories.invitation_repository().list().await?; - let invitation_list = raw_invitation_list - .into_iter() - .map(InvitationDto::from_entity); - Ok(invitation_list.collect()) + + let mut invitation_list = Vec::new(); + for raw_invitation in raw_invitation_list { + let inviter_id = raw_invitation.value.inviter(); + let raw_inviter = self + .repositories + .user_repository() + .find_by_id(inviter_id.clone()) + .await? + .ok_or(InvitationUseCaseError::UserNotFound(inviter_id.clone()))?; + + let project_id = raw_invitation.value.project_id(); + let raw_project = self + .repositories + .project_repository() + .find_by_id(project_id.clone()) + .await? + .ok_or(InvitationUseCaseError::ProjectNotFound(project_id.clone()))?; + + invitation_list.push(InvitationDto::from_entity(( + raw_invitation, + raw_inviter, + raw_project, + ))); + } + + Ok(invitation_list) } } diff --git a/crates/sos24-use-case/src/interactor/project.rs b/crates/sos24-use-case/src/interactor/project.rs index 6f78ae68..f6ce5690 100644 --- a/crates/sos24-use-case/src/interactor/project.rs +++ b/crates/sos24-use-case/src/interactor/project.rs @@ -2,6 +2,8 @@ use std::sync::Arc; use thiserror::Error; +use sos24_domain::entity::user::UserId; +use sos24_domain::repository::user::UserRepositoryError; use sos24_domain::{ entity::{ permission::PermissionDeniedError, @@ -28,7 +30,11 @@ pub enum ProjectUseCaseError { AlreadyOwnedProject(ProjectId), #[error("Project applications are not being accepted")] ApplicationsNotAccepted, + #[error("User not found: {0:?}")] + UserNotFound(UserId), + #[error(transparent)] + UserRepositoryError(#[from] UserRepositoryError), #[error(transparent)] BoundedStringError(#[from] BoundedStringError), #[error(transparent)] diff --git a/crates/sos24-use-case/src/interactor/project/find_by_id.rs b/crates/sos24-use-case/src/interactor/project/find_by_id.rs index 14b37817..04878995 100644 --- a/crates/sos24-use-case/src/interactor/project/find_by_id.rs +++ b/crates/sos24-use-case/src/interactor/project/find_by_id.rs @@ -4,6 +4,7 @@ use sos24_domain::ensure; use sos24_domain::entity::permission::Permissions; use sos24_domain::entity::project::ProjectId; use sos24_domain::repository::project::ProjectRepository; +use sos24_domain::repository::user::UserRepository; use sos24_domain::repository::Repositories; use crate::context::Context; @@ -29,7 +30,27 @@ impl ProjectUseCase { ensure!(raw_project.value.is_visible_to(&actor)); - let mut project = ProjectDto::from_entity(raw_project); + let owner_id = raw_project.value.owner_id(); + let raw_owner = self + .repositories + .user_repository() + .find_by_id(owner_id.clone()) + .await? + .ok_or(ProjectUseCaseError::UserNotFound(owner_id.clone()))?; + + let sub_owner_id = raw_project.value.sub_owner_id(); + let raw_sub_owner = match sub_owner_id { + Some(sub_owner_id) => Some( + self.repositories + .user_repository() + .find_by_id(sub_owner_id.clone()) + .await? + .ok_or(ProjectUseCaseError::UserNotFound(sub_owner_id.clone()))?, + ), + None => None, + }; + + let mut project = ProjectDto::from_entity((raw_project, raw_owner, raw_sub_owner)); if !actor.has_permission(Permissions::READ_PROJECT_ALL) { project.remarks = None; } @@ -59,6 +80,14 @@ mod tests { fixture::user::id1(), )))) }); + repositories + .user_repository_mut() + .expect_find_by_id() + .returning(|_| { + Ok(Some(fixture::date::with(fixture::user::user1( + UserRole::General, + )))) + }); let use_case = ProjectUseCase::new_for_test(repositories); let ctx = Context::with_actor(fixture::actor::actor1(UserRole::General)); @@ -104,6 +133,14 @@ mod tests { fixture::user::id2(), )))) }); + repositories + .user_repository_mut() + .expect_find_by_id() + .returning(|_| { + Ok(Some(fixture::date::with(fixture::user::user1( + UserRole::Committee, + )))) + }); let use_case = ProjectUseCase::new_for_test(repositories); let ctx = Context::with_actor(fixture::actor::actor1(UserRole::Committee)); diff --git a/crates/sos24-use-case/src/interactor/project/find_owned.rs b/crates/sos24-use-case/src/interactor/project/find_owned.rs index 9ba393d6..006fd318 100644 --- a/crates/sos24-use-case/src/interactor/project/find_owned.rs +++ b/crates/sos24-use-case/src/interactor/project/find_owned.rs @@ -2,6 +2,7 @@ use std::sync::Arc; use sos24_domain::ensure; use sos24_domain::entity::permission::Permissions; +use sos24_domain::repository::user::UserRepository; use sos24_domain::repository::Repositories; use crate::context::{Context, OwnedProject}; @@ -17,15 +18,35 @@ impl ProjectUseCase { let actor = ctx.actor(Arc::clone(&self.repositories)).await?; let project = ctx.project(Arc::clone(&self.repositories)).await?; - let project = match project { + let raw_project = match project { Some(OwnedProject::Owner(project)) => project, Some(OwnedProject::SubOwner(project)) => project, None => return Ok(None), }; - ensure!(project.value.is_visible_to(&actor)); + ensure!(raw_project.value.is_visible_to(&actor)); + + let owner_id = raw_project.value.owner_id(); + let raw_owner = self + .repositories + .user_repository() + .find_by_id(owner_id.clone()) + .await? + .ok_or(ProjectUseCaseError::UserNotFound(owner_id.clone()))?; + + let sub_owner_id = raw_project.value.sub_owner_id(); + let raw_sub_owner = match sub_owner_id { + Some(sub_owner_id) => Some( + self.repositories + .user_repository() + .find_by_id(sub_owner_id.clone()) + .await? + .ok_or(ProjectUseCaseError::UserNotFound(sub_owner_id.clone()))?, + ), + None => None, + }; - let mut project = ProjectDto::from_entity(project); + let mut project = ProjectDto::from_entity((raw_project, raw_owner, raw_sub_owner)); if !actor.has_permission(Permissions::READ_PROJECT_ALL) { project.remarks = None; } diff --git a/crates/sos24-use-case/src/interactor/project/list.rs b/crates/sos24-use-case/src/interactor/project/list.rs index 00b2bb12..a198ad45 100644 --- a/crates/sos24-use-case/src/interactor/project/list.rs +++ b/crates/sos24-use-case/src/interactor/project/list.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use sos24_domain::ensure; use sos24_domain::entity::permission::Permissions; use sos24_domain::repository::project::ProjectRepository; +use sos24_domain::repository::user::UserRepository; use sos24_domain::repository::Repositories; use crate::context::Context; @@ -16,8 +17,36 @@ impl ProjectUseCase { ensure!(actor.has_permission(Permissions::READ_PROJECT_ALL)); let raw_project_list = self.repositories.project_repository().list().await?; - let project_list = raw_project_list.into_iter().map(ProjectDto::from_entity); - Ok(project_list.collect()) + + let mut project_list = Vec::new(); + for raw_project in raw_project_list { + let owner_id = raw_project.value.owner_id(); + let raw_owner = self + .repositories + .user_repository() + .find_by_id(owner_id.clone()) + .await? + .ok_or(ProjectUseCaseError::UserNotFound(owner_id.clone()))?; + + let sub_owner_id = raw_project.value.sub_owner_id(); + let raw_sub_owner = match sub_owner_id { + Some(sub_owner_id) => Some( + self.repositories + .user_repository() + .find_by_id(sub_owner_id.clone()) + .await? + .ok_or(ProjectUseCaseError::UserNotFound(sub_owner_id.clone()))?, + ), + None => None, + }; + + project_list.push(ProjectDto::from_entity(( + raw_project, + raw_owner, + raw_sub_owner, + ))); + } + Ok(project_list) } } diff --git a/schema/form-answer.yml b/schema/form-answer.yml index 4a56afc9..d808e5b3 100644 --- a/schema/form-answer.yml +++ b/schema/form-answer.yml @@ -161,9 +161,13 @@ schemas: project_id: type: string format: uuid + project_title: + type: string form_id: type: string format: uuid + form_title: + type: string created_at: type: string format: date-time @@ -179,7 +183,9 @@ schemas: required: - id - project_id + - project_title - form_id + - form_title - created_at - updated_at - items diff --git a/schema/invitation.yml b/schema/invitation.yml index 2c4761fc..6d2798be 100644 --- a/schema/invitation.yml +++ b/schema/invitation.yml @@ -130,6 +130,8 @@ schemas: project_id: type: string format: uuid + project_title: + type: string position: type: string enum: @@ -148,6 +150,7 @@ schemas: required: - id - project_id + - project_title - position - created_at - updated_at