From a9c08c30993b47a020cbbf44051e01bedb24500d Mon Sep 17 00:00:00 2001 From: Arata Date: Mon, 29 Apr 2024 18:30:23 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20project=20repository=E3=81=A7Projec?= =?UTF-8?q?tWithOwners=E3=82=92=E8=BF=94=E3=81=99=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/sos24-domain/src/repository/project.rs | 27 +- .../sos24-domain/src/test/fixture/project.rs | 29 +- .../src/postgresql/project.rs | 419 +++++++++++++----- .../sos24-presentation/src/model/project.rs | 37 +- .../sos24-presentation/src/route/project.rs | 37 +- .../src/file/interactor/export_by_form_id.rs | 4 +- .../src/file/interactor/export_by_owner.rs | 6 +- .../src/file/interactor/find_by_id.rs | 22 +- .../interactor/check_form_and_send_notify.rs | 41 +- .../src/form/interactor/find_by_id.rs | 18 +- .../src/form/interactor/find_by_project_id.rs | 24 +- .../src/form_answer/interactor/create.rs | 41 +- .../interactor/export_by_form_id.rs | 4 +- .../form_answer/interactor/find_by_form_id.rs | 4 +- .../src/form_answer/interactor/find_by_id.rs | 28 +- .../interactor/find_by_project_id.rs | 24 +- .../src/form_answer/interactor/list.rs | 4 +- .../src/form_answer/interactor/update.rs | 24 +- .../src/invitation/interactor/find_by_id.rs | 22 +- .../invitation/interactor/find_or_create.rs | 28 +- .../src/invitation/interactor/list.rs | 4 +- .../src/invitation/interactor/receive.rs | 10 +- .../src/news/interactor/create.rs | 41 +- crates/sos24-use-case/src/project/dto.rs | 30 +- .../src/project/interactor/create.rs | 17 +- .../src/project/interactor/delete_by_id.rs | 12 +- .../src/project/interactor/find_by_id.rs | 54 +-- .../src/project/interactor/find_owned.rs | 35 +- .../src/project/interactor/list.rs | 30 +- .../src/project/interactor/update.rs | 38 +- crates/sos24-use-case/src/shared/context.rs | 15 +- .../src/user/interactor/find_by_id.rs | 21 +- 32 files changed, 696 insertions(+), 454 deletions(-) diff --git a/crates/sos24-domain/src/repository/project.rs b/crates/sos24-domain/src/repository/project.rs index 0b6ed362..35391706 100644 --- a/crates/sos24-domain/src/repository/project.rs +++ b/crates/sos24-domain/src/repository/project.rs @@ -3,7 +3,7 @@ use thiserror::Error; use crate::entity::{ project::{Project, ProjectId}, - user::UserId, + user::{User, UserId}, }; #[derive(Debug, Error)] @@ -12,20 +12,33 @@ pub enum ProjectRepositoryError { InternalError(#[from] anyhow::Error), } +#[derive(Debug)] +pub struct ProjectWithOwners { + pub project: Project, + pub owner: User, + pub sub_owner: Option, +} + #[automock] #[allow(async_fn_in_trait)] pub trait ProjectRepository: Send + Sync + 'static { - async fn list(&self) -> Result, ProjectRepositoryError>; + // command async fn create(&self, project: Project) -> Result<(), ProjectRepositoryError>; - async fn find_by_id(&self, id: ProjectId) -> Result, ProjectRepositoryError>; + async fn update(&self, project: Project) -> Result<(), ProjectRepositoryError>; + async fn delete_by_id(&self, id: ProjectId) -> Result<(), ProjectRepositoryError>; + + // query + async fn list(&self) -> Result, ProjectRepositoryError>; + async fn find_by_id( + &self, + id: ProjectId, + ) -> Result, ProjectRepositoryError>; async fn find_by_owner_id( &self, owner_id: UserId, - ) -> Result, ProjectRepositoryError>; + ) -> Result, ProjectRepositoryError>; async fn find_by_sub_owner_id( &self, sub_owner_id: UserId, - ) -> Result, ProjectRepositoryError>; - async fn update(&self, project: Project) -> Result<(), ProjectRepositoryError>; - async fn delete_by_id(&self, id: ProjectId) -> Result<(), ProjectRepositoryError>; + ) -> Result, ProjectRepositoryError>; } diff --git a/crates/sos24-domain/src/test/fixture/project.rs b/crates/sos24-domain/src/test/fixture/project.rs index cf1da1e5..e2781461 100644 --- a/crates/sos24-domain/src/test/fixture/project.rs +++ b/crates/sos24-domain/src/test/fixture/project.rs @@ -1,9 +1,12 @@ -use crate::entity::{ - project::{ - Project, ProjectAttributes, ProjectCategory, ProjectGroupName, ProjectId, ProjectIndex, - ProjectKanaGroupName, ProjectKanaTitle, ProjectTitle, +use crate::{ + entity::{ + project::{ + Project, ProjectAttributes, ProjectCategory, ProjectGroupName, ProjectId, ProjectIndex, + ProjectKanaGroupName, ProjectKanaTitle, ProjectTitle, + }, + user::{User, UserId}, }, - user::UserId, + repository::project::ProjectWithOwners, }; use super::datetime; @@ -58,6 +61,14 @@ pub fn project1(owner_id: UserId) -> Project { ) } +pub fn project_with_owners1(owner: User) -> ProjectWithOwners { + ProjectWithOwners { + project: project1(owner.id().clone()), + owner, + sub_owner: None, + } +} + pub fn id2() -> ProjectId { ProjectId::new(uuid::Uuid::from_u128(2)) } @@ -107,3 +118,11 @@ pub fn project2(owner_id: UserId) -> Project { datetime::now(), ) } + +pub fn project_with_owners2(owner: User) -> ProjectWithOwners { + ProjectWithOwners { + project: project2(owner.id().clone()), + owner, + sub_owner: None, + } +} diff --git a/crates/sos24-infrastructure/src/postgresql/project.rs b/crates/sos24-infrastructure/src/postgresql/project.rs index 4957d8b4..58023373 100644 --- a/crates/sos24-infrastructure/src/postgresql/project.rs +++ b/crates/sos24-infrastructure/src/postgresql/project.rs @@ -9,50 +9,119 @@ use sos24_domain::{ Project, ProjectAttributes, ProjectCategory, ProjectGroupName, ProjectId, ProjectIndex, ProjectKanaGroupName, ProjectKanaTitle, ProjectRemarks, ProjectTitle, }, - user::UserId, + user::{User, UserEmail, UserId, UserKanaName, UserName, UserPhoneNumber}, }, - repository::project::{ProjectRepository, ProjectRepositoryError}, + repository::project::{ProjectRepository, ProjectRepositoryError, ProjectWithOwners}, }; -use super::Postgresql; +use super::{user::UserRoleRow, Postgresql}; #[derive(FromRow)] -pub struct ProjectRow { - id: uuid::Uuid, - index: i32, - title: String, - kana_title: String, - group_name: String, - kana_group_name: String, - category: ProjectCategoryRow, - attributes: i32, +pub struct ProjectWithOwnersRow { + // project + project_id: uuid::Uuid, + project_index: i32, + project_title: String, + project_kana_title: String, + project_group_name: String, + project_kana_group_name: String, + project_category: ProjectCategoryRow, + project_attributes: i32, + project_owner_id: String, + project_sub_owner_id: Option, + project_remarks: Option, + project_created_at: chrono::DateTime, + project_updated_at: chrono::DateTime, + + // owner owner_id: String, - sub_owner_id: Option, - remarks: Option, + owner_name: String, + owner_kana_name: String, + owner_email: String, + owner_phone_number: String, + owner_role: UserRoleRow, + owner_created_at: chrono::DateTime, + owner_updated_at: chrono::DateTime, - created_at: chrono::DateTime, - updated_at: chrono::DateTime, + // sub_owner + sub_owner_id: Option, + sub_owner_name: Option, + sub_owner_kana_name: Option, + sub_owner_email: Option, + sub_owner_phone_number: Option, + sub_owner_role: Option, + sub_owner_created_at: Option>, + sub_owner_updated_at: Option>, } -impl TryFrom for Project { +impl TryFrom for ProjectWithOwners { type Error = anyhow::Error; - fn try_from(value: ProjectRow) -> Result { - Ok(Project::new( - ProjectId::new(value.id), - ProjectIndex::new(value.index), - ProjectTitle::try_from(value.title)?, - ProjectKanaTitle::new(value.kana_title), - ProjectGroupName::try_from(value.group_name)?, - ProjectKanaGroupName::new(value.kana_group_name), - value.category.into(), - ProjectAttributes::from_bits(value.attributes as u32) + fn try_from(value: ProjectWithOwnersRow) -> Result { + let project = Project::new( + ProjectId::new(value.project_id), + ProjectIndex::new(value.project_index), + ProjectTitle::try_from(value.project_title)?, + ProjectKanaTitle::new(value.project_kana_title), + ProjectGroupName::try_from(value.project_group_name)?, + ProjectKanaGroupName::new(value.project_kana_group_name), + value.project_category.into(), + ProjectAttributes::from_bits(value.project_attributes as u32) .ok_or(anyhow!("cannot convert project attributes"))?, + UserId::new(value.project_owner_id), + value.project_sub_owner_id.map(UserId::new), + value.project_remarks.map(ProjectRemarks::new), + DateTime::new(value.project_created_at), + DateTime::new(value.project_updated_at), + ); + + let owner = User::new( UserId::new(value.owner_id), - value.sub_owner_id.map(UserId::new), - value.remarks.map(ProjectRemarks::new), - DateTime::new(value.created_at), - DateTime::new(value.updated_at), - )) + UserName::new(value.owner_name), + UserKanaName::new(value.owner_kana_name), + UserEmail::try_from(value.owner_email)?, + UserPhoneNumber::new(value.owner_phone_number), + value.owner_role.into(), + DateTime::new(value.owner_created_at), + DateTime::new(value.owner_updated_at), + ); + + let sub_owner = match ( + value.sub_owner_id, + value.sub_owner_name, + value.sub_owner_kana_name, + value.sub_owner_email, + value.sub_owner_phone_number, + value.sub_owner_role, + value.sub_owner_created_at, + value.sub_owner_updated_at, + ) { + ( + Some(sub_owner_id), + Some(sub_owner_name), + Some(sub_owner_kana_name), + Some(sub_owner_email), + Some(sub_owner_phone_number), + Some(sub_owner_role), + Some(sub_owner_created_at), + Some(sub_owner_updated_at), + ) => Some(User::new( + UserId::new(sub_owner_id), + UserName::new(sub_owner_name), + UserKanaName::new(sub_owner_kana_name), + UserEmail::try_from(sub_owner_email)?, + UserPhoneNumber::new(sub_owner_phone_number), + sub_owner_role.into(), + DateTime::new(sub_owner_created_at), + DateTime::new(sub_owner_updated_at), + )), + _ => None, + }; + + Ok(ProjectWithOwners { + project, + owner, + sub_owner, + }) } } @@ -109,25 +178,6 @@ impl PgProjectRepository { } impl ProjectRepository for PgProjectRepository { - async fn list(&self) -> Result, ProjectRepositoryError> { - tracing::info!("企画一覧を取得します"); - - let project_list = sqlx::query_as!( - ProjectRow, - r#"SELECT id, index, title, kana_title, group_name, kana_group_name, category AS "category: ProjectCategoryRow", attributes, owner_id, sub_owner_id, remarks, created_at, updated_at - FROM projects - WHERE deleted_at IS NULL - ORDER BY index ASC"#) - .fetch(&*self.db) - .map(|row| Project::try_from(row?)) - .try_collect() - .await - .context("Failed to fetch project list")?; - - tracing::info!("企画一覧を取得しました"); - Ok(project_list) - } - async fn create(&self, project: Project) -> Result<(), ProjectRepositoryError> { tracing::info!("企画を作成します"); @@ -153,65 +203,6 @@ impl ProjectRepository for PgProjectRepository { Ok(()) } - async fn find_by_id(&self, id: ProjectId) -> Result, ProjectRepositoryError> { - tracing::info!("企画を取得します: {id:?}"); - - let project_row = sqlx::query_as!( - ProjectRow, - r#"SELECT id, index, title, kana_title, group_name, kana_group_name, category AS "category: ProjectCategoryRow", attributes, owner_id, sub_owner_id, remarks, created_at, updated_at - FROM projects - WHERE id = $1 AND deleted_at IS NULL"#, - id.clone().value() - ) - .fetch_optional(&*self.db) - .await - .context("Failed to fetch project")?; - - tracing::info!("企画を取得しました: {id:?}"); - Ok(project_row.map(Project::try_from).transpose()?) - } - - async fn find_by_owner_id( - &self, - owner_id: UserId, - ) -> Result, ProjectRepositoryError> { - tracing::info!("企画責任者に紐づく企画を取得します: {owner_id:?}"); - - let project_row = sqlx::query_as!( - ProjectRow, - r#"SELECT id, index, title, kana_title, group_name, kana_group_name, category AS "category: ProjectCategoryRow", attributes, owner_id, sub_owner_id, remarks, created_at, updated_at - FROM projects - WHERE owner_id = $1 AND deleted_at IS NULL"#, - owner_id.clone().value() - ) - .fetch_optional(&*self.db) - .await - .context("Failed to fetch project")?; - - tracing::info!("企画責任者に紐づく企画を取得しました: {owner_id:?}"); - Ok(project_row.map(Project::try_from).transpose()?) - } - - async fn find_by_sub_owner_id( - &self, - sub_owner_id: UserId, - ) -> Result, ProjectRepositoryError> { - tracing::info!("副企画責任者に紐づく企画を取得します: {sub_owner_id:?}"); - - let project_row = sqlx::query_as!( - ProjectRow, - r#"SELECT id, index, title, kana_title, group_name, kana_group_name, category AS "category: ProjectCategoryRow", attributes, owner_id, sub_owner_id, remarks, created_at, updated_at - FROM projects - WHERE sub_owner_id = $1 AND deleted_at IS NULL"#, - sub_owner_id.clone().value() - ).fetch_optional(&*self.db) - .await - .context("Failed to fetch project")?; - - tracing::info!("副企画責任者に紐づく企画を取得しました: {sub_owner_id:?}"); - Ok(project_row.map(Project::try_from).transpose()?) - } - async fn update(&self, project: Project) -> Result<(), ProjectRepositoryError> { tracing::info!("企画を更新します"); @@ -255,4 +246,212 @@ impl ProjectRepository for PgProjectRepository { tracing::info!("企画を削除しました: {id:?}"); Ok(()) } + + async fn list(&self) -> Result, ProjectRepositoryError> { + tracing::info!("企画一覧を取得します"); + + // 現在のsqlxはLEFT JOINで得られるnullableなフィールドの型をうまく推論できないので、明示的に指定する + // ref: https://github.com/launchbadge/sqlx/issues/2127 + let project_list = sqlx::query_as!( + ProjectWithOwnersRow, + r#"SELECT + projects.id AS "project_id", + projects.index AS "project_index", + projects.title AS "project_title", + projects.kana_title AS "project_kana_title", + projects.group_name AS "project_group_name", + projects.kana_group_name AS "project_kana_group_name", + projects.category AS "project_category: ProjectCategoryRow", + projects.attributes AS "project_attributes", + projects.owner_id AS "project_owner_id", + projects.sub_owner_id AS "project_sub_owner_id", + projects.remarks AS "project_remarks", + projects.created_at AS "project_created_at", + projects.updated_at AS "project_updated_at", + owners.id AS "owner_id", + owners.name AS "owner_name", + owners.kana_name AS "owner_kana_name", + owners.email AS "owner_email", + owners.phone_number AS "owner_phone_number", + owners.role AS "owner_role: UserRoleRow", + owners.created_at AS "owner_created_at", + owners.updated_at AS "owner_updated_at", + sub_owners.id AS "sub_owner_id?", + sub_owners.name AS "sub_owner_name?", + sub_owners.kana_name AS "sub_owner_kana_name?", + sub_owners.email AS "sub_owner_email?", + sub_owners.phone_number AS "sub_owner_phone_number?", + sub_owners.role AS "sub_owner_role?: UserRoleRow", + sub_owners.created_at AS "sub_owner_created_at?", + sub_owners.updated_at AS "sub_owner_updated_at?" + FROM projects + INNER JOIN users AS owners ON projects.owner_id = owners.id AND owners.deleted_at IS NULL + LEFT JOIN users AS sub_owners ON projects.sub_owner_id = sub_owners.id AND sub_owners.deleted_at IS NULL + WHERE projects.deleted_at IS NULL + ORDER BY projects.index ASC"# + ) + .fetch(&*self.db) + .map(|row| ProjectWithOwners::try_from(row?)) + .try_collect() + .await + .context("Failed to fetch project list")?; + + tracing::info!("企画一覧を取得しました"); + Ok(project_list) + } + + async fn find_by_id( + &self, + id: ProjectId, + ) -> Result, ProjectRepositoryError> { + tracing::info!("企画を取得します: {id:?}"); + + let project_row = sqlx::query_as!( + ProjectWithOwnersRow, + r#"SELECT + projects.id AS "project_id", + projects.index AS "project_index", + projects.title AS "project_title", + projects.kana_title AS "project_kana_title", + projects.group_name AS "project_group_name", + projects.kana_group_name AS "project_kana_group_name", + projects.category AS "project_category: ProjectCategoryRow", + projects.attributes AS "project_attributes", + projects.owner_id AS "project_owner_id", + projects.sub_owner_id AS "project_sub_owner_id", + projects.remarks AS "project_remarks", + projects.created_at AS "project_created_at", + projects.updated_at AS "project_updated_at", + owners.id AS "owner_id", + owners.name AS "owner_name", + owners.kana_name AS "owner_kana_name", + owners.email AS "owner_email", + owners.phone_number AS "owner_phone_number", + owners.role AS "owner_role: UserRoleRow", + owners.created_at AS "owner_created_at", + owners.updated_at AS "owner_updated_at", + sub_owners.id AS "sub_owner_id?", + sub_owners.name AS "sub_owner_name?", + sub_owners.kana_name AS "sub_owner_kana_name?", + sub_owners.email AS "sub_owner_email?", + sub_owners.phone_number AS "sub_owner_phone_number?", + sub_owners.role AS "sub_owner_role?: UserRoleRow", + sub_owners.created_at AS "sub_owner_created_at?", + sub_owners.updated_at AS "sub_owner_updated_at?" + FROM projects + INNER JOIN users AS owners ON projects.owner_id = owners.id AND owners.deleted_at IS NULL + LEFT JOIN users AS sub_owners ON projects.sub_owner_id = sub_owners.id AND sub_owners.deleted_at IS NULL + WHERE projects.id = $1 AND projects.deleted_at IS NULL"#, + id.clone().value() + ) + .fetch_optional(&*self.db) + .await + .context("Failed to fetch project")?; + + tracing::info!("企画を取得しました: {id:?}"); + Ok(project_row.map(ProjectWithOwners::try_from).transpose()?) + } + + async fn find_by_owner_id( + &self, + owner_id: UserId, + ) -> Result, ProjectRepositoryError> { + tracing::info!("企画責任者に紐づく企画を取得します: {owner_id:?}"); + + let project_row = sqlx::query_as!( + ProjectWithOwnersRow, + r#"SELECT + projects.id AS "project_id", + projects.index AS "project_index", + projects.title AS "project_title", + projects.kana_title AS "project_kana_title", + projects.group_name AS "project_group_name", + projects.kana_group_name AS "project_kana_group_name", + projects.category AS "project_category: ProjectCategoryRow", + projects.attributes AS "project_attributes", + projects.owner_id AS "project_owner_id", + projects.sub_owner_id AS "project_sub_owner_id", + projects.remarks AS "project_remarks", + projects.created_at AS "project_created_at", + projects.updated_at AS "project_updated_at", + owners.id AS "owner_id", + owners.name AS "owner_name", + owners.kana_name AS "owner_kana_name", + owners.email AS "owner_email", + owners.phone_number AS "owner_phone_number", + owners.role AS "owner_role: UserRoleRow", + owners.created_at AS "owner_created_at", + owners.updated_at AS "owner_updated_at", + sub_owners.id AS "sub_owner_id?", + sub_owners.name AS "sub_owner_name?", + sub_owners.kana_name AS "sub_owner_kana_name?", + sub_owners.email AS "sub_owner_email?", + sub_owners.phone_number AS "sub_owner_phone_number?", + sub_owners.role AS "sub_owner_role?: UserRoleRow", + sub_owners.created_at AS "sub_owner_created_at?", + sub_owners.updated_at AS "sub_owner_updated_at?" + FROM projects + INNER JOIN users AS owners ON projects.owner_id = owners.id AND owners.deleted_at IS NULL + LEFT JOIN users AS sub_owners ON projects.sub_owner_id = sub_owners.id AND sub_owners.deleted_at IS NULL + WHERE projects.owner_id = $1 AND projects.deleted_at IS NULL"#, + owner_id.clone().value() + ) + .fetch_optional(&*self.db) + .await + .context("Failed to fetch project")?; + + tracing::info!("企画責任者に紐づく企画を取得しました: {owner_id:?}"); + Ok(project_row.map(ProjectWithOwners::try_from).transpose()?) + } + + async fn find_by_sub_owner_id( + &self, + sub_owner_id: UserId, + ) -> Result, ProjectRepositoryError> { + tracing::info!("副企画責任者に紐づく企画を取得します: {sub_owner_id:?}"); + + let project_row = sqlx::query_as!( + ProjectWithOwnersRow, + r#"SELECT + projects.id AS "project_id", + projects.index AS "project_index", + projects.title AS "project_title", + projects.kana_title AS "project_kana_title", + projects.group_name AS "project_group_name", + projects.kana_group_name AS "project_kana_group_name", + projects.category AS "project_category: ProjectCategoryRow", + projects.attributes AS "project_attributes", + projects.owner_id AS "project_owner_id", + projects.sub_owner_id AS "project_sub_owner_id", + projects.remarks AS "project_remarks", + projects.created_at AS "project_created_at", + projects.updated_at AS "project_updated_at", + owners.id AS "owner_id", + owners.name AS "owner_name", + owners.kana_name AS "owner_kana_name", + owners.email AS "owner_email", + owners.phone_number AS "owner_phone_number", + owners.role AS "owner_role: UserRoleRow", + owners.created_at AS "owner_created_at", + owners.updated_at AS "owner_updated_at", + sub_owners.id AS "sub_owner_id?", + sub_owners.name AS "sub_owner_name?", + sub_owners.kana_name AS "sub_owner_kana_name?", + sub_owners.email AS "sub_owner_email?", + sub_owners.phone_number AS "sub_owner_phone_number?", + sub_owners.role AS "sub_owner_role?: UserRoleRow", + sub_owners.created_at AS "sub_owner_created_at?", + sub_owners.updated_at AS "sub_owner_updated_at?" + FROM projects + INNER JOIN users AS owners ON projects.owner_id = owners.id AND owners.deleted_at IS NULL + LEFT JOIN users AS sub_owners ON projects.sub_owner_id = sub_owners.id AND sub_owners.deleted_at IS NULL + WHERE projects.sub_owner_id = $1 AND projects.deleted_at IS NULL"#, + sub_owner_id.clone().value() + ).fetch_optional(&*self.db) + .await + .context("Failed to fetch project")?; + + tracing::info!("副企画責任者に紐づく企画を取得しました: {sub_owner_id:?}"); + Ok(project_row.map(ProjectWithOwners::try_from).transpose()?) + } } diff --git a/crates/sos24-presentation/src/model/project.rs b/crates/sos24-presentation/src/model/project.rs index b23a5a99..de42d357 100644 --- a/crates/sos24-presentation/src/model/project.rs +++ b/crates/sos24-presentation/src/model/project.rs @@ -1,15 +1,12 @@ use chrono_tz::Asia::Tokyo; use serde::{Deserialize, Serialize}; -use sos24_use_case::{ - project::{ - dto::{ - ProjectAttributeDto, ProjectAttributesDto, ProjectCategoriesDto, ProjectCategoryDto, - ProjectDto, - }, - interactor::{create::CreateProjectCommand, update::UpdateProjectCommand}, +use sos24_use_case::project::{ + dto::{ + ProjectAttributeDto, ProjectAttributesDto, ProjectCategoriesDto, ProjectCategoryDto, + ProjectDto, }, - user::dto::UserDto, + interactor::{create::CreateProjectCommand, update::UpdateProjectCommand}, }; use utoipa::ToSchema; @@ -139,10 +136,10 @@ pub struct ProjectToBeExported { group_name: String, #[serde(rename(serialize = "企画責任者"))] owner_name: String, - #[serde(rename(serialize = "企画責任者電話番号"))] - owner_phone_number: String, #[serde(rename(serialize = "企画責任者メールアドレス"))] owner_email: String, + #[serde(rename(serialize = "企画責任者電話番号"))] + owner_phone_number: String, #[serde(rename(serialize = "副企画責任者"))] sub_owner_name: Option, #[serde(rename(serialize = "副企画責任者メールアドレス"))] @@ -154,24 +151,24 @@ pub struct ProjectToBeExported { #[serde(rename(serialize = "企画属性"))] attributes: String, #[serde(rename(serialize = "備考"))] - remark: Option, + remarks: Option, #[serde(rename(serialize = "作成日時"))] created_at: String, } -impl From<(ProjectDto, UserDto, Option)> for ProjectToBeExported { - fn from((project, owner, sub_owner): (ProjectDto, UserDto, Option)) -> Self { +impl From for ProjectToBeExported { + fn from(project: ProjectDto) -> Self { ProjectToBeExported { id: project.index, - owner_name: owner.name, - sub_owner_name: sub_owner.as_ref().map(|it| it.name.clone()), - owner_email: owner.email, - sub_owner_email: sub_owner.as_ref().map(|it| it.email.clone()), - owner_phone_number: owner.phone_number, - sub_owner_phone_number: sub_owner.map(|it| it.phone_number.clone()), title: project.title, kana_title: project.kana_title, group_name: project.group_name, + owner_name: project.owner_name, + owner_email: project.owner_email, + owner_phone_number: project.owner_phone_number, + sub_owner_name: project.sub_owner_name, + sub_owner_email: project.sub_owner_email, + sub_owner_phone_number: project.sub_owner_phone_number, category: project.category.to_string(), attributes: project .attributes @@ -180,7 +177,7 @@ impl From<(ProjectDto, UserDto, Option)> for ProjectToBeExported { .map(ToString::to_string) .collect::>() .join(";"), - remark: project.remarks, + remarks: project.remarks, created_at: project.created_at.with_timezone(&Tokyo).to_rfc3339(), } } diff --git a/crates/sos24-presentation/src/route/project.rs b/crates/sos24-presentation/src/route/project.rs index 92e013e6..b3c35e13 100644 --- a/crates/sos24-presentation/src/route/project.rs +++ b/crates/sos24-presentation/src/route/project.rs @@ -110,38 +110,11 @@ pub async fn handle_export( } }; - let mut projects: Vec = Vec::new(); - - for project in raw_project_list { - let owner = match modules - .user_use_case() - .find_by_id(&ctx, 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 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, - }; - - projects.push(ProjectToBeExported::from((project, owner, sub_owner))); - } - - let data = serialize_to_csv(projects).map_err(|err| { + let project_list = raw_project_list + .into_iter() + .map(ProjectToBeExported::from) + .collect(); + let data = serialize_to_csv(project_list).map_err(|err| { tracing::error!("Failed to serialize to csv: {err:?}"); AppError::from(err) })?; diff --git a/crates/sos24-use-case/src/file/interactor/export_by_form_id.rs b/crates/sos24-use-case/src/file/interactor/export_by_form_id.rs index ba0a84c4..67c8a5f2 100644 --- a/crates/sos24-use-case/src/file/interactor/export_by_form_id.rs +++ b/crates/sos24-use-case/src/file/interactor/export_by_form_id.rs @@ -44,13 +44,13 @@ impl FileUseCase { let mut file_list = Vec::new(); for form_answer in form_answer_list { let project_id = form_answer.project_id().clone(); - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) .await? .ok_or(FileUseCaseError::ProjectNotFound(project_id))?; - let project = project.destruct(); + let project = project_with_owners.project.destruct(); let file_items = form_answer.list_file_items(); for (item_id, files) in file_items { diff --git a/crates/sos24-use-case/src/file/interactor/export_by_owner.rs b/crates/sos24-use-case/src/file/interactor/export_by_owner.rs index 47022efb..10dfcc82 100644 --- a/crates/sos24-use-case/src/file/interactor/export_by_owner.rs +++ b/crates/sos24-use-case/src/file/interactor/export_by_owner.rs @@ -25,13 +25,13 @@ impl FileUseCase { ensure!(actor.has_permission(Permissions::READ_FILE_ALL)); let owner_project = ProjectId::try_from(owner_project)?; - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(owner_project.clone()) .await? .ok_or(FileUseCaseError::ProjectNotFound(owner_project.clone()))?; - ensure!(project.is_visible_to(&actor)); + ensure!(project_with_owners.project.is_visible_to(&actor)); let file_list = self .repositories @@ -57,7 +57,7 @@ impl FileUseCase { } }); - let project = project.destruct(); + let project = project_with_owners.project.destruct(); Ok(ArchiveToBeExportedDto { filename: format!("{}_ファイル一覧.zip", project.title.value()), body: reader, diff --git a/crates/sos24-use-case/src/file/interactor/find_by_id.rs b/crates/sos24-use-case/src/file/interactor/find_by_id.rs index b6acae66..d5f99a8c 100644 --- a/crates/sos24-use-case/src/file/interactor/find_by_id.rs +++ b/crates/sos24-use-case/src/file/interactor/find_by_id.rs @@ -27,13 +27,13 @@ impl FileUseCase { .await? .ok_or(FileUseCaseError::NotFound(id))?; if let Some(project_id) = raw_file_data.owner().clone() { - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id) .await? .ok_or(FileUseCaseError::OwnerNotFound)?; - ensure!(project.is_visible_to(&actor)); + ensure!(project_with_owners.project.is_visible_to(&actor)); } let signed_url = self .repositories @@ -99,7 +99,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .file_object_repository_mut() .expect_generate_url() @@ -132,7 +136,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); let use_case = FileUseCase::new(Arc::new(repositories)); let ctx = TestContext::new(fixture::actor::actor1(UserRole::General)); @@ -166,7 +174,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::Committee), + ))) + }); repositories .file_object_repository_mut() .expect_generate_url() diff --git a/crates/sos24-use-case/src/form/interactor/check_form_and_send_notify.rs b/crates/sos24-use-case/src/form/interactor/check_form_and_send_notify.rs index 0d90978d..75030c7f 100644 --- a/crates/sos24-use-case/src/form/interactor/check_form_and_send_notify.rs +++ b/crates/sos24-use-case/src/form/interactor/check_form_and_send_notify.rs @@ -1,7 +1,7 @@ +use std::convert::identity; + use chrono_tz::Asia::Tokyo; -use sos24_domain::repository::{ - form::FormRepository, project::ProjectRepository, user::UserRepository, Repositories, -}; +use sos24_domain::repository::{form::FormRepository, project::ProjectRepository, Repositories}; use crate::{ form::{FormUseCase, FormUseCaseError}, @@ -27,29 +27,20 @@ impl FormUseCase { for form in form_list_to_notify { let target_project_list = project_list .iter() - .filter(|project| form.is_sent_to(&project)); - - let mut emails = Vec::new(); - for project in target_project_list { - let owner_id = project.owner_id().clone(); - let owner = self - .repositories - .user_repository() - .find_by_id(owner_id.clone()) - .await? - .ok_or(FormUseCaseError::UserNotFound(owner_id))?; - emails.push(owner.email().clone().value()); + .filter(|project_with_owners| form.is_sent_to(&project_with_owners.project)); - if let Some(sub_owner_id) = project.sub_owner_id().clone() { - let sub_owner = self - .repositories - .user_repository() - .find_by_id(sub_owner_id.clone()) - .await? - .ok_or(FormUseCaseError::UserNotFound(sub_owner_id))?; - emails.push(sub_owner.email().clone().value()); - } - } + let emails = target_project_list + .flat_map(|project_with_owners| { + [ + Some(project_with_owners.owner.email().clone().value()), + project_with_owners + .sub_owner + .as_ref() + .map(|it| it.email().clone().value()), + ] + }) + .filter_map(identity) + .collect::>(); let command = SendEmailCommand { from: Email { diff --git a/crates/sos24-use-case/src/form/interactor/find_by_id.rs b/crates/sos24-use-case/src/form/interactor/find_by_id.rs index 0f9afb09..23268d78 100644 --- a/crates/sos24-use-case/src/form/interactor/find_by_id.rs +++ b/crates/sos24-use-case/src/form/interactor/find_by_id.rs @@ -8,7 +8,7 @@ use sos24_domain::{ use crate::form::dto::FormDto; use crate::form::{FormUseCase, FormUseCaseError}; use crate::shared::adapter::Adapters; -use crate::shared::context::{ContextProvider, OwnedProject}; +use crate::shared::context::ContextProvider; impl FormUseCase { pub async fn find_by_id( @@ -27,14 +27,8 @@ impl FormUseCase { .await? .ok_or(FormUseCaseError::NotFound(form_id.clone()))?; - let project = ctx - .project(&*self.repositories) - .await? - .map(|project| match project { - OwnedProject::Owner(project) => project, - OwnedProject::SubOwner(project) => project, - }); - let project_id = project.map(|it| it.id().clone()); + let project_with_owners = ctx.project(&*self.repositories).await?; + let project_id = project_with_owners.map(|it| it.project.id().clone()); let raw_form_answer = match project_id { Some(project_id) => { @@ -71,7 +65,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_owner_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_answer_repository_mut() .expect_find_by_project_id_and_form_id() diff --git a/crates/sos24-use-case/src/form/interactor/find_by_project_id.rs b/crates/sos24-use-case/src/form/interactor/find_by_project_id.rs index e6471f4c..fac4eace 100644 --- a/crates/sos24-use-case/src/form/interactor/find_by_project_id.rs +++ b/crates/sos24-use-case/src/form/interactor/find_by_project_id.rs @@ -20,18 +20,18 @@ impl FormUseCase { let actor = ctx.actor(&*self.repositories).await?; let project_id = ProjectId::try_from(project_id)?; - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) .await? .ok_or(FormUseCaseError::ProjectNotFound(project_id.clone()))?; - ensure!(project.is_visible_to(&actor)); + ensure!(project_with_owners.project.is_visible_to(&actor)); let forms = self.repositories.form_repository().list().await?; let filtered_forms = forms .into_iter() - .filter(|form| form.is_sent_to(&project)) + .filter(|form| form.is_sent_to(&project_with_owners.project)) .filter(|form| form.is_started(ctx.requested_at())); // FIXME : N+1 @@ -68,7 +68,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_repository_mut() .expect_list() @@ -89,7 +93,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); let adapters = MockAdapters::default(); let use_case = FormUseCase::new(Arc::new(repositories), Arc::new(adapters)); @@ -111,7 +119,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::Committee), + ))) + }); repositories .form_repository_mut() .expect_list() diff --git a/crates/sos24-use-case/src/form_answer/interactor/create.rs b/crates/sos24-use-case/src/form_answer/interactor/create.rs index c32417ce..03edf2e8 100644 --- a/crates/sos24-use-case/src/form_answer/interactor/create.rs +++ b/crates/sos24-use-case/src/form_answer/interactor/create.rs @@ -13,7 +13,7 @@ use sos24_domain::{ use crate::form_answer::dto::FormAnswerItemDto; use crate::form_answer::{FormAnswerUseCase, FormAnswerUseCaseError}; -use crate::shared::context::{ContextProvider, OwnedProject}; +use crate::shared::context::ContextProvider; #[derive(Debug)] pub struct CreateFormAnswerCommand { @@ -30,11 +30,10 @@ impl FormAnswerUseCase { let actor = ctx.actor(&*self.repositories).await?; ensure!(actor.has_permission(Permissions::CREATE_FORM_ANSWER)); - let project_id = match ctx.project(&*self.repositories).await? { - Some(OwnedProject::Owner(project)) => project.id().clone(), - Some(OwnedProject::SubOwner(project)) => project.id().clone(), - None => return Err(FormAnswerUseCaseError::NotProjectOwner), + let Some(project_with_owners) = ctx.project(&*self.repositories).await? else { + return Err(FormAnswerUseCaseError::NotProjectOwner); }; + let project_id = project_with_owners.project.id().clone(); let form_answer = FormAnswer::create( project_id, @@ -46,7 +45,7 @@ impl FormAnswerUseCase { .collect::>()?, ); - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(form_answer.project_id().clone()) @@ -55,7 +54,7 @@ impl FormAnswerUseCase { form_answer.project_id().clone(), ))?; - ensure!(project.is_visible_to(&actor)); + ensure!(project_with_owners.project.is_visible_to(&actor)); let form = self .repositories @@ -132,16 +131,24 @@ mod tests { let mut repositories = MockRepositories::default(); repositories .project_repository_mut() - .expect_find_by_owner_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .expect_find_by_id() + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_answer_repository_mut() .expect_find_by_project_id_and_form_id() .returning(|_, _| Ok(None)); repositories .project_repository_mut() - .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .expect_find_by_owner_id() + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_repository_mut() .expect_find_by_id() @@ -203,7 +210,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_owner_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_answer_repository_mut() .expect_find_by_project_id_and_form_id() @@ -215,7 +226,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_repository_mut() .expect_find_by_id() diff --git a/crates/sos24-use-case/src/form_answer/interactor/export_by_form_id.rs b/crates/sos24-use-case/src/form_answer/interactor/export_by_form_id.rs index 97ea880b..30d379e0 100644 --- a/crates/sos24-use-case/src/form_answer/interactor/export_by_form_id.rs +++ b/crates/sos24-use-case/src/form_answer/interactor/export_by_form_id.rs @@ -51,13 +51,13 @@ impl FormAnswerUseCase { let mut form_answers = Vec::new(); for form_answer in form_answer_list { let project_id = form_answer.project_id; - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) .await? .ok_or(FormAnswerUseCaseError::ProjectNotFound(project_id.clone()))?; - let project = project.destruct(); + let project = project_with_owners.project.destruct(); let form_answer_item_values = form_item_ids .iter() diff --git a/crates/sos24-use-case/src/form_answer/interactor/find_by_form_id.rs b/crates/sos24-use-case/src/form_answer/interactor/find_by_form_id.rs index 758fa36c..90484722 100644 --- a/crates/sos24-use-case/src/form_answer/interactor/find_by_form_id.rs +++ b/crates/sos24-use-case/src/form_answer/interactor/find_by_form_id.rs @@ -35,7 +35,7 @@ impl FormAnswerUseCase { let mut form_answer_list = Vec::new(); for raw_form_answer in raw_form_answer_list { let project_id = raw_form_answer.project_id(); - let raw_project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) @@ -43,7 +43,7 @@ impl FormAnswerUseCase { .ok_or(FormAnswerUseCaseError::ProjectNotFound(project_id.clone()))?; form_answer_list.push(FormAnswerDto::from(( raw_form_answer, - raw_project, + project_with_owners.project, raw_form.clone(), ))); } diff --git a/crates/sos24-use-case/src/form_answer/interactor/find_by_id.rs b/crates/sos24-use-case/src/form_answer/interactor/find_by_id.rs index 92e68358..ce278727 100644 --- a/crates/sos24-use-case/src/form_answer/interactor/find_by_id.rs +++ b/crates/sos24-use-case/src/form_answer/interactor/find_by_id.rs @@ -26,13 +26,13 @@ impl FormAnswerUseCase { .ok_or(FormAnswerUseCaseError::NotFound(id))?; let project_id = form_answer.project_id(); - let raw_project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) .await? .ok_or(FormAnswerUseCaseError::ProjectNotFound(project_id.clone()))?; - ensure!(raw_project.is_visible_to(&actor)); + ensure!(project_with_owners.project.is_visible_to(&actor)); let form_id = form_answer.form_id(); let raw_form = self @@ -42,7 +42,11 @@ impl FormAnswerUseCase { .await? .ok_or(FormAnswerUseCaseError::FormNotFound(form_id.clone()))?; - Ok(FormAnswerDto::from((form_answer, raw_project, raw_form))) + Ok(FormAnswerDto::from(( + form_answer, + project_with_owners.project, + raw_form, + ))) } } @@ -74,7 +78,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_repository_mut() .expect_find_by_id() @@ -102,7 +110,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); let use_case = FormAnswerUseCase::new(Arc::new(repositories)); let ctx = TestContext::new(fixture::actor::actor1(UserRole::General)); @@ -131,7 +143,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .form_repository_mut() .expect_find_by_id() diff --git a/crates/sos24-use-case/src/form_answer/interactor/find_by_project_id.rs b/crates/sos24-use-case/src/form_answer/interactor/find_by_project_id.rs index 2c80669a..72a13391 100644 --- a/crates/sos24-use-case/src/form_answer/interactor/find_by_project_id.rs +++ b/crates/sos24-use-case/src/form_answer/interactor/find_by_project_id.rs @@ -18,13 +18,13 @@ impl FormAnswerUseCase { let actor = ctx.actor(&*self.repositories).await?; let project_id = ProjectId::try_from(project_id)?; - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) .await? .ok_or(FormAnswerUseCaseError::ProjectNotFound(project_id.clone()))?; - ensure!(project.is_visible_to(&actor)); + ensure!(project_with_owners.project.is_visible_to(&actor)); let raw_form_answer_list = self .repositories @@ -43,7 +43,7 @@ impl FormAnswerUseCase { .ok_or(FormAnswerUseCaseError::FormNotFound(form_id.clone()))?; form_answer_list.push(FormAnswerDto::from(( raw_form_answer, - project.clone(), + project_with_owners.project.clone(), raw_form, ))); } @@ -72,7 +72,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_answer_repository_mut() .expect_find_by_project_id() @@ -92,7 +96,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .form_answer_repository_mut() .expect_find_by_project_id() @@ -117,7 +125,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .form_answer_repository_mut() .expect_find_by_project_id() diff --git a/crates/sos24-use-case/src/form_answer/interactor/list.rs b/crates/sos24-use-case/src/form_answer/interactor/list.rs index 7c6e426f..e4b2c3e2 100644 --- a/crates/sos24-use-case/src/form_answer/interactor/list.rs +++ b/crates/sos24-use-case/src/form_answer/interactor/list.rs @@ -23,7 +23,7 @@ impl FormAnswerUseCase { let mut form_answer_list = Vec::new(); for raw_form_answer in raw_form_answer_list { let project_id = raw_form_answer.project_id(); - let raw_project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) @@ -40,7 +40,7 @@ impl FormAnswerUseCase { form_answer_list.push(FormAnswerDto::from(( raw_form_answer, - raw_project, + project_with_owners.project, raw_form, ))); } diff --git a/crates/sos24-use-case/src/form_answer/interactor/update.rs b/crates/sos24-use-case/src/form_answer/interactor/update.rs index 914ac6d7..34927ef3 100644 --- a/crates/sos24-use-case/src/form_answer/interactor/update.rs +++ b/crates/sos24-use-case/src/form_answer/interactor/update.rs @@ -7,7 +7,7 @@ use sos24_domain::{ use crate::{ form_answer::{dto::FormAnswerItemDto, FormAnswerUseCase, FormAnswerUseCaseError}, - shared::context::{ContextProvider, OwnedProject}, + shared::context::ContextProvider, }; pub struct UpdateFormAnswerCommand { @@ -22,13 +22,9 @@ impl FormAnswerUseCase { form_answer_data: UpdateFormAnswerCommand, ) -> Result<(), FormAnswerUseCaseError> { let actor = ctx.actor(&*self.repositories).await?; - let owned_project_id = - ctx.project(&*self.repositories) - .await? - .map(|project| match project { - OwnedProject::Owner(project) => project.id().clone(), - OwnedProject::SubOwner(project) => project.id().clone(), - }); + + let project_with_owners = ctx.project(&*self.repositories).await?; + let owned_project_id = project_with_owners.as_ref().map(|p| p.project.id().clone()); let id = FormAnswerId::try_from(form_answer_data.id)?; let form_answer = self @@ -89,7 +85,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_owner_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_answer_repository_mut() .expect_find_by_id() @@ -130,7 +130,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_owner_id() - .returning(|_| Ok(Some(fixture::project::project2(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners2( + fixture::user::user1(UserRole::General), + ))) + }); repositories .form_answer_repository_mut() .expect_find_by_id() diff --git a/crates/sos24-use-case/src/invitation/interactor/find_by_id.rs b/crates/sos24-use-case/src/invitation/interactor/find_by_id.rs index f50df7c1..c36e95ca 100644 --- a/crates/sos24-use-case/src/invitation/interactor/find_by_id.rs +++ b/crates/sos24-use-case/src/invitation/interactor/find_by_id.rs @@ -34,7 +34,7 @@ impl InvitationUseCase { .ok_or(InvitationUseCaseError::UserNotFound(inviter_id.clone()))?; let project_id = raw_invitation.project_id(); - let raw_project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) @@ -44,7 +44,7 @@ impl InvitationUseCase { Ok(InvitationDto::from(( raw_invitation, raw_inviter, - raw_project, + project_with_owners.project, ))) } } @@ -80,7 +80,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); let use_case = InvitationUseCase::new( Arc::new(repositories), fixture::project_application_period::applicable_period(), @@ -113,7 +117,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); let use_case = InvitationUseCase::new( Arc::new(repositories), fixture::project_application_period::applicable_period(), @@ -146,7 +154,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::Committee), + ))) + }); let use_case = InvitationUseCase::new( Arc::new(repositories), fixture::project_application_period::applicable_period(), diff --git a/crates/sos24-use-case/src/invitation/interactor/find_or_create.rs b/crates/sos24-use-case/src/invitation/interactor/find_or_create.rs index 8bf8e161..26e6a85a 100644 --- a/crates/sos24-use-case/src/invitation/interactor/find_or_create.rs +++ b/crates/sos24-use-case/src/invitation/interactor/find_or_create.rs @@ -53,7 +53,7 @@ impl InvitationUseCase { new_invitation.inviter().clone(), ))?; - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(new_invitation.project_id().clone()) @@ -62,7 +62,7 @@ impl InvitationUseCase { new_invitation.project_id().clone(), ))?; - ensure!(project.is_visible_to(&actor)); + ensure!(project_with_owners.project.is_visible_to(&actor)); let invitation_list = self .repositories @@ -116,7 +116,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .invitation_repository_mut() .expect_find_by_inviter() @@ -181,7 +185,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .invitation_repository_mut() .expect_create() @@ -220,7 +228,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .invitation_repository_mut() .expect_find_by_inviter() @@ -258,7 +270,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .invitation_repository_mut() .expect_find_by_inviter() diff --git a/crates/sos24-use-case/src/invitation/interactor/list.rs b/crates/sos24-use-case/src/invitation/interactor/list.rs index 2a480570..45eab05d 100644 --- a/crates/sos24-use-case/src/invitation/interactor/list.rs +++ b/crates/sos24-use-case/src/invitation/interactor/list.rs @@ -31,7 +31,7 @@ impl InvitationUseCase { .ok_or(InvitationUseCaseError::UserNotFound(inviter_id.clone()))?; let project_id = raw_invitation.project_id(); - let raw_project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) @@ -41,7 +41,7 @@ impl InvitationUseCase { invitation_list.push(InvitationDto::from(( raw_invitation, raw_inviter, - raw_project, + project_with_owners.project, ))); } diff --git a/crates/sos24-use-case/src/invitation/interactor/receive.rs b/crates/sos24-use-case/src/invitation/interactor/receive.rs index d6d168fa..411db1e6 100644 --- a/crates/sos24-use-case/src/invitation/interactor/receive.rs +++ b/crates/sos24-use-case/src/invitation/interactor/receive.rs @@ -34,14 +34,14 @@ impl InvitationUseCase { .ok_or(InvitationUseCaseError::NotFound(id.clone()))?; let project_id = invitation.project_id().clone(); - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(project_id.clone()) .await? .ok_or(InvitationUseCaseError::ProjectNotFound(project_id))?; - let mut new_project = project; + let mut new_project = project_with_owners.project; let user_id = UserId::new(ctx.user_id().clone()); match invitation.position() { InvitationPosition::Owner => new_project.set_owner_id(user_id)?, @@ -98,7 +98,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .project_repository_mut() .expect_update() diff --git a/crates/sos24-use-case/src/news/interactor/create.rs b/crates/sos24-use-case/src/news/interactor/create.rs index 9e60fd78..b9158fe8 100644 --- a/crates/sos24-use-case/src/news/interactor/create.rs +++ b/crates/sos24-use-case/src/news/interactor/create.rs @@ -1,3 +1,5 @@ +use std::convert::identity; + use sos24_domain::{ ensure, entity::{ @@ -8,7 +10,7 @@ use sos24_domain::{ }, repository::{ file_data::FileDataRepository, news::NewsRepository, project::ProjectRepository, - user::UserRepository, Repositories, + Repositories, }, }; @@ -72,29 +74,20 @@ impl NewsUseCase { let project_list = self.repositories.project_repository().list().await?; let target_project_list = project_list .into_iter() - .filter(|project| news.is_sent_to(&project)); - - let mut emails = Vec::new(); - for project in target_project_list { - let owner_id = project.owner_id().clone(); - let owner = self - .repositories - .user_repository() - .find_by_id(owner_id.clone()) - .await? - .ok_or(NewsUseCaseError::UserNotFound(owner_id))?; - emails.push(owner.email().clone().value()); - - if let Some(sub_owner_id) = project.sub_owner_id().clone() { - let sub_owner = self - .repositories - .user_repository() - .find_by_id(sub_owner_id.clone()) - .await? - .ok_or(NewsUseCaseError::UserNotFound(sub_owner_id))?; - emails.push(sub_owner.email().clone().value()); - } - } + .filter(|project_with_owners| news.is_sent_to(&project_with_owners.project)); + + let emails = target_project_list + .flat_map(|project_with_owners| { + [ + Some(project_with_owners.owner.email().clone().value()), + project_with_owners + .sub_owner + .as_ref() + .map(|it| it.email().clone().value()), + ] + }) + .filter_map(identity) + .collect::>(); let command = SendEmailCommand { from: Email { diff --git a/crates/sos24-use-case/src/project/dto.rs b/crates/sos24-use-case/src/project/dto.rs index 35c24483..e6a8c2af 100644 --- a/crates/sos24-use-case/src/project/dto.rs +++ b/crates/sos24-use-case/src/project/dto.rs @@ -2,9 +2,10 @@ use std::fmt; use std::fmt::Formatter; use sos24_domain::entity::project::ProjectCategories; -use sos24_domain::entity::project::{Project, ProjectAttributes, ProjectCategory}; +use sos24_domain::entity::project::{ProjectAttributes, ProjectCategory}; use sos24_domain::entity::project_application_period::ProjectApplicationPeriod; -use sos24_domain::entity::user::User; + +use sos24_domain::repository::project::ProjectWithOwners; #[derive(Debug)] pub struct ProjectDto { @@ -19,23 +20,28 @@ pub struct ProjectDto { pub owner_id: String, pub owner_name: String, pub owner_email: String, + pub owner_phone_number: String, pub sub_owner_id: Option, pub sub_owner_name: Option, pub sub_owner_email: Option, + pub sub_owner_phone_number: Option, pub remarks: Option, pub created_at: chrono::DateTime, pub updated_at: chrono::DateTime, } -impl From<(Project, User, Option)> for ProjectDto { - fn from(entity: (Project, User, Option)) -> Self { - let (project, owner, sub_owner) = entity; - let project = project.destruct(); - let owner = owner.destruct(); - let sub_owner = sub_owner.map(|it| it.destruct()); - let (sub_owner_name, sub_owner_email) = match sub_owner { - Some(user) => (Some(user.name.value()), Some(user.email.value())), - None => (None, None), +impl From for ProjectDto { + fn from(entity: ProjectWithOwners) -> Self { + let project = entity.project.destruct(); + let owner = entity.owner.destruct(); + let sub_owner = entity.sub_owner.map(|it| it.destruct()); + let (sub_owner_name, sub_owner_email, sub_owner_phone_number) = match sub_owner { + Some(user) => ( + Some(user.name.value()), + Some(user.email.value()), + Some(user.phone_number.value()), + ), + None => (None, None, None), }; Self { @@ -50,9 +56,11 @@ impl From<(Project, User, Option)> for ProjectDto { owner_id: project.owner_id.value(), owner_name: owner.name.value(), owner_email: owner.email.value(), + owner_phone_number: owner.phone_number.value(), sub_owner_id: project.sub_owner_id.map(|id| id.value()), sub_owner_name, sub_owner_email, + sub_owner_phone_number, remarks: project.remarks.map(|it| it.value()), created_at: project.created_at.value(), updated_at: project.updated_at.value(), diff --git a/crates/sos24-use-case/src/project/interactor/create.rs b/crates/sos24-use-case/src/project/interactor/create.rs index 1f1c376b..5b45f0a0 100644 --- a/crates/sos24-use-case/src/project/interactor/create.rs +++ b/crates/sos24-use-case/src/project/interactor/create.rs @@ -11,7 +11,7 @@ use sos24_domain::{ensure, entity::project::Project}; use crate::project::dto::ProjectAttributesDto; use crate::{ project::{dto::ProjectCategoryDto, ProjectUseCase, ProjectUseCaseError}, - shared::context::{ContextProvider, OwnedProject}, + shared::context::ContextProvider, }; #[derive(Debug)] @@ -44,13 +44,10 @@ impl ProjectUseCase { let project_id = { let lock = self.creation_lock.lock().await; - if let Some(project) = ctx.project(&*self.repositories).await? { - let project_id = match project { - OwnedProject::Owner(project) => project.id().clone(), - OwnedProject::SubOwner(project) => project.id().clone(), - }; + if let Some(project_with_owners) = ctx.project(&*self.repositories).await? { + let project_id = project_with_owners.project.id().clone(); return Err(ProjectUseCaseError::AlreadyOwnedProject(project_id)); - } + }; let project = Project::create( ProjectTitle::try_from(raw_project.title) @@ -139,7 +136,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_owner_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); repositories .project_repository_mut() .expect_find_by_sub_owner_id() diff --git a/crates/sos24-use-case/src/project/interactor/delete_by_id.rs b/crates/sos24-use-case/src/project/interactor/delete_by_id.rs index 3000215a..d27ef449 100644 --- a/crates/sos24-use-case/src/project/interactor/delete_by_id.rs +++ b/crates/sos24-use-case/src/project/interactor/delete_by_id.rs @@ -68,7 +68,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::Committee), + ))) + }); let use_case = ProjectUseCase::new( Arc::new(repositories), fixture::project_application_period::applicable_period(), @@ -92,7 +96,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .project_repository_mut() .expect_delete_by_id() diff --git a/crates/sos24-use-case/src/project/interactor/find_by_id.rs b/crates/sos24-use-case/src/project/interactor/find_by_id.rs index 9e91d365..2f7827d2 100644 --- a/crates/sos24-use-case/src/project/interactor/find_by_id.rs +++ b/crates/sos24-use-case/src/project/interactor/find_by_id.rs @@ -2,7 +2,6 @@ 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::project::dto::ProjectDto; @@ -18,36 +17,15 @@ impl ProjectUseCase { let actor = ctx.actor(&*self.repositories).await?; let id = ProjectId::try_from(id)?; - let raw_project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(id.clone()) .await? .ok_or(ProjectUseCaseError::NotFound(id))?; + ensure!(project_with_owners.project.is_visible_to(&actor)); - ensure!(raw_project.is_visible_to(&actor)); - - let owner_id = raw_project.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.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((raw_project, raw_owner, raw_sub_owner)); + let mut project = ProjectDto::from(project_with_owners); if !actor.has_permission(Permissions::READ_PROJECT_ALL) { project.remarks = None; } @@ -74,11 +52,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); - repositories - .user_repository_mut() - .expect_find_by_id() - .returning(|_| Ok(Some(fixture::user::user1(UserRole::General)))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::General), + ))) + }); let use_case = ProjectUseCase::new( Arc::new(repositories), fixture::project_application_period::applicable_period(), @@ -97,7 +75,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); let use_case = ProjectUseCase::new( Arc::new(repositories), fixture::project_application_period::applicable_period(), @@ -121,11 +103,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); - repositories - .user_repository_mut() - .expect_find_by_id() - .returning(|_| Ok(Some(fixture::user::user1(UserRole::Committee)))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); let use_case = ProjectUseCase::new( Arc::new(repositories), fixture::project_application_period::applicable_period(), diff --git a/crates/sos24-use-case/src/project/interactor/find_owned.rs b/crates/sos24-use-case/src/project/interactor/find_owned.rs index ff7f9310..323ef388 100644 --- a/crates/sos24-use-case/src/project/interactor/find_owned.rs +++ b/crates/sos24-use-case/src/project/interactor/find_owned.rs @@ -1,11 +1,10 @@ use sos24_domain::ensure; use sos24_domain::entity::permission::Permissions; -use sos24_domain::repository::user::UserRepository; use sos24_domain::repository::Repositories; use crate::{ project::{dto::ProjectDto, ProjectUseCase, ProjectUseCaseError}, - shared::context::{ContextProvider, OwnedProject}, + shared::context::ContextProvider, }; impl ProjectUseCase { @@ -14,37 +13,13 @@ impl ProjectUseCase { ctx: &impl ContextProvider, ) -> Result, ProjectUseCaseError> { let actor = ctx.actor(&*self.repositories).await?; - let project = ctx.project(&*self.repositories).await?; - let raw_project = match project { - Some(OwnedProject::Owner(project)) => project, - Some(OwnedProject::SubOwner(project)) => project, - None => return Ok(None), + let Some(project_with_owners) = ctx.project(&*self.repositories).await? else { + return Ok(None); }; + ensure!(project_with_owners.project.is_visible_to(&actor)); - ensure!(raw_project.is_visible_to(&actor)); - - let owner_id = raw_project.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.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((raw_project, raw_owner, raw_sub_owner)); + let mut project = ProjectDto::from(project_with_owners); if !actor.has_permission(Permissions::READ_PROJECT_ALL) { project.remarks = None; } diff --git a/crates/sos24-use-case/src/project/interactor/list.rs b/crates/sos24-use-case/src/project/interactor/list.rs index 982056a5..d634fe3a 100644 --- a/crates/sos24-use-case/src/project/interactor/list.rs +++ b/crates/sos24-use-case/src/project/interactor/list.rs @@ -1,7 +1,6 @@ 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::project::dto::ProjectDto; @@ -16,33 +15,8 @@ impl ProjectUseCase { let actor = ctx.actor(&*self.repositories).await?; ensure!(actor.has_permission(Permissions::READ_PROJECT_ALL)); - let raw_project_list = self.repositories.project_repository().list().await?; - - let mut project_list = Vec::new(); - for raw_project in raw_project_list { - let owner_id = raw_project.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.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((raw_project, raw_owner, raw_sub_owner))); - } - Ok(project_list) + let project_list = self.repositories.project_repository().list().await?; + Ok(project_list.into_iter().map(ProjectDto::from).collect()) } } diff --git a/crates/sos24-use-case/src/project/interactor/update.rs b/crates/sos24-use-case/src/project/interactor/update.rs index 4bd97dc0..49101620 100644 --- a/crates/sos24-use-case/src/project/interactor/update.rs +++ b/crates/sos24-use-case/src/project/interactor/update.rs @@ -32,15 +32,15 @@ impl ProjectUseCase { let actor = ctx.actor(&*self.repositories).await?; let id = ProjectId::try_from(project_data.id)?; - let project = self + let project_with_owners = self .repositories .project_repository() .find_by_id(id.clone()) .await? .ok_or(ProjectUseCaseError::NotFound(id))?; - ensure!(project.is_visible_to(&actor)); - ensure!(project.is_updatable_by(&actor)); + ensure!(project_with_owners.project.is_visible_to(&actor)); + ensure!(project_with_owners.project.is_updatable_by(&actor)); if !actor.has_permission(Permissions::UPDATE_PROJECT_ALL) && !self @@ -50,7 +50,7 @@ impl ProjectUseCase { return Err(ProjectUseCaseError::ApplicationsNotAccepted); } - let mut new_project = project; + let mut new_project = project_with_owners.project; new_project.set_title( &actor, ProjectTitle::try_from(project_data.title) @@ -100,7 +100,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::Committee), + ))) + }); repositories .project_repository_mut() .expect_update() @@ -135,7 +139,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id1())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user1(UserRole::Committee), + ))) + }); repositories .project_repository_mut() .expect_update() @@ -173,7 +181,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .project_repository_mut() .expect_update() @@ -213,7 +225,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .project_repository_mut() .expect_update() @@ -248,7 +264,11 @@ mod tests { repositories .project_repository_mut() .expect_find_by_id() - .returning(|_| Ok(Some(fixture::project::project1(fixture::user::id2())))); + .returning(|_| { + Ok(Some(fixture::project::project_with_owners1( + fixture::user::user2(UserRole::General), + ))) + }); repositories .project_repository_mut() .expect_update() diff --git a/crates/sos24-use-case/src/shared/context.rs b/crates/sos24-use-case/src/shared/context.rs index 162e2962..80b2ee95 100644 --- a/crates/sos24-use-case/src/shared/context.rs +++ b/crates/sos24-use-case/src/shared/context.rs @@ -1,11 +1,10 @@ use sos24_domain::{ entity::{ actor::Actor, - project::Project, user::{User, UserId}, }, repository::{ - project::{ProjectRepository, ProjectRepositoryError}, + project::{ProjectRepository, ProjectRepositoryError, ProjectWithOwners}, user::{UserRepository, UserRepositoryError}, Repositories, }, @@ -23,12 +22,6 @@ pub enum ContextError { ProjectRepositoryError(#[from] ProjectRepositoryError), } -#[derive(Debug)] -pub enum OwnedProject { - Owner(Project), - SubOwner(Project), -} - #[derive(Clone, Debug, Default)] pub struct Config { pub email_sender_address: String, @@ -59,14 +52,14 @@ pub trait ContextProvider: Send + Sync + 'static { async fn project( &self, repositories: &R, - ) -> Result, ContextError> { + ) -> Result, ContextError> { let user_id = UserId::new(self.user_id()); if let Some(project) = repositories .project_repository() .find_by_owner_id(user_id.clone()) .await? { - return Ok(Some(OwnedProject::Owner(project))); + return Ok(Some(project)); } if let Some(project) = repositories @@ -74,7 +67,7 @@ pub trait ContextProvider: Send + Sync + 'static { .find_by_sub_owner_id(user_id) .await? { - return Ok(Some(OwnedProject::SubOwner(project))); + return Ok(Some(project)); } Ok(None) diff --git a/crates/sos24-use-case/src/user/interactor/find_by_id.rs b/crates/sos24-use-case/src/user/interactor/find_by_id.rs index 3ada0717..310c3059 100644 --- a/crates/sos24-use-case/src/user/interactor/find_by_id.rs +++ b/crates/sos24-use-case/src/user/interactor/find_by_id.rs @@ -1,4 +1,4 @@ -use sos24_domain::entity::permission::PermissionDeniedError; +use sos24_domain::ensure; use sos24_domain::entity::project::Project; use sos24_domain::entity::user::UserId; use sos24_domain::repository::project::ProjectRepository; @@ -23,15 +23,10 @@ impl UserUseCase { .find_by_id(user_id.clone()) .await? .ok_or(UserUseCaseError::NotFound(user_id.clone()))?; + ensure!(raw_user.is_visible_to(&actor)); - if raw_user.is_visible_to(&actor) { - let raw_project = find_owned_project(user_id.clone(), &*self.repositories).await?; - Ok(UserDto::from((raw_user, raw_project))) - } else { - Err(UserUseCaseError::PermissionDeniedError( - PermissionDeniedError, - )) - } + let raw_project = find_owned_project(user_id.clone(), &*self.repositories).await?; + Ok(UserDto::from((raw_user, raw_project))) } } @@ -39,20 +34,20 @@ async fn find_owned_project( user_id: UserId, repositories: &impl Repositories, ) -> Result, UserUseCaseError> { - if let Some(project) = repositories + if let Some(project_with_owners) = repositories .project_repository() .find_by_owner_id(user_id.clone()) .await? { - return Ok(Some(project)); + return Ok(Some(project_with_owners.project)); } - if let Some(project) = repositories + if let Some(project_with_owners) = repositories .project_repository() .find_by_sub_owner_id(user_id.clone()) .await? { - return Ok(Some(project)); + return Ok(Some(project_with_owners.project)); } Ok(None) From 2f655cb646512d3ff3bdc9254c7990fff9053261 Mon Sep 17 00:00:00 2001 From: Arata Date: Mon, 29 Apr 2024 18:31:47 +0900 Subject: [PATCH 2/2] chore: cargo sqlx prepare --workspace --- ...c57318fbc7ff34a08f8d81b48e91a549002c7.json | 109 --------- ...85a856c2cd5956f6bd55f3334d6f58db6f064.json | 229 ++++++++++++++++++ ...4dbda6072cf3a78f7c7c362e97d677c3a38ad.json | 109 --------- ...089dc5c93dc1346f673d7e7def14ac66d5bd6.json | 107 -------- ...1b3bc7e7a9f1bc1da3e7c51326abe295ec0d9.json | 109 --------- ...7f036aee99474f80849c8a1f1547bd3cb56f9.json | 229 ++++++++++++++++++ ...44470f1827f91946d25a153bf59d9f9075449.json | 227 +++++++++++++++++ ...aad855fea9fed7a0703498de7e4c33b056d0b.json | 229 ++++++++++++++++++ 8 files changed, 914 insertions(+), 434 deletions(-) delete mode 100644 .sqlx/query-51c8cadd619fee8b15120d184aec57318fbc7ff34a08f8d81b48e91a549002c7.json create mode 100644 .sqlx/query-534b70416c30aad680775911a8185a856c2cd5956f6bd55f3334d6f58db6f064.json delete mode 100644 .sqlx/query-64addd99ada5f27837abfc94f724dbda6072cf3a78f7c7c362e97d677c3a38ad.json delete mode 100644 .sqlx/query-a5e0ecb3fc39f08f52f76e3a222089dc5c93dc1346f673d7e7def14ac66d5bd6.json delete mode 100644 .sqlx/query-ab69446a012c39c2608ac9f0c891b3bc7e7a9f1bc1da3e7c51326abe295ec0d9.json create mode 100644 .sqlx/query-c2bc88d40ec8f78e6a377b4d4de7f036aee99474f80849c8a1f1547bd3cb56f9.json create mode 100644 .sqlx/query-c7d81f6ff993bb8c6751d76b7fe44470f1827f91946d25a153bf59d9f9075449.json create mode 100644 .sqlx/query-e4c735693f487aa3911eb1708a1aad855fea9fed7a0703498de7e4c33b056d0b.json diff --git a/.sqlx/query-51c8cadd619fee8b15120d184aec57318fbc7ff34a08f8d81b48e91a549002c7.json b/.sqlx/query-51c8cadd619fee8b15120d184aec57318fbc7ff34a08f8d81b48e91a549002c7.json deleted file mode 100644 index f11f938a..00000000 --- a/.sqlx/query-51c8cadd619fee8b15120d184aec57318fbc7ff34a08f8d81b48e91a549002c7.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id, index, title, kana_title, group_name, kana_group_name, category AS \"category: ProjectCategoryRow\", attributes, owner_id, sub_owner_id, remarks, created_at, updated_at\n FROM projects\n WHERE id = $1 AND deleted_at IS NULL", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "index", - "type_info": "Int4" - }, - { - "ordinal": 2, - "name": "title", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "kana_title", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "group_name", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "kana_group_name", - "type_info": "Text" - }, - { - "ordinal": 6, - "name": "category: ProjectCategoryRow", - "type_info": { - "Custom": { - "name": "project_category", - "kind": { - "Enum": [ - "general", - "foods_with_kitchen", - "foods_without_kitchen", - "foods_without_cooking", - "stage_1a", - "stage_university_hall", - "stage_united" - ] - } - } - } - }, - { - "ordinal": 7, - "name": "attributes", - "type_info": "Int4" - }, - { - "ordinal": 8, - "name": "owner_id", - "type_info": "Text" - }, - { - "ordinal": 9, - "name": "sub_owner_id", - "type_info": "Text" - }, - { - "ordinal": 10, - "name": "remarks", - "type_info": "Text" - }, - { - "ordinal": 11, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 12, - "name": "updated_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - true, - true, - false, - false - ] - }, - "hash": "51c8cadd619fee8b15120d184aec57318fbc7ff34a08f8d81b48e91a549002c7" -} diff --git a/.sqlx/query-534b70416c30aad680775911a8185a856c2cd5956f6bd55f3334d6f58db6f064.json b/.sqlx/query-534b70416c30aad680775911a8185a856c2cd5956f6bd55f3334d6f58db6f064.json new file mode 100644 index 00000000..98f1ef65 --- /dev/null +++ b/.sqlx/query-534b70416c30aad680775911a8185a856c2cd5956f6bd55f3334d6f58db6f064.json @@ -0,0 +1,229 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n projects.id AS \"project_id\",\n projects.index AS \"project_index\",\n projects.title AS \"project_title\",\n projects.kana_title AS \"project_kana_title\",\n projects.group_name AS \"project_group_name\",\n projects.kana_group_name AS \"project_kana_group_name\",\n projects.category AS \"project_category: ProjectCategoryRow\",\n projects.attributes AS \"project_attributes\",\n projects.owner_id AS \"project_owner_id\",\n projects.sub_owner_id AS \"project_sub_owner_id\",\n projects.remarks AS \"project_remarks\",\n projects.created_at AS \"project_created_at\",\n projects.updated_at AS \"project_updated_at\",\n owners.id AS \"owner_id\",\n owners.name AS \"owner_name\",\n owners.kana_name AS \"owner_kana_name\",\n owners.email AS \"owner_email\",\n owners.phone_number AS \"owner_phone_number\",\n owners.role AS \"owner_role: UserRoleRow\",\n owners.created_at AS \"owner_created_at\",\n owners.updated_at AS \"owner_updated_at\",\n sub_owners.id AS \"sub_owner_id?\",\n sub_owners.name AS \"sub_owner_name?\",\n sub_owners.kana_name AS \"sub_owner_kana_name?\",\n sub_owners.email AS \"sub_owner_email?\",\n sub_owners.phone_number AS \"sub_owner_phone_number?\",\n sub_owners.role AS \"sub_owner_role?: UserRoleRow\",\n sub_owners.created_at AS \"sub_owner_created_at?\",\n sub_owners.updated_at AS \"sub_owner_updated_at?\"\n FROM projects\n INNER JOIN users AS owners ON projects.owner_id = owners.id AND owners.deleted_at IS NULL\n LEFT JOIN users AS sub_owners ON projects.sub_owner_id = sub_owners.id AND sub_owners.deleted_at IS NULL\n WHERE projects.sub_owner_id = $1 AND projects.deleted_at IS NULL", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "project_id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "project_index", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "project_title", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "project_kana_title", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "project_group_name", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "project_kana_group_name", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "project_category: ProjectCategoryRow", + "type_info": { + "Custom": { + "name": "project_category", + "kind": { + "Enum": [ + "general", + "foods_with_kitchen", + "foods_without_kitchen", + "foods_without_cooking", + "stage_1a", + "stage_university_hall", + "stage_united" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "project_attributes", + "type_info": "Int4" + }, + { + "ordinal": 8, + "name": "project_owner_id", + "type_info": "Text" + }, + { + "ordinal": 9, + "name": "project_sub_owner_id", + "type_info": "Text" + }, + { + "ordinal": 10, + "name": "project_remarks", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "project_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "project_updated_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "owner_id", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "owner_name", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "owner_kana_name", + "type_info": "Text" + }, + { + "ordinal": 16, + "name": "owner_email", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "owner_phone_number", + "type_info": "Text" + }, + { + "ordinal": 18, + "name": "owner_role: UserRoleRow", + "type_info": { + "Custom": { + "name": "user_role", + "kind": { + "Enum": [ + "administrator", + "committee_operator", + "committee", + "general" + ] + } + } + } + }, + { + "ordinal": 19, + "name": "owner_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 20, + "name": "owner_updated_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 21, + "name": "sub_owner_id?", + "type_info": "Text" + }, + { + "ordinal": 22, + "name": "sub_owner_name?", + "type_info": "Text" + }, + { + "ordinal": 23, + "name": "sub_owner_kana_name?", + "type_info": "Text" + }, + { + "ordinal": 24, + "name": "sub_owner_email?", + "type_info": "Text" + }, + { + "ordinal": 25, + "name": "sub_owner_phone_number?", + "type_info": "Text" + }, + { + "ordinal": 26, + "name": "sub_owner_role?: UserRoleRow", + "type_info": { + "Custom": { + "name": "user_role", + "kind": { + "Enum": [ + "administrator", + "committee_operator", + "committee", + "general" + ] + } + } + } + }, + { + "ordinal": 27, + "name": "sub_owner_created_at?", + "type_info": "Timestamptz" + }, + { + "ordinal": 28, + "name": "sub_owner_updated_at?", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "534b70416c30aad680775911a8185a856c2cd5956f6bd55f3334d6f58db6f064" +} diff --git a/.sqlx/query-64addd99ada5f27837abfc94f724dbda6072cf3a78f7c7c362e97d677c3a38ad.json b/.sqlx/query-64addd99ada5f27837abfc94f724dbda6072cf3a78f7c7c362e97d677c3a38ad.json deleted file mode 100644 index 5888afae..00000000 --- a/.sqlx/query-64addd99ada5f27837abfc94f724dbda6072cf3a78f7c7c362e97d677c3a38ad.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id, index, title, kana_title, group_name, kana_group_name, category AS \"category: ProjectCategoryRow\", attributes, owner_id, sub_owner_id, remarks, created_at, updated_at\n FROM projects\n WHERE sub_owner_id = $1 AND deleted_at IS NULL", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "index", - "type_info": "Int4" - }, - { - "ordinal": 2, - "name": "title", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "kana_title", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "group_name", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "kana_group_name", - "type_info": "Text" - }, - { - "ordinal": 6, - "name": "category: ProjectCategoryRow", - "type_info": { - "Custom": { - "name": "project_category", - "kind": { - "Enum": [ - "general", - "foods_with_kitchen", - "foods_without_kitchen", - "foods_without_cooking", - "stage_1a", - "stage_university_hall", - "stage_united" - ] - } - } - } - }, - { - "ordinal": 7, - "name": "attributes", - "type_info": "Int4" - }, - { - "ordinal": 8, - "name": "owner_id", - "type_info": "Text" - }, - { - "ordinal": 9, - "name": "sub_owner_id", - "type_info": "Text" - }, - { - "ordinal": 10, - "name": "remarks", - "type_info": "Text" - }, - { - "ordinal": 11, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 12, - "name": "updated_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - true, - true, - false, - false - ] - }, - "hash": "64addd99ada5f27837abfc94f724dbda6072cf3a78f7c7c362e97d677c3a38ad" -} diff --git a/.sqlx/query-a5e0ecb3fc39f08f52f76e3a222089dc5c93dc1346f673d7e7def14ac66d5bd6.json b/.sqlx/query-a5e0ecb3fc39f08f52f76e3a222089dc5c93dc1346f673d7e7def14ac66d5bd6.json deleted file mode 100644 index 4c281f9f..00000000 --- a/.sqlx/query-a5e0ecb3fc39f08f52f76e3a222089dc5c93dc1346f673d7e7def14ac66d5bd6.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id, index, title, kana_title, group_name, kana_group_name, category AS \"category: ProjectCategoryRow\", attributes, owner_id, sub_owner_id, remarks, created_at, updated_at\n FROM projects\n WHERE deleted_at IS NULL\n ORDER BY index ASC", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "index", - "type_info": "Int4" - }, - { - "ordinal": 2, - "name": "title", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "kana_title", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "group_name", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "kana_group_name", - "type_info": "Text" - }, - { - "ordinal": 6, - "name": "category: ProjectCategoryRow", - "type_info": { - "Custom": { - "name": "project_category", - "kind": { - "Enum": [ - "general", - "foods_with_kitchen", - "foods_without_kitchen", - "foods_without_cooking", - "stage_1a", - "stage_university_hall", - "stage_united" - ] - } - } - } - }, - { - "ordinal": 7, - "name": "attributes", - "type_info": "Int4" - }, - { - "ordinal": 8, - "name": "owner_id", - "type_info": "Text" - }, - { - "ordinal": 9, - "name": "sub_owner_id", - "type_info": "Text" - }, - { - "ordinal": 10, - "name": "remarks", - "type_info": "Text" - }, - { - "ordinal": 11, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 12, - "name": "updated_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - true, - true, - false, - false - ] - }, - "hash": "a5e0ecb3fc39f08f52f76e3a222089dc5c93dc1346f673d7e7def14ac66d5bd6" -} diff --git a/.sqlx/query-ab69446a012c39c2608ac9f0c891b3bc7e7a9f1bc1da3e7c51326abe295ec0d9.json b/.sqlx/query-ab69446a012c39c2608ac9f0c891b3bc7e7a9f1bc1da3e7c51326abe295ec0d9.json deleted file mode 100644 index 7081147d..00000000 --- a/.sqlx/query-ab69446a012c39c2608ac9f0c891b3bc7e7a9f1bc1da3e7c51326abe295ec0d9.json +++ /dev/null @@ -1,109 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT id, index, title, kana_title, group_name, kana_group_name, category AS \"category: ProjectCategoryRow\", attributes, owner_id, sub_owner_id, remarks, created_at, updated_at\n FROM projects\n WHERE owner_id = $1 AND deleted_at IS NULL", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Uuid" - }, - { - "ordinal": 1, - "name": "index", - "type_info": "Int4" - }, - { - "ordinal": 2, - "name": "title", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "kana_title", - "type_info": "Text" - }, - { - "ordinal": 4, - "name": "group_name", - "type_info": "Text" - }, - { - "ordinal": 5, - "name": "kana_group_name", - "type_info": "Text" - }, - { - "ordinal": 6, - "name": "category: ProjectCategoryRow", - "type_info": { - "Custom": { - "name": "project_category", - "kind": { - "Enum": [ - "general", - "foods_with_kitchen", - "foods_without_kitchen", - "foods_without_cooking", - "stage_1a", - "stage_university_hall", - "stage_united" - ] - } - } - } - }, - { - "ordinal": 7, - "name": "attributes", - "type_info": "Int4" - }, - { - "ordinal": 8, - "name": "owner_id", - "type_info": "Text" - }, - { - "ordinal": 9, - "name": "sub_owner_id", - "type_info": "Text" - }, - { - "ordinal": 10, - "name": "remarks", - "type_info": "Text" - }, - { - "ordinal": 11, - "name": "created_at", - "type_info": "Timestamptz" - }, - { - "ordinal": 12, - "name": "updated_at", - "type_info": "Timestamptz" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false, - false, - false, - false, - false, - false, - true, - true, - false, - false - ] - }, - "hash": "ab69446a012c39c2608ac9f0c891b3bc7e7a9f1bc1da3e7c51326abe295ec0d9" -} diff --git a/.sqlx/query-c2bc88d40ec8f78e6a377b4d4de7f036aee99474f80849c8a1f1547bd3cb56f9.json b/.sqlx/query-c2bc88d40ec8f78e6a377b4d4de7f036aee99474f80849c8a1f1547bd3cb56f9.json new file mode 100644 index 00000000..f2b10e79 --- /dev/null +++ b/.sqlx/query-c2bc88d40ec8f78e6a377b4d4de7f036aee99474f80849c8a1f1547bd3cb56f9.json @@ -0,0 +1,229 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n projects.id AS \"project_id\",\n projects.index AS \"project_index\",\n projects.title AS \"project_title\",\n projects.kana_title AS \"project_kana_title\",\n projects.group_name AS \"project_group_name\",\n projects.kana_group_name AS \"project_kana_group_name\",\n projects.category AS \"project_category: ProjectCategoryRow\",\n projects.attributes AS \"project_attributes\",\n projects.owner_id AS \"project_owner_id\",\n projects.sub_owner_id AS \"project_sub_owner_id\",\n projects.remarks AS \"project_remarks\",\n projects.created_at AS \"project_created_at\",\n projects.updated_at AS \"project_updated_at\",\n owners.id AS \"owner_id\",\n owners.name AS \"owner_name\",\n owners.kana_name AS \"owner_kana_name\",\n owners.email AS \"owner_email\",\n owners.phone_number AS \"owner_phone_number\",\n owners.role AS \"owner_role: UserRoleRow\",\n owners.created_at AS \"owner_created_at\",\n owners.updated_at AS \"owner_updated_at\",\n sub_owners.id AS \"sub_owner_id?\",\n sub_owners.name AS \"sub_owner_name?\",\n sub_owners.kana_name AS \"sub_owner_kana_name?\",\n sub_owners.email AS \"sub_owner_email?\",\n sub_owners.phone_number AS \"sub_owner_phone_number?\",\n sub_owners.role AS \"sub_owner_role?: UserRoleRow\",\n sub_owners.created_at AS \"sub_owner_created_at?\",\n sub_owners.updated_at AS \"sub_owner_updated_at?\"\n FROM projects\n INNER JOIN users AS owners ON projects.owner_id = owners.id AND owners.deleted_at IS NULL\n LEFT JOIN users AS sub_owners ON projects.sub_owner_id = sub_owners.id AND sub_owners.deleted_at IS NULL\n WHERE projects.id = $1 AND projects.deleted_at IS NULL", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "project_id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "project_index", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "project_title", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "project_kana_title", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "project_group_name", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "project_kana_group_name", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "project_category: ProjectCategoryRow", + "type_info": { + "Custom": { + "name": "project_category", + "kind": { + "Enum": [ + "general", + "foods_with_kitchen", + "foods_without_kitchen", + "foods_without_cooking", + "stage_1a", + "stage_university_hall", + "stage_united" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "project_attributes", + "type_info": "Int4" + }, + { + "ordinal": 8, + "name": "project_owner_id", + "type_info": "Text" + }, + { + "ordinal": 9, + "name": "project_sub_owner_id", + "type_info": "Text" + }, + { + "ordinal": 10, + "name": "project_remarks", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "project_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "project_updated_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "owner_id", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "owner_name", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "owner_kana_name", + "type_info": "Text" + }, + { + "ordinal": 16, + "name": "owner_email", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "owner_phone_number", + "type_info": "Text" + }, + { + "ordinal": 18, + "name": "owner_role: UserRoleRow", + "type_info": { + "Custom": { + "name": "user_role", + "kind": { + "Enum": [ + "administrator", + "committee_operator", + "committee", + "general" + ] + } + } + } + }, + { + "ordinal": 19, + "name": "owner_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 20, + "name": "owner_updated_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 21, + "name": "sub_owner_id?", + "type_info": "Text" + }, + { + "ordinal": 22, + "name": "sub_owner_name?", + "type_info": "Text" + }, + { + "ordinal": 23, + "name": "sub_owner_kana_name?", + "type_info": "Text" + }, + { + "ordinal": 24, + "name": "sub_owner_email?", + "type_info": "Text" + }, + { + "ordinal": 25, + "name": "sub_owner_phone_number?", + "type_info": "Text" + }, + { + "ordinal": 26, + "name": "sub_owner_role?: UserRoleRow", + "type_info": { + "Custom": { + "name": "user_role", + "kind": { + "Enum": [ + "administrator", + "committee_operator", + "committee", + "general" + ] + } + } + } + }, + { + "ordinal": 27, + "name": "sub_owner_created_at?", + "type_info": "Timestamptz" + }, + { + "ordinal": 28, + "name": "sub_owner_updated_at?", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Uuid" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "c2bc88d40ec8f78e6a377b4d4de7f036aee99474f80849c8a1f1547bd3cb56f9" +} diff --git a/.sqlx/query-c7d81f6ff993bb8c6751d76b7fe44470f1827f91946d25a153bf59d9f9075449.json b/.sqlx/query-c7d81f6ff993bb8c6751d76b7fe44470f1827f91946d25a153bf59d9f9075449.json new file mode 100644 index 00000000..1e4f42fd --- /dev/null +++ b/.sqlx/query-c7d81f6ff993bb8c6751d76b7fe44470f1827f91946d25a153bf59d9f9075449.json @@ -0,0 +1,227 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n projects.id AS \"project_id\",\n projects.index AS \"project_index\",\n projects.title AS \"project_title\",\n projects.kana_title AS \"project_kana_title\",\n projects.group_name AS \"project_group_name\",\n projects.kana_group_name AS \"project_kana_group_name\",\n projects.category AS \"project_category: ProjectCategoryRow\",\n projects.attributes AS \"project_attributes\",\n projects.owner_id AS \"project_owner_id\",\n projects.sub_owner_id AS \"project_sub_owner_id\",\n projects.remarks AS \"project_remarks\",\n projects.created_at AS \"project_created_at\",\n projects.updated_at AS \"project_updated_at\",\n owners.id AS \"owner_id\",\n owners.name AS \"owner_name\",\n owners.kana_name AS \"owner_kana_name\",\n owners.email AS \"owner_email\",\n owners.phone_number AS \"owner_phone_number\",\n owners.role AS \"owner_role: UserRoleRow\",\n owners.created_at AS \"owner_created_at\",\n owners.updated_at AS \"owner_updated_at\",\n sub_owners.id AS \"sub_owner_id?\",\n sub_owners.name AS \"sub_owner_name?\",\n sub_owners.kana_name AS \"sub_owner_kana_name?\",\n sub_owners.email AS \"sub_owner_email?\",\n sub_owners.phone_number AS \"sub_owner_phone_number?\",\n sub_owners.role AS \"sub_owner_role?: UserRoleRow\",\n sub_owners.created_at AS \"sub_owner_created_at?\",\n sub_owners.updated_at AS \"sub_owner_updated_at?\"\n FROM projects\n INNER JOIN users AS owners ON projects.owner_id = owners.id AND owners.deleted_at IS NULL\n LEFT JOIN users AS sub_owners ON projects.sub_owner_id = sub_owners.id AND sub_owners.deleted_at IS NULL\n WHERE projects.deleted_at IS NULL\n ORDER BY projects.index ASC", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "project_id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "project_index", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "project_title", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "project_kana_title", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "project_group_name", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "project_kana_group_name", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "project_category: ProjectCategoryRow", + "type_info": { + "Custom": { + "name": "project_category", + "kind": { + "Enum": [ + "general", + "foods_with_kitchen", + "foods_without_kitchen", + "foods_without_cooking", + "stage_1a", + "stage_university_hall", + "stage_united" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "project_attributes", + "type_info": "Int4" + }, + { + "ordinal": 8, + "name": "project_owner_id", + "type_info": "Text" + }, + { + "ordinal": 9, + "name": "project_sub_owner_id", + "type_info": "Text" + }, + { + "ordinal": 10, + "name": "project_remarks", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "project_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "project_updated_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "owner_id", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "owner_name", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "owner_kana_name", + "type_info": "Text" + }, + { + "ordinal": 16, + "name": "owner_email", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "owner_phone_number", + "type_info": "Text" + }, + { + "ordinal": 18, + "name": "owner_role: UserRoleRow", + "type_info": { + "Custom": { + "name": "user_role", + "kind": { + "Enum": [ + "administrator", + "committee_operator", + "committee", + "general" + ] + } + } + } + }, + { + "ordinal": 19, + "name": "owner_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 20, + "name": "owner_updated_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 21, + "name": "sub_owner_id?", + "type_info": "Text" + }, + { + "ordinal": 22, + "name": "sub_owner_name?", + "type_info": "Text" + }, + { + "ordinal": 23, + "name": "sub_owner_kana_name?", + "type_info": "Text" + }, + { + "ordinal": 24, + "name": "sub_owner_email?", + "type_info": "Text" + }, + { + "ordinal": 25, + "name": "sub_owner_phone_number?", + "type_info": "Text" + }, + { + "ordinal": 26, + "name": "sub_owner_role?: UserRoleRow", + "type_info": { + "Custom": { + "name": "user_role", + "kind": { + "Enum": [ + "administrator", + "committee_operator", + "committee", + "general" + ] + } + } + } + }, + { + "ordinal": 27, + "name": "sub_owner_created_at?", + "type_info": "Timestamptz" + }, + { + "ordinal": 28, + "name": "sub_owner_updated_at?", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "c7d81f6ff993bb8c6751d76b7fe44470f1827f91946d25a153bf59d9f9075449" +} diff --git a/.sqlx/query-e4c735693f487aa3911eb1708a1aad855fea9fed7a0703498de7e4c33b056d0b.json b/.sqlx/query-e4c735693f487aa3911eb1708a1aad855fea9fed7a0703498de7e4c33b056d0b.json new file mode 100644 index 00000000..44ea1d8b --- /dev/null +++ b/.sqlx/query-e4c735693f487aa3911eb1708a1aad855fea9fed7a0703498de7e4c33b056d0b.json @@ -0,0 +1,229 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n projects.id AS \"project_id\",\n projects.index AS \"project_index\",\n projects.title AS \"project_title\",\n projects.kana_title AS \"project_kana_title\",\n projects.group_name AS \"project_group_name\",\n projects.kana_group_name AS \"project_kana_group_name\",\n projects.category AS \"project_category: ProjectCategoryRow\",\n projects.attributes AS \"project_attributes\",\n projects.owner_id AS \"project_owner_id\",\n projects.sub_owner_id AS \"project_sub_owner_id\",\n projects.remarks AS \"project_remarks\",\n projects.created_at AS \"project_created_at\",\n projects.updated_at AS \"project_updated_at\",\n owners.id AS \"owner_id\",\n owners.name AS \"owner_name\",\n owners.kana_name AS \"owner_kana_name\",\n owners.email AS \"owner_email\",\n owners.phone_number AS \"owner_phone_number\",\n owners.role AS \"owner_role: UserRoleRow\",\n owners.created_at AS \"owner_created_at\",\n owners.updated_at AS \"owner_updated_at\",\n sub_owners.id AS \"sub_owner_id?\",\n sub_owners.name AS \"sub_owner_name?\",\n sub_owners.kana_name AS \"sub_owner_kana_name?\",\n sub_owners.email AS \"sub_owner_email?\",\n sub_owners.phone_number AS \"sub_owner_phone_number?\",\n sub_owners.role AS \"sub_owner_role?: UserRoleRow\",\n sub_owners.created_at AS \"sub_owner_created_at?\",\n sub_owners.updated_at AS \"sub_owner_updated_at?\"\n FROM projects\n INNER JOIN users AS owners ON projects.owner_id = owners.id AND owners.deleted_at IS NULL\n LEFT JOIN users AS sub_owners ON projects.sub_owner_id = sub_owners.id AND sub_owners.deleted_at IS NULL\n WHERE projects.owner_id = $1 AND projects.deleted_at IS NULL", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "project_id", + "type_info": "Uuid" + }, + { + "ordinal": 1, + "name": "project_index", + "type_info": "Int4" + }, + { + "ordinal": 2, + "name": "project_title", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "project_kana_title", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "project_group_name", + "type_info": "Text" + }, + { + "ordinal": 5, + "name": "project_kana_group_name", + "type_info": "Text" + }, + { + "ordinal": 6, + "name": "project_category: ProjectCategoryRow", + "type_info": { + "Custom": { + "name": "project_category", + "kind": { + "Enum": [ + "general", + "foods_with_kitchen", + "foods_without_kitchen", + "foods_without_cooking", + "stage_1a", + "stage_university_hall", + "stage_united" + ] + } + } + } + }, + { + "ordinal": 7, + "name": "project_attributes", + "type_info": "Int4" + }, + { + "ordinal": 8, + "name": "project_owner_id", + "type_info": "Text" + }, + { + "ordinal": 9, + "name": "project_sub_owner_id", + "type_info": "Text" + }, + { + "ordinal": 10, + "name": "project_remarks", + "type_info": "Text" + }, + { + "ordinal": 11, + "name": "project_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 12, + "name": "project_updated_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 13, + "name": "owner_id", + "type_info": "Text" + }, + { + "ordinal": 14, + "name": "owner_name", + "type_info": "Text" + }, + { + "ordinal": 15, + "name": "owner_kana_name", + "type_info": "Text" + }, + { + "ordinal": 16, + "name": "owner_email", + "type_info": "Text" + }, + { + "ordinal": 17, + "name": "owner_phone_number", + "type_info": "Text" + }, + { + "ordinal": 18, + "name": "owner_role: UserRoleRow", + "type_info": { + "Custom": { + "name": "user_role", + "kind": { + "Enum": [ + "administrator", + "committee_operator", + "committee", + "general" + ] + } + } + } + }, + { + "ordinal": 19, + "name": "owner_created_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 20, + "name": "owner_updated_at", + "type_info": "Timestamptz" + }, + { + "ordinal": 21, + "name": "sub_owner_id?", + "type_info": "Text" + }, + { + "ordinal": 22, + "name": "sub_owner_name?", + "type_info": "Text" + }, + { + "ordinal": 23, + "name": "sub_owner_kana_name?", + "type_info": "Text" + }, + { + "ordinal": 24, + "name": "sub_owner_email?", + "type_info": "Text" + }, + { + "ordinal": 25, + "name": "sub_owner_phone_number?", + "type_info": "Text" + }, + { + "ordinal": 26, + "name": "sub_owner_role?: UserRoleRow", + "type_info": { + "Custom": { + "name": "user_role", + "kind": { + "Enum": [ + "administrator", + "committee_operator", + "committee", + "general" + ] + } + } + } + }, + { + "ordinal": 27, + "name": "sub_owner_created_at?", + "type_info": "Timestamptz" + }, + { + "ordinal": 28, + "name": "sub_owner_updated_at?", + "type_info": "Timestamptz" + } + ], + "parameters": { + "Left": [ + "Text" + ] + }, + "nullable": [ + false, + false, + false, + false, + false, + false, + false, + false, + false, + true, + true, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false, + false + ] + }, + "hash": "e4c735693f487aa3911eb1708a1aad855fea9fed7a0703498de7e4c33b056d0b" +}