diff --git a/src/identity/application/services/errors.rs b/src/identity/application/services/errors.rs index e7ed714..50bdbb3 100644 --- a/src/identity/application/services/errors.rs +++ b/src/identity/application/services/errors.rs @@ -11,6 +11,7 @@ pub type IdentityResult = Result; #[derive(Debug, Display, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub enum IdentityError { UsernameExists, + VerificationOTPSecretDoesntExist, EmailExists, } diff --git a/src/identity/application/services/mark_user_verified/command.rs b/src/identity/application/services/mark_user_verified/command.rs new file mode 100644 index 0000000..be637ad --- /dev/null +++ b/src/identity/application/services/mark_user_verified/command.rs @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use super::*; +use derive_getters::Getters; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters)] +pub struct MarkUserVerifiedCommand { + username: String, + secret: String, +} + +impl MarkUserVerifiedCommand { + pub fn new(username: String, secret: String) -> IdentityCommandResult { + Ok(Self { username, secret }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cmd() { + let username = "realaravinth"; + let secret = "asdfasdf"; + let cmd = MarkUserVerifiedCommand::new(username.into(), secret.into()).unwrap(); + assert_eq!(cmd.username(), username); + assert_eq!(cmd.secret(), secret); + } +} diff --git a/src/identity/application/services/mark_user_verified/mod.rs b/src/identity/application/services/mark_user_verified/mod.rs new file mode 100644 index 0000000..ee33d40 --- /dev/null +++ b/src/identity/application/services/mark_user_verified/mod.rs @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod command; +pub mod service; + +use super::errors::*; + +#[async_trait::async_trait] +pub trait MarkUserVerifiedUseCase { + async fn mark_user_verified(&self, cmd: command::MarkUserVerifiedCommand) + -> IdentityResult<()>; +} diff --git a/src/identity/application/services/mark_user_verified/service.rs b/src/identity/application/services/mark_user_verified/service.rs new file mode 100644 index 0000000..b744ab1 --- /dev/null +++ b/src/identity/application/services/mark_user_verified/service.rs @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later +use derive_builder::Builder; + +use super::*; +use crate::identity::application::port::output::db::{ + delete_verification_secret::*, verification_secret_exists::*, +}; + +#[derive(Builder)] +pub struct MarkUserVerifiedService { + db_verification_secret_exists_adapter: VerificationSecretExistsOutDBPortObj, + db_delete_verification_secret_adapter: DeleteVerificationSecretOutDBPortObj, +} + +#[async_trait::async_trait] +impl MarkUserVerifiedUseCase for MarkUserVerifiedService { + async fn mark_user_verified( + &self, + cmd: command::MarkUserVerifiedCommand, + ) -> IdentityResult<()> { + let msg = VerifySecretExistsMsgBuilder::default() + .username(cmd.username().into()) + .secret(cmd.secret().into()) + .build() + .unwrap(); + if !self + .db_verification_secret_exists_adapter + .verification_secret_exists(&msg) + .await + .unwrap() + { + return Err(IdentityError::VerificationOTPSecretDoesntExist); + } + + self.db_delete_verification_secret_adapter + .delete_verification_secret(&msg.into()) + .await + .unwrap(); + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use crate::tests::bdd::*; + + #[actix_rt::test] + async fn test_service() { + let username = "realaravinth"; + let secret = "password"; + let cmd = command::MarkUserVerifiedCommand::new(username.into(), secret.into()).unwrap(); + + // happy case + { + let s = MarkUserVerifiedServiceBuilder::default() + .db_verification_secret_exists_adapter(mock_verification_secret_exists_db_port( + IS_CALLED_ONLY_ONCE, + RETURNS_TRUE, + )) + .db_delete_verification_secret_adapter(mock_delete_verification_secret_db_port( + IS_CALLED_ONLY_ONCE, + )) + .build() + .unwrap(); + + s.mark_user_verified(cmd.clone()).await.unwrap(); + } + + // error: secret doesn't exist + { + let s = MarkUserVerifiedServiceBuilder::default() + .db_verification_secret_exists_adapter(mock_verification_secret_exists_db_port( + IS_CALLED_ONLY_ONCE, + RETURNS_FALSE, + )) + .db_delete_verification_secret_adapter(mock_delete_verification_secret_db_port( + IS_NEVER_CALLED, + )) + .build() + .unwrap(); + + assert_eq!( + s.mark_user_verified(cmd.clone()).await.err(), + Some(IdentityError::VerificationOTPSecretDoesntExist) + ); + } + } +} diff --git a/src/identity/application/services/mod.rs b/src/identity/application/services/mod.rs index 2cd7d0c..9159e3b 100644 --- a/src/identity/application/services/mod.rs +++ b/src/identity/application/services/mod.rs @@ -17,6 +17,7 @@ mod set_user_admin; use delete_user::command::*; use login::command::*; +use mark_user_verified::command::*; use register_user::command::*; use set_user_admin::command::*; use update_email::command::*; @@ -29,6 +30,6 @@ pub enum UserCommand { Login(LoginCommand), UpdatePassword(UpdatePasswordCommand), UpdateEmail(UpdateEmailCommand), - SetVerified, + MarkUserVerified(MarkUserVerifiedCommand), SetAdmin(SetAdminCommand), }