feat: owner manage employees #138
80 changed files with 3980 additions and 176 deletions
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT \n created_time,\n name,\n role_id,\n store_id,\n deleted\n FROM\n cqrs_identity_role_query\n WHERE\n role_id = $1;",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "created_time",
|
||||
"type_info": "Timestamptz"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "role_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "store_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 4,
|
||||
"name": "deleted",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "3a2efcc806c9a4cf7bd00b3eae4474de188981176005664ad53e39460319d34f"
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT \n role_id, version\n FROM\n cqrs_identity_role_query\n WHERE\n role_id = $1;",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "role_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "version",
|
||||
"type_info": "Int8"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "4df5ca308db869c3488227a26fabe8f9452cf458a000842f5104c7b65e8ed5f2"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT \n created_time,\n store_id,\n emp_id,\n first_name,\n last_name,\n phone_number_number,\n phone_number_country_code,\n phone_verified,\n deleted\n FROM\n cqrs_identity_employee_query\n WHERE\n emp_id = $1;",
|
||||
"query": "SELECT \n created_time,\n store_id,\n emp_id,\n first_name,\n last_name,\n phone_number_number,\n phone_number_country_code,\n phone_verified,\n role_id,\n deleted\n FROM\n cqrs_identity_employee_query\n WHERE\n emp_id = $1;",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -45,6 +45,11 @@
|
|||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "role_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "deleted",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
|
@ -63,8 +68,9 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "7c2fd6e897bf18b1f2229eec5fd12932a86d4e88f2fd4ab8ac32246a55303b03"
|
||||
"hash": "5cf15aaa2223dd2e68681e529a1972a336b8b2346c5dd7a55e445373fff4bf61"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO cqrs_identity_employee_query (\n version,\n created_time,\n store_id,\n emp_id,\n first_name,\n last_name,\n phone_number_number,\n phone_number_country_code,\n phone_verified,\n deleted\n\n\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10\n );",
|
||||
"query": "INSERT INTO cqrs_identity_employee_query (\n version,\n created_time,\n store_id,\n emp_id,\n first_name,\n last_name,\n phone_number_number,\n phone_number_country_code,\n phone_verified,\n role_id,\n deleted\n\n\n ) VALUES (\n $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11\n );",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
|
@ -14,10 +14,11 @@
|
|||
"Int8",
|
||||
"Int4",
|
||||
"Bool",
|
||||
"Uuid",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "796be4344e585654ea27252b02239158ed4691448b33d4427bf70717aad41263"
|
||||
"hash": "610f776a0591cba4d7af0bb4d4ef6406fd0dc9c5378146f996ce6d8e8e9b43c8"
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS (\n SELECT 1\n FROM cqrs_identity_role_query\n WHERE\n role_id = $1\n );",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "70211e169bab5fc0f9240e26c14bd183fa8fa607f9b50377b2d1edbd4ec97472"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT \n created_time,\n store_id,\n emp_id,\n first_name,\n last_name,\n phone_number_number,\n phone_number_country_code,\n phone_verified,\n deleted\n\n FROM\n cqrs_identity_employee_query\n WHERE\n emp_id = $1;",
|
||||
"query": "SELECT \n created_time,\n store_id,\n emp_id,\n first_name,\n last_name,\n phone_number_number,\n phone_number_country_code,\n phone_verified,\n role_id,\n deleted\n\n FROM\n cqrs_identity_employee_query\n WHERE\n emp_id = $1;",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
|
@ -45,6 +45,11 @@
|
|||
},
|
||||
{
|
||||
"ordinal": 8,
|
||||
"name": "role_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 9,
|
||||
"name": "deleted",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
|
@ -63,8 +68,9 @@
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "848f7c8250f7aba08fcf11491ee1a80c9fd0bfb8e37ca1051604bc2bb25d5356"
|
||||
"hash": "7137685170920291b99261bb95042a1f8c8ff1ffc5e724c5ceee68e49513246c"
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT EXISTS (\n SELECT 1\n FROM cqrs_identity_role_query\n WHERE\n name = $1\n AND\n store_id = $2\n AND\n deleted = false\n );",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "exists",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Text",
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
null
|
||||
]
|
||||
},
|
||||
"hash": "79e633fecc51e6691730f40831678492191cb240271b6600bc9ab96dcb959035"
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE\n cqrs_identity_role_query\n SET\n version = $1,\n\n created_time = $2,\n store_id = $3,\n name = $4,\n deleted = $5;",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Timestamptz",
|
||||
"Uuid",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "a93291e26fe69c7945bef30c84066ddefeafed4cb8f2d5f97ec3646f30ca2e78"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "UPDATE\n cqrs_identity_employee_query\n SET\n version = $1,\n\n created_time = $2,\n store_id = $3,\n first_name = $4,\n last_name = $5,\n phone_number_number = $6,\n phone_number_country_code = $7,\n phone_verified = $8,\n\n\n deleted = $9;",
|
||||
"query": "UPDATE\n cqrs_identity_employee_query\n SET\n version = $1,\n\n created_time = $2,\n store_id = $3,\n first_name = $4,\n last_name = $5,\n phone_number_number = $6,\n phone_number_country_code = $7,\n phone_verified = $8,\n role_id = $9,\n\n\n deleted = $10;",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
|
@ -13,10 +13,11 @@
|
|||
"Int8",
|
||||
"Int4",
|
||||
"Bool",
|
||||
"Uuid",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "4b8bf25b161a8337bc1ee7bcfba5a065417280cbae1527d3363f6f7561cb50c3"
|
||||
"hash": "b753a03fcb9f40976b0dd179da0c35730372a03557b703d07bab7ab90e7fe4d1"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "INSERT INTO cqrs_identity_role_query (\n version,\n created_time,\n store_id,\n role_id,\n name,\n deleted\n\n\n ) VALUES (\n $1, $2, $3, $4, $5, $6\n );",
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Int8",
|
||||
"Timestamptz",
|
||||
"Uuid",
|
||||
"Uuid",
|
||||
"Text",
|
||||
"Bool"
|
||||
]
|
||||
},
|
||||
"nullable": []
|
||||
},
|
||||
"hash": "d0bda8d2ac1ede3becdc0aa642671dfb143547e5022b391a411bbb065a29dda0"
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"db_name": "PostgreSQL",
|
||||
"query": "SELECT \n name, role_id, store_id, deleted\n FROM \n cqrs_identity_role_query\n WHERE\n store_id = $1\n AND\n deleted = false;",
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"ordinal": 0,
|
||||
"name": "name",
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"ordinal": 1,
|
||||
"name": "role_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 2,
|
||||
"name": "store_id",
|
||||
"type_info": "Uuid"
|
||||
},
|
||||
{
|
||||
"ordinal": 3,
|
||||
"name": "deleted",
|
||||
"type_info": "Bool"
|
||||
}
|
||||
],
|
||||
"parameters": {
|
||||
"Left": [
|
||||
"Uuid"
|
||||
]
|
||||
},
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
]
|
||||
},
|
||||
"hash": "e3e3b18f95cf920b423ea3c63db3327d6fdd548ecc0219230d529383297e3c40"
|
||||
}
|
43
devenv.lock
43
devenv.lock
|
@ -3,11 +3,11 @@
|
|||
"devenv": {
|
||||
"locked": {
|
||||
"dir": "src/modules",
|
||||
"lastModified": 1734441494,
|
||||
"lastModified": 1736426010,
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "bdc1a2cefdda8f89e31b1a0f3771786ba9e5d052",
|
||||
"treeHash": "9f63e582153de59f2326d8efb83d2f8eedd71f58",
|
||||
"rev": "1c384bc4be3ee571511fbbc6fdc94fe47d60f6cf",
|
||||
"treeHash": "cd68d11052e7a7fd8f4f2c3ad9f950ce9a23a2c5",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -25,11 +25,11 @@
|
|||
"rust-analyzer-src": "rust-analyzer-src"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1734503722,
|
||||
"lastModified": 1736663419,
|
||||
"owner": "nix-community",
|
||||
"repo": "fenix",
|
||||
"rev": "07f1f47c8f634a5ec52a2ad1d14e7cc7521d9a4f",
|
||||
"treeHash": "2dbf42e1832bef3cd88faa0c6e8cb8214f605842",
|
||||
"rev": "89350fe9d9b7f77a17be0541115bfc4f52ba6e40",
|
||||
"treeHash": "f8992ba6a89adc04f174aced883093a51378f1a3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -91,37 +91,20 @@
|
|||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1734202038,
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "bcba2fbf6963bf6bed3a749f9f4cf5bff4adb96d",
|
||||
"treeHash": "ed868e7045ff3d48595deec9ca09f1311c91e749",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1734425854,
|
||||
"lastModified": 1735882644,
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "0ddd26d0925f618c3a5d85a4fa5eb1e23a09491d",
|
||||
"treeHash": "7180381e4de59f052b3a3134571af84dc523fd93",
|
||||
"rev": "a5a961387e75ae44cc20f0a57ae463da5e959656",
|
||||
"treeHash": "bc4924d0f5cbc6ac438e9652cd6ff1ef7a78bd61",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -141,11 +124,11 @@
|
|||
"rust-analyzer-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1734386068,
|
||||
"lastModified": 1736576343,
|
||||
"owner": "rust-lang",
|
||||
"repo": "rust-analyzer",
|
||||
"rev": "0a706f7d2ac093985eae317781200689cfd48b78",
|
||||
"treeHash": "3f8418c9949a4084758a307478884360952624d2",
|
||||
"rev": "9923b0085c18c45b1a2340f7ef329ea4828e0734",
|
||||
"treeHash": "e3e3b73490f3883f769a72a2efd3955378ff4901",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -22,6 +22,8 @@ CREATE TABLE IF NOT EXISTS cqrs_identity_employee_query
|
|||
|
||||
store_id UUID DEFAULT NULL,
|
||||
|
||||
role_id UUID DEFAULT NULL,
|
||||
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
|
||||
PRIMARY KEY (emp_id)
|
||||
|
|
21
migrations/20250112171332_cqrs_identity_role_query.sql
Normal file
21
migrations/20250112171332_cqrs_identity_role_query.sql
Normal file
|
@ -0,0 +1,21 @@
|
|||
-- SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
--
|
||||
-- SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
CREATE TABLE IF NOT EXISTS cqrs_identity_role_query
|
||||
(
|
||||
version bigint CHECK (version >= 0) NOT NULL,
|
||||
|
||||
created_time timestamp with time zone DEFAULT (CURRENT_TIMESTAMP) NOT NULL,
|
||||
|
||||
name TEXT NOT NULL,
|
||||
|
||||
role_id UUID NOT NULL UNIQUE,
|
||||
|
||||
store_id UUID NOT NULL,
|
||||
|
||||
deleted BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
UNIQUE(name,store_id),
|
||||
|
||||
PRIMARY KEY (role_id)
|
||||
);
|
|
@ -47,6 +47,10 @@ pub enum WebError {
|
|||
DuplicateStoreName,
|
||||
StoreIDNotFound,
|
||||
DuplicateStoreID,
|
||||
DuplicateRoleID,
|
||||
DuplicateRoleName,
|
||||
RoleIDNotFound,
|
||||
RoleNotFound,
|
||||
}
|
||||
|
||||
impl From<IdentityError> for WebError {
|
||||
|
@ -71,6 +75,11 @@ impl From<IdentityError> for WebError {
|
|||
IdentityError::DuplicateStoreName => Self::DuplicateStoreName,
|
||||
IdentityError::StoreIDNotFound => Self::StoreIDNotFound,
|
||||
IdentityError::DuplicateStoreID => Self::DuplicateStoreID,
|
||||
IdentityError::DuplicateRoleID => Self::DuplicateRoleID,
|
||||
IdentityError::DuplicateUserID => Self::InternalError,
|
||||
IdentityError::DuplicateRoleName => Self::DuplicateRoleName,
|
||||
IdentityError::RoleIDNotFound => Self::RoleIDNotFound,
|
||||
IdentityError::RoleNotFound => Self::RoleNotFound,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +108,10 @@ impl ResponseError for WebError {
|
|||
Self::DuplicateStoreName => StatusCode::BAD_REQUEST,
|
||||
Self::StoreIDNotFound => StatusCode::NOT_FOUND,
|
||||
Self::DuplicateStoreID => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::DuplicateRoleID => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::DuplicateRoleName => StatusCode::BAD_REQUEST,
|
||||
Self::RoleIDNotFound => StatusCode::BAD_REQUEST,
|
||||
Self::RoleNotFound => StatusCode::NOT_FOUND,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,6 +140,10 @@ impl ResponseError for WebError {
|
|||
Self::DuplicateStoreName => HttpResponse::BadRequest().json(e),
|
||||
Self::StoreIDNotFound => HttpResponse::NotFound().json(e),
|
||||
Self::DuplicateStoreID => HttpResponse::InternalServerError().json(e),
|
||||
Self::DuplicateRoleID => HttpResponse::InternalServerError().json(e),
|
||||
Self::DuplicateRoleName => HttpResponse::BadRequest().json(e),
|
||||
Self::RoleIDNotFound => HttpResponse::BadRequest().json(e),
|
||||
Self::RoleNotFound => HttpResponse::NotFound().json(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,13 @@ use sqlx::postgres::PgPool;
|
|||
|
||||
use crate::identity::{
|
||||
application::services::{IdentityServices, IdentityServicesObj},
|
||||
domain::{aggregate::User, employee_aggregate::Employee, store_aggregate::Store},
|
||||
domain::{
|
||||
aggregate::User, employee_aggregate::Employee, role_aggregate::Role, store_aggregate::Store,
|
||||
},
|
||||
};
|
||||
use crate::settings::Settings;
|
||||
use output::{
|
||||
db::postgres::{employee_view, store_view, user_view, DBOutPostgresAdapter},
|
||||
db::postgres::{employee_view, role_view, store_view, user_view, DBOutPostgresAdapter},
|
||||
mailer::lettre::LettreMailer,
|
||||
phone::twilio::Phone,
|
||||
};
|
||||
|
@ -48,6 +50,9 @@ pub fn load_adapters(pool: PgPool, settings: Settings) -> impl FnOnce(&mut web::
|
|||
Arc::new(db.clone()),
|
||||
Arc::new(db.clone()),
|
||||
Arc::new(db.clone()),
|
||||
Arc::new(db.clone()),
|
||||
Arc::new(db.clone()),
|
||||
Arc::new(db.clone()),
|
||||
Arc::new(mailer.clone()),
|
||||
Arc::new(phone.clone()),
|
||||
Arc::new(phone.clone()),
|
||||
|
@ -60,11 +65,13 @@ pub fn load_adapters(pool: PgPool, settings: Settings) -> impl FnOnce(&mut web::
|
|||
let (store_cqrs_exec, store_cqrs_query) = store_view::init_cqrs(db.clone(), services.clone());
|
||||
let (employee_cqrs_exec, employee_cqrs_query) =
|
||||
employee_view::init_cqrs(db.clone(), services.clone());
|
||||
let (role_cqrs_exec, role_cqrs_query) = role_view::init_cqrs(db.clone(), services.clone());
|
||||
|
||||
let identity_cqrs_exec = types::WebIdentityCqrsExec::new(Arc::new(
|
||||
types::IdentityCqrsExecBuilder::default()
|
||||
.user(user_cqrs_exec)
|
||||
.employee(employee_cqrs_exec)
|
||||
.role(role_cqrs_exec)
|
||||
.store(store_cqrs_exec)
|
||||
.build()
|
||||
.unwrap(),
|
||||
|
@ -74,6 +81,7 @@ pub fn load_adapters(pool: PgPool, settings: Settings) -> impl FnOnce(&mut web::
|
|||
cfg.configure(input::web::load_ctx());
|
||||
|
||||
cfg.app_data(Data::new(user_cqrs_query.clone()));
|
||||
cfg.app_data(Data::new(role_cqrs_query.clone()));
|
||||
cfg.app_data(Data::new(store_cqrs_query.clone()));
|
||||
cfg.app_data(Data::new(employee_cqrs_query.clone()));
|
||||
cfg.app_data(identity_cqrs_exec.clone());
|
||||
|
|
|
@ -33,6 +33,8 @@ pub struct EmployeeView {
|
|||
phone_number_country_code: i32,
|
||||
phone_number_number: i64,
|
||||
|
||||
role_id: Option<Uuid>,
|
||||
|
||||
phone_verified: bool,
|
||||
store_id: Option<Uuid>,
|
||||
deleted: bool,
|
||||
|
@ -51,6 +53,7 @@ impl From<EmployeeView> for Employee {
|
|||
.unwrap(),
|
||||
)
|
||||
.emp_id(v.emp_id)
|
||||
.role_id(v.role_id)
|
||||
.phone_verified(v.phone_verified)
|
||||
.store_id(v.store_id)
|
||||
.deleted(v.deleted)
|
||||
|
@ -68,6 +71,7 @@ impl Default for EmployeeView {
|
|||
last_name: e.last_name().clone(),
|
||||
phone_number_number: *e.phone_number().number() as i64,
|
||||
phone_number_country_code: *e.phone_number().country_code() as i32,
|
||||
role_id: e.role_id().clone(),
|
||||
phone_verified: *e.phone_verified(),
|
||||
emp_id: *e.emp_id(),
|
||||
store_id: e.store_id().clone(),
|
||||
|
@ -85,6 +89,7 @@ impl EmployeeView {
|
|||
self.phone_verified = *e.phone_verified();
|
||||
self.emp_id = *e.emp_id();
|
||||
self.store_id = e.store_id().clone();
|
||||
self.role_id = e.role_id().clone();
|
||||
self.deleted = *e.deleted();
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +109,21 @@ impl View<Employee> for EmployeeView {
|
|||
IdentityEvent::PhoneNumberChanged(e) => unimplemented!(),
|
||||
// IdentityEvent::InviteAccepted(e) => self.store_id = Some(*e.store_id()),
|
||||
IdentityEvent::OrganizationExited(e) => self.store_id = None,
|
||||
IdentityEvent::OwnerAddedEmployeeToStore(e) => {
|
||||
self.store_id = Some(*e.store_id());
|
||||
self.role_id = None;
|
||||
}
|
||||
IdentityEvent::OwnerRemovedEmployeeFromStore(e) => {
|
||||
self.store_id = None;
|
||||
self.role_id = None;
|
||||
}
|
||||
IdentityEvent::RoleAdded(e) => {
|
||||
self.store_id = Some(*e.store_id());
|
||||
self.role_id = Some(*e.role_id());
|
||||
}
|
||||
IdentityEvent::EmployeeRemovedFromRole(e) => {
|
||||
self.role_id = None;
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
@ -129,6 +149,7 @@ impl ViewRepository<EmployeeView, Employee> for DBOutPostgresAdapter {
|
|||
phone_number_number,
|
||||
phone_number_country_code,
|
||||
phone_verified,
|
||||
role_id,
|
||||
deleted
|
||||
FROM
|
||||
cqrs_identity_employee_query
|
||||
|
@ -162,6 +183,7 @@ impl ViewRepository<EmployeeView, Employee> for DBOutPostgresAdapter {
|
|||
phone_number_number,
|
||||
phone_number_country_code,
|
||||
phone_verified,
|
||||
role_id,
|
||||
deleted
|
||||
|
||||
FROM
|
||||
|
@ -216,11 +238,12 @@ impl ViewRepository<EmployeeView, Employee> for DBOutPostgresAdapter {
|
|||
phone_number_number,
|
||||
phone_number_country_code,
|
||||
phone_verified,
|
||||
role_id,
|
||||
deleted
|
||||
|
||||
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11
|
||||
);",
|
||||
version,
|
||||
view.created_time,
|
||||
|
@ -231,6 +254,7 @@ impl ViewRepository<EmployeeView, Employee> for DBOutPostgresAdapter {
|
|||
view.phone_number_number,
|
||||
view.phone_number_country_code,
|
||||
view.phone_verified,
|
||||
view.role_id,
|
||||
view.deleted,
|
||||
)
|
||||
.execute(&self.pool)
|
||||
|
@ -252,9 +276,10 @@ impl ViewRepository<EmployeeView, Employee> for DBOutPostgresAdapter {
|
|||
phone_number_number = $6,
|
||||
phone_number_country_code = $7,
|
||||
phone_verified = $8,
|
||||
role_id = $9,
|
||||
|
||||
|
||||
deleted = $9;",
|
||||
deleted = $10;",
|
||||
version,
|
||||
view.created_time,
|
||||
view.store_id,
|
||||
|
@ -263,6 +288,7 @@ impl ViewRepository<EmployeeView, Employee> for DBOutPostgresAdapter {
|
|||
view.phone_number_number,
|
||||
view.phone_number_country_code,
|
||||
view.phone_verified,
|
||||
view.role_id,
|
||||
view.deleted,
|
||||
)
|
||||
.execute(&self.pool)
|
||||
|
@ -441,6 +467,7 @@ mod tests {
|
|||
let emp = employee_query.load(&emp_id_str).await.unwrap().unwrap();
|
||||
assert!(emp.phone_verified);
|
||||
|
||||
// TODO: test OwnerAddedEmployeeToStore and OwnerRemovedEmployeeFromStore
|
||||
settings.drop_db().await;
|
||||
}
|
||||
}
|
||||
|
|
101
src/identity/adapters/output/db/postgres/get_roles_for_store.rs
Normal file
101
src/identity/adapters/output/db/postgres/get_roles_for_store.rs
Normal file
|
@ -0,0 +1,101 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::DBOutPostgresAdapter;
|
||||
use crate::identity::application::port::output::db::{errors::*, get_roles_for_store::*};
|
||||
use crate::identity::domain::role_aggregate::*;
|
||||
|
||||
struct DBRole {
|
||||
name: String,
|
||||
role_id: Uuid,
|
||||
store_id: Uuid,
|
||||
deleted: bool,
|
||||
}
|
||||
|
||||
impl From<DBRole> for Role {
|
||||
fn from(v: DBRole) -> Self {
|
||||
RoleBuilder::default()
|
||||
.role_id(v.role_id)
|
||||
.store_id(v.store_id)
|
||||
.name(v.name)
|
||||
.deleted(v.deleted)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl GetRolesForStoreDBPort for DBOutPostgresAdapter {
|
||||
async fn get_roles_for_store(&self, store_id: Uuid) -> OutDBPortResult<Vec<Role>> {
|
||||
let mut res = sqlx::query_as!(
|
||||
DBRole,
|
||||
"SELECT
|
||||
name, role_id, store_id, deleted
|
||||
FROM
|
||||
cqrs_identity_role_query
|
||||
WHERE
|
||||
store_id = $1
|
||||
AND
|
||||
deleted = false;",
|
||||
store_id,
|
||||
)
|
||||
.fetch_all(&self.pool)
|
||||
.await?;
|
||||
Ok(res.drain(0..).map(|r| r.into()).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::*;
|
||||
use crate::identity::adapters::output::db::postgres::role_id_exists::tests::create_dummy_role_record;
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_postgres_role_exists() {
|
||||
let role_id = Uuid::new_v4();
|
||||
let store_id = Uuid::new_v4();
|
||||
let settings = crate::settings::tests::get_settings().await;
|
||||
settings.create_db().await;
|
||||
let db = super::DBOutPostgresAdapter::new(
|
||||
sqlx::postgres::PgPool::connect(&settings.database.url)
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let role = RoleBuilder::default()
|
||||
.name("role_name".into())
|
||||
.store_id(UUID)
|
||||
.role_id(role_id)
|
||||
.build()
|
||||
.unwrap();
|
||||
// state doesn't exist
|
||||
assert!(db.get_roles_for_store(UUID).await.unwrap().is_empty());
|
||||
|
||||
create_dummy_role_record(&role, &db).await;
|
||||
|
||||
// state exists
|
||||
assert_eq!(
|
||||
db.get_roles_for_store(*role.store_id()).await.unwrap(),
|
||||
vec![role.clone()]
|
||||
);
|
||||
|
||||
// Set role.deleted = true; now db.role_name_exists_for_store must return false
|
||||
sqlx::query!(
|
||||
"UPDATE cqrs_identity_role_query SET deleted = true WHERE role_id = $1;",
|
||||
role.role_id(),
|
||||
)
|
||||
.execute(&db.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(db.get_roles_for_store(UUID).await.unwrap().is_empty());
|
||||
|
||||
settings.drop_db().await;
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ pub mod email_exists;
|
|||
pub mod employee_view;
|
||||
mod errors;
|
||||
pub mod get_verification_secret;
|
||||
pub mod role_view;
|
||||
pub mod store_view;
|
||||
pub mod user_id_exists;
|
||||
pub mod user_view;
|
||||
|
@ -27,8 +28,11 @@ pub mod delete_verification_otp;
|
|||
pub mod emp_id_exists;
|
||||
pub mod get_emp_id_from_phone_number;
|
||||
pub mod get_login_otp;
|
||||
mod get_roles_for_store;
|
||||
pub mod get_verification_otp;
|
||||
pub mod phone_exists;
|
||||
mod role_id_exists;
|
||||
mod role_name_exists_for_store;
|
||||
|
||||
//pub mod get_invite;
|
||||
//pub mod invite_id_exists;
|
||||
|
|
85
src/identity/adapters/output/db/postgres/role_id_exists.rs
Normal file
85
src/identity/adapters/output/db/postgres/role_id_exists.rs
Normal file
|
@ -0,0 +1,85 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::DBOutPostgresAdapter;
|
||||
use crate::identity::application::port::output::db::{errors::*, role_id_exists::*};
|
||||
use crate::identity::domain::role_aggregate::*;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl RoleIDExistsDBPort for DBOutPostgresAdapter {
|
||||
async fn role_id_exists(&self, role_id: &Uuid) -> OutDBPortResult<bool> {
|
||||
let res = sqlx::query!(
|
||||
"SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM cqrs_identity_role_query
|
||||
WHERE
|
||||
role_id = $1
|
||||
);",
|
||||
role_id
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
if let Some(x) = res.exists {
|
||||
Ok(x)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub async fn create_dummy_role_record(s: &Role, db: &DBOutPostgresAdapter) {
|
||||
sqlx::query!(
|
||||
"INSERT INTO cqrs_identity_role_query
|
||||
(version, name, role_id, store_id, deleted)
|
||||
VALUES ($1, $2, $3, $4, $5);",
|
||||
1,
|
||||
s.name(),
|
||||
s.role_id(),
|
||||
s.store_id(),
|
||||
false
|
||||
)
|
||||
.execute(&db.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_postgres_role_exists() {
|
||||
let role_id = Uuid::new_v4();
|
||||
let settings = crate::settings::tests::get_settings().await;
|
||||
settings.create_db().await;
|
||||
let db = super::DBOutPostgresAdapter::new(
|
||||
sqlx::postgres::PgPool::connect(&settings.database.url)
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let role = RoleBuilder::default()
|
||||
.name("role_name".into())
|
||||
.store_id(UUID)
|
||||
.role_id(role_id)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// state doesn't exist
|
||||
assert!(!db.role_id_exists(role.role_id()).await.unwrap());
|
||||
|
||||
create_dummy_role_record(&role, &db).await;
|
||||
|
||||
// state exists
|
||||
assert!(db.role_id_exists(role.role_id()).await.unwrap());
|
||||
|
||||
settings.drop_db().await;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use super::DBOutPostgresAdapter;
|
||||
use crate::identity::application::port::output::db::{errors::*, role_name_exists_for_store::*};
|
||||
use crate::identity::domain::role_aggregate::*;
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl RoleNameExistsForStoreDBPort for DBOutPostgresAdapter {
|
||||
async fn role_name_exists_for_store(&self, s: &Role) -> OutDBPortResult<bool> {
|
||||
let res = sqlx::query!(
|
||||
"SELECT EXISTS (
|
||||
SELECT 1
|
||||
FROM cqrs_identity_role_query
|
||||
WHERE
|
||||
name = $1
|
||||
AND
|
||||
store_id = $2
|
||||
AND
|
||||
deleted = false
|
||||
);",
|
||||
s.name(),
|
||||
s.store_id(),
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await?;
|
||||
if let Some(x) = res.exists {
|
||||
Ok(x)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::*;
|
||||
use crate::identity::adapters::output::db::postgres::role_id_exists::tests::create_dummy_role_record;
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_postgres_role_exists() {
|
||||
let role_id = Uuid::new_v4();
|
||||
let store_id = Uuid::new_v4();
|
||||
let settings = crate::settings::tests::get_settings().await;
|
||||
settings.create_db().await;
|
||||
let db = super::DBOutPostgresAdapter::new(
|
||||
sqlx::postgres::PgPool::connect(&settings.database.url)
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let role = RoleBuilder::default()
|
||||
.name("role_name".into())
|
||||
.store_id(UUID)
|
||||
.role_id(role_id)
|
||||
.build()
|
||||
.unwrap();
|
||||
// state doesn't exist
|
||||
assert!(!db.role_name_exists_for_store(&role).await.unwrap());
|
||||
|
||||
create_dummy_role_record(&role, &db).await;
|
||||
|
||||
// state exists
|
||||
assert!(db.role_name_exists_for_store(&role).await.unwrap());
|
||||
|
||||
// Set role.deleted = true; now db.role_name_exists_for_store must return false
|
||||
sqlx::query!(
|
||||
"UPDATE cqrs_identity_role_query SET deleted = true WHERE role_id = $1;",
|
||||
role.role_id(),
|
||||
)
|
||||
.execute(&db.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(!db.role_name_exists_for_store(&role).await.unwrap());
|
||||
|
||||
settings.drop_db().await;
|
||||
}
|
||||
}
|
395
src/identity/adapters/output/db/postgres/role_view.rs
Normal file
395
src/identity/adapters/output/db/postgres/role_view.rs
Normal file
|
@ -0,0 +1,395 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cqrs_es::persist::{PersistenceError, ViewContext, ViewRepository};
|
||||
use cqrs_es::{EventEnvelope, Query, View};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::errors::*;
|
||||
use super::DBOutPostgresAdapter;
|
||||
use crate::identity::adapters::types::{IdentityRoleCqrsExec, IdentityRoleCqrsView};
|
||||
use crate::identity::application::services::{events::IdentityEvent, IdentityServicesObj};
|
||||
use crate::identity::domain::role_aggregate::*;
|
||||
use crate::types::currency::{self, Currency, PriceBuilder};
|
||||
use crate::utils::parse_aggregate_id::parse_aggregate_id;
|
||||
|
||||
pub const NEW_ROLE_NON_UUID: &str = "identity_new_bill_non_uuid-asdfa";
|
||||
|
||||
// The view for a Role query, for a standard http application this should
|
||||
// be designed to reflect the response dto that will be returned to a user.
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct RoleView {
|
||||
created_time: OffsetDateTime,
|
||||
name: String,
|
||||
role_id: Uuid,
|
||||
store_id: Uuid,
|
||||
|
||||
deleted: bool,
|
||||
}
|
||||
|
||||
impl From<RoleView> for Role {
|
||||
fn from(v: RoleView) -> Self {
|
||||
RoleBuilder::default()
|
||||
.name(v.name)
|
||||
.role_id(v.role_id)
|
||||
.store_id(v.store_id)
|
||||
.deleted(v.deleted)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for RoleView {
|
||||
fn default() -> Self {
|
||||
let e = Role::default();
|
||||
Self {
|
||||
created_time: OffsetDateTime::now_utc(),
|
||||
name: e.name().clone(),
|
||||
role_id: *e.role_id(),
|
||||
store_id: e.store_id().clone(),
|
||||
deleted: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RoleView {
|
||||
fn merge(&mut self, e: &Role) {
|
||||
self.name = e.name().clone();
|
||||
self.role_id = *e.role_id();
|
||||
self.store_id = e.store_id().clone();
|
||||
self.deleted = *e.deleted();
|
||||
}
|
||||
}
|
||||
|
||||
// This updates the view with events as they are committed.
|
||||
// The logic should be minimal here, e.g., don't calculate the account balance,
|
||||
// design the events to carry the balance information instead.
|
||||
impl View<Role> for RoleView {
|
||||
fn update(&mut self, event: &EventEnvelope<Role>) {
|
||||
match &event.payload {
|
||||
IdentityEvent::RoleAdded(e) => {
|
||||
self.name = e.name().clone();
|
||||
self.role_id = *e.role_id();
|
||||
self.store_id = e.store_id().clone();
|
||||
self.deleted = false;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl ViewRepository<RoleView, Role> for DBOutPostgresAdapter {
|
||||
async fn load(&self, role_id: &str) -> Result<Option<RoleView>, PersistenceError> {
|
||||
let role_id = match parse_aggregate_id(role_id, NEW_ROLE_NON_UUID)? {
|
||||
Some((val, _)) => return Ok(Some(val)),
|
||||
None => Uuid::parse_str(role_id).unwrap(),
|
||||
};
|
||||
|
||||
let res = sqlx::query_as!(
|
||||
RoleView,
|
||||
"SELECT
|
||||
created_time,
|
||||
name,
|
||||
role_id,
|
||||
store_id,
|
||||
deleted
|
||||
FROM
|
||||
cqrs_identity_role_query
|
||||
WHERE
|
||||
role_id = $1;",
|
||||
role_id
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
.map_err(PostgresAggregateError::from)?;
|
||||
Ok(Some(res))
|
||||
}
|
||||
|
||||
async fn load_with_context(
|
||||
&self,
|
||||
role_id: &str,
|
||||
) -> Result<Option<(RoleView, ViewContext)>, PersistenceError> {
|
||||
let role_id = match parse_aggregate_id(role_id, NEW_ROLE_NON_UUID)? {
|
||||
Some(val) => return Ok(Some(val)),
|
||||
None => Uuid::parse_str(role_id).unwrap(),
|
||||
};
|
||||
|
||||
let res = sqlx::query_as!(
|
||||
RoleView,
|
||||
"SELECT
|
||||
created_time,
|
||||
name,
|
||||
role_id,
|
||||
store_id,
|
||||
deleted
|
||||
FROM
|
||||
cqrs_identity_role_query
|
||||
WHERE
|
||||
role_id = $1;",
|
||||
&role_id,
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
.map_err(PostgresAggregateError::from)?;
|
||||
|
||||
struct Context {
|
||||
version: i64,
|
||||
role_id: Uuid,
|
||||
}
|
||||
|
||||
let ctx = sqlx::query_as!(
|
||||
Context,
|
||||
"SELECT
|
||||
role_id, version
|
||||
FROM
|
||||
cqrs_identity_role_query
|
||||
WHERE
|
||||
role_id = $1;",
|
||||
role_id
|
||||
)
|
||||
.fetch_one(&self.pool)
|
||||
.await
|
||||
.map_err(PostgresAggregateError::from)?;
|
||||
|
||||
let view_context = ViewContext::new(ctx.role_id.to_string(), ctx.version);
|
||||
Ok(Some((res, view_context)))
|
||||
}
|
||||
|
||||
async fn update_view(
|
||||
&self,
|
||||
view: RoleView,
|
||||
context: ViewContext,
|
||||
) -> Result<(), PersistenceError> {
|
||||
match context.version {
|
||||
0 => {
|
||||
let version = context.version + 1;
|
||||
sqlx::query!(
|
||||
"INSERT INTO cqrs_identity_role_query (
|
||||
version,
|
||||
created_time,
|
||||
store_id,
|
||||
role_id,
|
||||
name,
|
||||
deleted
|
||||
|
||||
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6
|
||||
);",
|
||||
version,
|
||||
view.created_time,
|
||||
view.store_id,
|
||||
view.role_id,
|
||||
view.name,
|
||||
view.deleted,
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(PostgresAggregateError::from)?;
|
||||
}
|
||||
_ => {
|
||||
let version = context.version + 1;
|
||||
sqlx::query!(
|
||||
"UPDATE
|
||||
cqrs_identity_role_query
|
||||
SET
|
||||
version = $1,
|
||||
|
||||
created_time = $2,
|
||||
store_id = $3,
|
||||
name = $4,
|
||||
deleted = $5;",
|
||||
version,
|
||||
view.created_time,
|
||||
view.store_id,
|
||||
view.name,
|
||||
view.deleted
|
||||
)
|
||||
.execute(&self.pool)
|
||||
.await
|
||||
.map_err(PostgresAggregateError::from)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Query<Role> for DBOutPostgresAdapter {
|
||||
async fn dispatch(&self, role_id: &str, events: &[EventEnvelope<Role>]) {
|
||||
let res = self
|
||||
.load_with_context(role_id)
|
||||
.await
|
||||
.unwrap_or_else(|_| Some((RoleView::default(), ViewContext::new(role_id.into(), 0))));
|
||||
let (mut view, view_context): (RoleView, ViewContext) = res.unwrap();
|
||||
for event in events {
|
||||
view.update(event);
|
||||
}
|
||||
self.update_view(view, view_context).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_cqrs(
|
||||
db: DBOutPostgresAdapter,
|
||||
services: IdentityServicesObj,
|
||||
) -> (IdentityRoleCqrsExec, IdentityRoleCqrsView) {
|
||||
let queries: Vec<Box<dyn Query<Role>>> = vec![Box::new(db.clone())];
|
||||
|
||||
let pool = db.pool.clone();
|
||||
|
||||
(
|
||||
Arc::new(postgres_es::postgres_cqrs(pool.clone(), queries, services)),
|
||||
Arc::new(db.clone()),
|
||||
)
|
||||
}
|
||||
//
|
||||
//#[cfg(test)]
|
||||
//mod tests {
|
||||
// use super::*;
|
||||
//
|
||||
// use postgres_es::PostgresCqrs;
|
||||
//
|
||||
// use crate::{
|
||||
// db::migrate::*,
|
||||
// identity::{
|
||||
// application::{
|
||||
// port::output::{
|
||||
// db::get_verification_otp::GetVerificationOTPOutDBPort,
|
||||
// phone::account_validation_otp::mock_account_validation_otp_phone_port,
|
||||
// },
|
||||
// services::{
|
||||
// employee_register_service::*, employee_verify_phone_number_service::*,
|
||||
// IdentityCommand, MockIdentityServicesInterface,
|
||||
// },
|
||||
// },
|
||||
// domain::{
|
||||
// employee_aggregate::*, employee_register_command::*, verify_phone_number_command::*,
|
||||
// },
|
||||
// },
|
||||
// tests::bdd::*,
|
||||
// utils::{random_number::tests::mock_generate_random_number, uuid::tests::*},
|
||||
// };
|
||||
// use std::sync::Arc;
|
||||
//
|
||||
// #[actix_rt::test]
|
||||
// async fn pg_query_identity_employee_view() {
|
||||
// let settings = crate::settings::tests::get_settings().await;
|
||||
// //let settings = crate::settings::Settings::new().unwrap();
|
||||
// settings.create_db().await;
|
||||
//
|
||||
// let db = crate::db::sqlx_postgres::Postgres::init(&settings.database.url).await;
|
||||
// db.migrate().await;
|
||||
// let db = DBOutPostgresAdapter::new(db.pool.clone());
|
||||
//
|
||||
// let queries: Vec<Box<dyn Query<Role>>> = vec![Box::new(db.clone())];
|
||||
//
|
||||
// let mut mock_services = MockIdentityServicesInterface::new();
|
||||
//
|
||||
// //let store = Store::default();
|
||||
// //crate::identity::adapters::output::db::postgres::store_id_exists::tests::create_dummy_store_record(&store, &db).await;
|
||||
//
|
||||
// let db2 = Arc::new(db.clone());
|
||||
// mock_services
|
||||
// .expect_employee_register_service()
|
||||
// .times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
// .returning(move || {
|
||||
// Arc::new(
|
||||
// RoleRegisterUserServiceBuilder::default()
|
||||
// .db_role_id_exists_adapter(db2.clone())
|
||||
// .db_create_verification_otp_adapter(db2.clone())
|
||||
// .db_phone_exists_adapter(db2.clone())
|
||||
// .random_number_adapter(mock_generate_random_number(
|
||||
// IS_CALLED_ONLY_ONCE,
|
||||
// 999,
|
||||
// ))
|
||||
// .phone_account_validation_otp_adapter(
|
||||
// mock_account_validation_otp_phone_port(IS_CALLED_ONLY_ONCE),
|
||||
// )
|
||||
// .build()
|
||||
// .unwrap(),
|
||||
// )
|
||||
// });
|
||||
//
|
||||
// let db2 = Arc::new(db.clone());
|
||||
// mock_services
|
||||
// .expect_employee_verify_phone_number_service()
|
||||
// .times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
// .returning(move || {
|
||||
// Arc::new(
|
||||
// RoleVerifyPhoneNumberServiceBuilder::default()
|
||||
// .db_get_role_id_from_phone_number_adapter(db2.clone())
|
||||
// .db_delete_verification_otp(db2.clone())
|
||||
// .db_get_verification_otp(db2.clone())
|
||||
// .build()
|
||||
// .unwrap(),
|
||||
// )
|
||||
// });
|
||||
//
|
||||
// let (cqrs, employee_query): (
|
||||
// Arc<PostgresCqrs<Role>>,
|
||||
// Arc<dyn ViewRepository<RoleView, Role>>,
|
||||
// ) = (
|
||||
// Arc::new(postgres_es::postgres_cqrs(
|
||||
// db.pool.clone(),
|
||||
// queries,
|
||||
// Arc::new(mock_services),
|
||||
// )),
|
||||
// Arc::new(db.clone()),
|
||||
// );
|
||||
//
|
||||
// let cmd = RoleRegisterCommandBuilder::default()
|
||||
// .name("foooint".into())
|
||||
// .last_name("foooint".into())
|
||||
// .phone_number(PhoneNumber::default())
|
||||
// .role_id(Uuid::new_v4())
|
||||
// .build()
|
||||
// .unwrap();
|
||||
//
|
||||
// let role_id_str = cmd.role_id().to_string();
|
||||
//
|
||||
// cqrs.execute(&role_id_str, IdentityCommand::RoleRegister(cmd.clone()))
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let emp = employee_query.load(&role_id_str).await.unwrap().unwrap();
|
||||
// let emp: Role = emp.into();
|
||||
// assert_eq!(emp.name(), cmd.name());
|
||||
// assert_eq!(emp.last_name(), cmd.last_name());
|
||||
// assert_eq!(emp.role_id(), cmd.role_id());
|
||||
// assert_eq!(emp.phone_number(), cmd.phone_number());
|
||||
// assert!(!*emp.phone_verified());
|
||||
// assert!(!*emp.deleted());
|
||||
// assert!(emp.store_id().is_none());
|
||||
//
|
||||
// let otp = db
|
||||
// .get_verification_otp(emp.phone_number())
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .unwrap();
|
||||
// cqrs.execute(
|
||||
// &role_id_str,
|
||||
// IdentityCommand::RoleVerifyPhoneNumber(
|
||||
// VerifyPhoneNumberCommandBuilder::default()
|
||||
// .otp(otp.into())
|
||||
// .phone_number(emp.phone_number().clone())
|
||||
// .build()
|
||||
// .unwrap(),
|
||||
// ),
|
||||
// )
|
||||
// .await
|
||||
// .unwrap();
|
||||
//
|
||||
// let emp = employee_query.load(&role_id_str).await.unwrap().unwrap();
|
||||
// assert!(emp.phone_verified);
|
||||
//
|
||||
// settings.drop_db().await;
|
||||
// }
|
||||
//}
|
|
@ -32,13 +32,30 @@ impl UserIDExistsOutDBPort for DBOutPostgresAdapter {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
use crate::identity::domain::aggregate::*;
|
||||
|
||||
pub async fn create_user(user: &User, user_id: Uuid, db: &DBOutPostgresAdapter) {
|
||||
sqlx::query!(
|
||||
"INSERT INTO user_query
|
||||
(version, user_id, email, hashed_password, first_name, last_name)
|
||||
VALUES ($1, $2, $3, $4, $5, $6);",
|
||||
1,
|
||||
user_id,
|
||||
user.email(),
|
||||
user.hashed_password(),
|
||||
user.first_name(),
|
||||
user.last_name(),
|
||||
)
|
||||
.execute(&db.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_postgres_user_id_exists() {
|
||||
let settings = crate::settings::tests::get_settings().await;
|
||||
|
@ -54,20 +71,21 @@ mod tests {
|
|||
// state doesn't exist
|
||||
assert!(!db.user_id_exists(&UUID).await.unwrap());
|
||||
|
||||
sqlx::query!(
|
||||
"INSERT INTO user_query
|
||||
(version, user_id, email, hashed_password, first_name, last_name)
|
||||
VALUES ($1, $2, $3, $4, $5, $6);",
|
||||
1,
|
||||
UUID,
|
||||
user.email(),
|
||||
user.hashed_password(),
|
||||
user.first_name(),
|
||||
user.last_name(),
|
||||
)
|
||||
.execute(&db.pool)
|
||||
.await
|
||||
.unwrap();
|
||||
create_user(&user, UUID, &db).await;
|
||||
// sqlx::query!(
|
||||
// "INSERT INTO user_query
|
||||
// (version, user_id, email, hashed_password, first_name, last_name)
|
||||
// VALUES ($1, $2, $3, $4, $5, $6);",
|
||||
// 1,
|
||||
// UUID,
|
||||
// user.email(),
|
||||
// user.hashed_password(),
|
||||
// user.first_name(),
|
||||
// user.last_name(),
|
||||
// )
|
||||
// .execute(&db.pool)
|
||||
// .await
|
||||
// .unwrap();
|
||||
|
||||
// state exists
|
||||
assert!(db.user_id_exists(&UUID).await.unwrap());
|
||||
|
|
|
@ -14,7 +14,7 @@ use super::errors::*;
|
|||
use super::DBOutPostgresAdapter;
|
||||
use crate::identity::adapters::types::{IdentityUserCqrsExec, IdentityUserCqrsView};
|
||||
use crate::identity::application::services::{events::IdentityEvent, IdentityServicesObj};
|
||||
use crate::identity::domain::aggregate::User;
|
||||
use crate::identity::domain::aggregate::{User, UserBuilder};
|
||||
use crate::utils::parse_aggregate_id::parse_aggregate_id;
|
||||
|
||||
pub const NEW_USER_NON_UUID: &str = "new_user_non_uuid-asdfa";
|
||||
|
@ -33,6 +33,23 @@ pub struct UserView {
|
|||
deleted: bool,
|
||||
}
|
||||
|
||||
impl From<UserView> for User {
|
||||
fn from(v: UserView) -> Self {
|
||||
UserBuilder::default()
|
||||
.first_name(v.first_name)
|
||||
.last_name(v.last_name)
|
||||
.user_id(v.user_id)
|
||||
.email(v.email)
|
||||
.hashed_password(v.hashed_password)
|
||||
.is_admin(v.is_admin)
|
||||
.is_verified(v.is_verified)
|
||||
.deleted(v.deleted)
|
||||
.email_verified(v.is_verified)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
// This updates the view with events as they are committed.
|
||||
// The logic should be minimal here, e.g., don't calculate the account balance,
|
||||
// design the events to carry the balance information instead.
|
||||
|
@ -241,3 +258,221 @@ pub fn init_cqrs(
|
|||
Arc::new(db.clone()),
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use postgres_es::PostgresCqrs;
|
||||
|
||||
use crate::{
|
||||
db::migrate::*,
|
||||
identity::{
|
||||
adapters::output::db::postgres::user_id_exists::tests::create_user,
|
||||
application::{
|
||||
port::output::mailer::account_validation_link::mock_account_validation_link_mailer_port,
|
||||
services::{
|
||||
add_store_service::*,
|
||||
events::*,
|
||||
login::{command::*, service::*, *},
|
||||
register_user::{
|
||||
command::{tests::PASSWORD, *},
|
||||
events::*,
|
||||
service::*,
|
||||
*,
|
||||
},
|
||||
update_store_service::*,
|
||||
MockIdentityServicesInterface, *,
|
||||
},
|
||||
},
|
||||
domain::{add_store_command::*, update_store_command::*},
|
||||
},
|
||||
settings::Settings,
|
||||
tests::bdd::*,
|
||||
utils::{random_number::*, random_string::*, uuid::tests::UUID, uuid::*},
|
||||
};
|
||||
use std::sync::Arc;
|
||||
|
||||
async fn init_test_context() -> (
|
||||
Settings,
|
||||
DBOutPostgresAdapter,
|
||||
Vec<Box<dyn Query<User>>>,
|
||||
MockIdentityServicesInterface,
|
||||
GetUUIDInterfaceObj,
|
||||
GenerateRandomStringInterfaceObj,
|
||||
GenerateRandomNumberInterfaceObj,
|
||||
) {
|
||||
let settings = crate::settings::tests::get_settings().await;
|
||||
//let settings = crate::settings::Settings::new().unwrap();
|
||||
settings.create_db().await;
|
||||
|
||||
let db = crate::db::sqlx_postgres::Postgres::init(&settings.database.url).await;
|
||||
db.migrate().await;
|
||||
let db = DBOutPostgresAdapter::new(db.pool.clone());
|
||||
|
||||
let simple_query = SimpleLoggingQuery {};
|
||||
|
||||
let queries: Vec<Box<dyn Query<User>>> = vec![Box::new(simple_query), Box::new(db.clone())];
|
||||
|
||||
let mut mock_services = MockIdentityServicesInterface::new();
|
||||
let random_uuid = Arc::new(GenerateUUID);
|
||||
let random_string = GenerateRandomString::new();
|
||||
let random_number = GenerateRandomNumber::new();
|
||||
(
|
||||
settings,
|
||||
db,
|
||||
queries,
|
||||
mock_services,
|
||||
random_uuid,
|
||||
random_string,
|
||||
random_number,
|
||||
)
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn user_view_login() {
|
||||
let (settings, db, queries, mut mock_services, random_uuid, random_string, random_number) =
|
||||
init_test_context().await;
|
||||
let db2 = db.clone();
|
||||
mock_services
|
||||
.expect_login()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.returning(move || Arc::new(LoginService));
|
||||
let (cqrs_executor, cqrs_view) = init_cqrs(db.clone(), Arc::new(mock_services));
|
||||
|
||||
let user = User::default();
|
||||
create_user(&user, *user.user_id(), &db).await;
|
||||
|
||||
let cmd = LoginCommand::get_cmd();
|
||||
cqrs_executor
|
||||
.execute(&user.user_id().to_string(), IdentityCommand::Login(cmd))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
settings.drop_db().await;
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn user_view_register() {
|
||||
let (settings, db, queries, mut mock_services, random_uuid, random_string, random_number) =
|
||||
init_test_context().await;
|
||||
let service: RegisterUserServiceObj = Arc::new(
|
||||
RegisterUserServiceBuilder::default()
|
||||
.db_email_exists_adapter(Arc::new(db.clone()))
|
||||
.db_user_id_exists_adapter(Arc::new(db.clone()))
|
||||
.db_create_verification_secret_adapter(Arc::new(db.clone()))
|
||||
.mailer_account_validation_link_adapter(mock_account_validation_link_mailer_port(
|
||||
IGNORE_CALL_COUNT,
|
||||
))
|
||||
.random_string_adapter(random_string.clone())
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
mock_services
|
||||
.expect_register_user()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(service); //(move || Arc::new(service.clone()));
|
||||
let (cqrs_executor, cqrs_view) = init_cqrs(db.clone(), Arc::new(mock_services));
|
||||
|
||||
let cmd = RegisterUserCommand::get_command();
|
||||
let user_id = *cmd.user_id();
|
||||
cqrs_executor
|
||||
.execute(
|
||||
&user_id.to_string(),
|
||||
IdentityCommand::RegisterUser(cmd.clone()),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let user = cqrs_view.load(&user_id.to_string()).await.unwrap().unwrap();
|
||||
let user: User = user.into();
|
||||
|
||||
assert_eq!(user.first_name(), cmd.first_name());
|
||||
assert_eq!(user.last_name(), cmd.last_name());
|
||||
assert_eq!(user.email(), cmd.email());
|
||||
assert_eq!(user.user_id(), cmd.user_id());
|
||||
assert_eq!(user.hashed_password(), cmd.hashed_password());
|
||||
assert!(!user.deleted());
|
||||
|
||||
settings.drop_db().await;
|
||||
}
|
||||
|
||||
// let cmd = AddStoreCommandBuilder::default()
|
||||
// .name(rand.get_random(10))
|
||||
// .address(None)
|
||||
// .owner(UUID)
|
||||
// .store_id(UUID)
|
||||
// .build()
|
||||
// .unwrap();
|
||||
// cqrs.execute(
|
||||
// &cmd.store_id().to_string(),
|
||||
// IdentityCommand::AddStore(cmd.clone()),
|
||||
// )
|
||||
//
|
||||
//
|
||||
// let (cqrs, store_query): (
|
||||
// Arc<PostgresCqrs<Store>>,
|
||||
// Arc<dyn ViewRepository<StoreView, Store>>,
|
||||
// ) = (
|
||||
// Arc::new(postgres_es::postgres_cqrs(
|
||||
// db.pool.clone(),
|
||||
// queries,
|
||||
// Arc::new(mock_services),
|
||||
// )),
|
||||
// Arc::new(db.clone()),
|
||||
// );
|
||||
//
|
||||
// let cmd = AddStoreCommandBuilder::default()
|
||||
// .name(rand.get_random(10))
|
||||
// .address(None)
|
||||
// .owner(UUID)
|
||||
// .store_id(UUID)
|
||||
// .build()
|
||||
// .unwrap();
|
||||
// cqrs.execute(
|
||||
// &cmd.store_id().to_string(),
|
||||
// IdentityCommand::AddStore(cmd.clone()),
|
||||
// )
|
||||
// .await
|
||||
// .unwrap();
|
||||
//
|
||||
// let store = store_query
|
||||
// .load(&(*cmd.store_id()).to_string())
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .unwrap();
|
||||
// let store: Store = store.into();
|
||||
// assert_eq!(store.name(), cmd.name());
|
||||
// assert_eq!(store.address(), cmd.address());
|
||||
// assert_eq!(store.owner(), cmd.owner());
|
||||
// assert_eq!(store.store_id(), cmd.store_id());
|
||||
// assert!(!store.deleted());
|
||||
//
|
||||
// let update_store_cmd = UpdateStoreCommand::new(
|
||||
// rand.get_random(10),
|
||||
// Some(rand.get_random(10)),
|
||||
// UUID,
|
||||
// store,
|
||||
// UUID,
|
||||
// )
|
||||
// .unwrap();
|
||||
// cqrs.execute(
|
||||
// &cmd.store_id().to_string(),
|
||||
// IdentityCommand::UpdateStore(update_store_cmd.clone()),
|
||||
// )
|
||||
// .await
|
||||
// .unwrap();
|
||||
// let store = store_query
|
||||
// .load(&(*cmd.store_id()).to_string())
|
||||
// .await
|
||||
// .unwrap()
|
||||
// .unwrap();
|
||||
// let store: Store = store.into();
|
||||
// assert_eq!(store.name(), update_store_cmd.name());
|
||||
// assert_eq!(store.address(), update_store_cmd.address());
|
||||
// assert_eq!(store.owner(), update_store_cmd.owner());
|
||||
// assert_eq!(store.store_id(), update_store_cmd.old_store().store_id());
|
||||
// assert!(!store.deleted());
|
||||
//
|
||||
// settings.drop_db().await;
|
||||
}
|
||||
|
|
|
@ -17,12 +17,14 @@ use crate::identity::{
|
|||
adapters::{
|
||||
input::web::RoutesRepository,
|
||||
output::db::postgres::{
|
||||
employee_view::EmployeeView, store_view::StoreView, user_view::UserView,
|
||||
DBOutPostgresAdapter,
|
||||
employee_view::EmployeeView, role_view::RoleView, store_view::StoreView,
|
||||
user_view::UserView, DBOutPostgresAdapter,
|
||||
},
|
||||
},
|
||||
application::services::{errors::IdentityError, IdentityCommand, IdentityServicesObj},
|
||||
domain::{aggregate::User, employee_aggregate::Employee, store_aggregate::Store},
|
||||
domain::{
|
||||
aggregate::User, employee_aggregate::Employee, role_aggregate::Role, store_aggregate::Store,
|
||||
},
|
||||
};
|
||||
|
||||
pub type WebIdentityRoutesRepository = Data<Arc<RoutesRepository>>;
|
||||
|
@ -56,6 +58,7 @@ pub struct IdentityCqrsExec {
|
|||
user: IdentityUserCqrsExec,
|
||||
store: IdentityStoreCqrsExec,
|
||||
employee: IdentityEmployeeCqrsExec,
|
||||
role: IdentityRoleCqrsExec,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -67,8 +70,13 @@ impl IdentityCqrsExecutor for IdentityCqrsExec {
|
|||
) -> Result<(), AggregateError<IdentityError>> {
|
||||
self.user.execute(aggregate_id, command.clone()).await?;
|
||||
self.store.execute(aggregate_id, command.clone()).await?;
|
||||
self.role.execute(aggregate_id, command.clone()).await?;
|
||||
self.employee.execute(aggregate_id, command).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub type IdentityRoleCqrsExec = Arc<PostgresCqrs<Role>>;
|
||||
pub type IdentityRoleCqrsView = Arc<dyn ViewRepository<RoleView, Role>>;
|
||||
pub type WebidentityRoleCqrsView = Data<IdentityRoleCqrsView>;
|
||||
|
|
|
@ -21,4 +21,6 @@ pub enum OutDBPortError {
|
|||
DuplicateStoreName,
|
||||
DuplicateStoreID,
|
||||
StoreIDNotFound,
|
||||
RoleIDNotFound,
|
||||
DuplicateRoleName,
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::identity::domain::role_aggregate::Role;
|
||||
|
||||
use super::errors::*;
|
||||
#[cfg(test)]
|
||||
#[allow(unused_imports)]
|
||||
pub use tests::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait GetRolesForStoreDBPort: Send + Sync {
|
||||
async fn get_roles_for_store(&self, store_id: Uuid) -> OutDBPortResult<Vec<Role>>;
|
||||
}
|
||||
|
||||
pub type GetRolesForStoreDBPortObj = std::sync::Arc<dyn GetRolesForStoreDBPort>;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn mock_get_roles_for_store_db_port_empty(
|
||||
times: Option<usize>,
|
||||
) -> GetRolesForStoreDBPortObj {
|
||||
let mut m = MockGetRolesForStoreDBPort::new();
|
||||
if let Some(times) = times {
|
||||
m.expect_get_roles_for_store()
|
||||
.times(times)
|
||||
.return_const(Ok(Vec::default()));
|
||||
} else {
|
||||
m.expect_get_roles_for_store()
|
||||
.return_const(Ok(Vec::default()));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
}
|
||||
|
||||
pub fn mock_get_roles_for_store_db_port(times: Option<usize>) -> GetRolesForStoreDBPortObj {
|
||||
let mut m = MockGetRolesForStoreDBPort::new();
|
||||
if let Some(times) = times {
|
||||
m.expect_get_roles_for_store()
|
||||
.times(times)
|
||||
.return_const(Ok(vec![Role::get_role()]));
|
||||
} else {
|
||||
m.expect_get_roles_for_store()
|
||||
.return_const(Ok(vec![Role::get_role()]));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
}
|
||||
}
|
|
@ -22,4 +22,7 @@ pub mod store_id_exists;
|
|||
pub mod store_name_exists;
|
||||
pub mod user_id_exists;
|
||||
//pub mod verification_otp_exists;
|
||||
pub mod get_roles_for_store;
|
||||
pub mod role_id_exists;
|
||||
pub mod role_name_exists_for_store;
|
||||
pub mod verification_secret_exists;
|
||||
|
|
53
src/identity/application/port/output/db/role_id_exists.rs
Normal file
53
src/identity/application/port/output/db/role_id_exists.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::errors::*;
|
||||
#[cfg(test)]
|
||||
#[allow(unused_imports)]
|
||||
pub use tests::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait RoleIDExistsDBPort: Send + Sync {
|
||||
async fn role_id_exists(&self, role_id: &Uuid) -> OutDBPortResult<bool>;
|
||||
}
|
||||
|
||||
pub type RoleIDExistsDBPortObj = std::sync::Arc<dyn RoleIDExistsDBPort>;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn mock_role_id_exists_db_port_false(times: Option<usize>) -> RoleIDExistsDBPortObj {
|
||||
let mut m = MockRoleIDExistsDBPort::new();
|
||||
if let Some(times) = times {
|
||||
m.expect_role_id_exists()
|
||||
.times(times)
|
||||
.returning(|_| Ok(false));
|
||||
} else {
|
||||
m.expect_role_id_exists().returning(|_| Ok(false));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
}
|
||||
|
||||
pub fn mock_role_id_exists_db_port_true(times: Option<usize>) -> RoleIDExistsDBPortObj {
|
||||
let mut m = MockRoleIDExistsDBPort::new();
|
||||
if let Some(times) = times {
|
||||
m.expect_role_id_exists()
|
||||
.times(times)
|
||||
.returning(|_| Ok(true));
|
||||
} else {
|
||||
m.expect_role_id_exists().returning(|_| Ok(true));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
use crate::identity::domain::role_aggregate::Role;
|
||||
|
||||
use super::errors::*;
|
||||
#[cfg(test)]
|
||||
#[allow(unused_imports)]
|
||||
pub use tests::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait RoleNameExistsForStoreDBPort: Send + Sync {
|
||||
async fn role_name_exists_for_store(&self, n: &Role) -> OutDBPortResult<bool>;
|
||||
}
|
||||
|
||||
pub type RoleNameExistsForStoreDBPortObj = std::sync::Arc<dyn RoleNameExistsForStoreDBPort>;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
pub fn mock_role_name_exists_for_store_db_port_false(
|
||||
times: Option<usize>,
|
||||
) -> RoleNameExistsForStoreDBPortObj {
|
||||
let mut m = MockRoleNameExistsForStoreDBPort::new();
|
||||
if let Some(times) = times {
|
||||
m.expect_role_name_exists_for_store()
|
||||
.times(times)
|
||||
.returning(|_| Ok(false));
|
||||
} else {
|
||||
m.expect_role_name_exists_for_store()
|
||||
.returning(|_| Ok(false));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
}
|
||||
|
||||
pub fn mock_role_name_exists_for_store_db_port_true(
|
||||
times: Option<usize>,
|
||||
) -> RoleNameExistsForStoreDBPortObj {
|
||||
let mut m = MockRoleNameExistsForStoreDBPort::new();
|
||||
if let Some(times) = times {
|
||||
m.expect_role_name_exists_for_store()
|
||||
.times(times)
|
||||
.returning(|_| Ok(true));
|
||||
} else {
|
||||
m.expect_role_name_exists_for_store()
|
||||
.returning(|_| Ok(true));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
}
|
||||
}
|
186
src/identity/application/services/add_role_to_store_service.rs
Normal file
186
src/identity/application/services/add_role_to_store_service.rs
Normal file
|
@ -0,0 +1,186 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use derive_builder::Builder;
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
use crate::identity::application::port::output::db::{
|
||||
role_id_exists::*, role_name_exists_for_store::*, store_id_exists::*,
|
||||
};
|
||||
use crate::identity::domain::role_aggregate::RoleBuilder;
|
||||
use crate::identity::domain::{add_role_command::*, role_added_event::*};
|
||||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait AddRoleToStoreUseCase: Send + Sync {
|
||||
async fn add_role_to_store(&self, cmd: AddRoleCommand) -> IdentityResult<RoleAddedEvent>;
|
||||
}
|
||||
|
||||
pub type AddRoleToStoreServiceObj = std::sync::Arc<dyn AddRoleToStoreUseCase>;
|
||||
|
||||
#[derive(Clone, Builder)]
|
||||
pub struct AddRoleToStoreService {
|
||||
db_store_id_exists_adapter: StoreIDExistsDBPortObj,
|
||||
db_role_id_exists_adapter: RoleIDExistsDBPortObj,
|
||||
db_role_name_exists_for_store_adapter: RoleNameExistsForStoreDBPortObj,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl AddRoleToStoreUseCase for AddRoleToStoreService {
|
||||
async fn add_role_to_store(&self, cmd: AddRoleCommand) -> IdentityResult<RoleAddedEvent> {
|
||||
if !self
|
||||
.db_store_id_exists_adapter
|
||||
.store_id_exists(cmd.store_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::StoreNotFound);
|
||||
}
|
||||
|
||||
if self
|
||||
.db_role_id_exists_adapter
|
||||
.role_id_exists(cmd.role_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::DuplicateRoleID);
|
||||
}
|
||||
|
||||
let role = RoleBuilder::default()
|
||||
.name(cmd.name().trim().to_lowercase())
|
||||
.role_id(*cmd.role_id())
|
||||
.store_id(*cmd.store_id())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
if self
|
||||
.db_role_name_exists_for_store_adapter
|
||||
.role_name_exists_for_store(&role)
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::DuplicateRoleName);
|
||||
}
|
||||
|
||||
Ok(RoleAddedEventBuilder::default()
|
||||
.name(role.name().clone())
|
||||
.store_id(*role.store_id())
|
||||
.role_id(*role.role_id())
|
||||
.build()
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{tests::bdd::*, utils::uuid::tests::*};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl AddRoleToStoreService {
|
||||
pub fn mock_service(times: Option<usize>, cmd: AddRoleCommand) -> AddRoleToStoreServiceObj {
|
||||
let res = RoleAddedEvent::get_event(&cmd);
|
||||
let mut m = MockAddRoleToStoreUseCase::default();
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_add_role_to_store()
|
||||
.times(times)
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
} else {
|
||||
m.expect_add_role_to_store()
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let s = AddRoleToStoreServiceBuilder::default()
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_false(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = AddRoleCommandBuilder::default()
|
||||
.role_id(UUID)
|
||||
.store_id(UUID)
|
||||
.name("foo".into())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let res = s.add_role_to_store(cmd.clone()).await.unwrap();
|
||||
assert_eq!(*res.role_id(), *cmd.role_id());
|
||||
assert_eq!(res.name(), cmd.name());
|
||||
assert_eq!(*res.store_id(), *cmd.store_id());
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_store_no_exist() {
|
||||
let s = AddRoleToStoreServiceBuilder::default()
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_false(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = AddRoleCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.add_role_to_store(cmd.clone()).await.err(),
|
||||
Some(IdentityError::StoreNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_role_id_exist() {
|
||||
let s = AddRoleToStoreServiceBuilder::default()
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_false(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = AddRoleCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.add_role_to_store(cmd.clone()).await.err(),
|
||||
Some(IdentityError::DuplicateRoleID)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_role_exists_for_store() {
|
||||
let s = AddRoleToStoreServiceBuilder::default()
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = AddRoleCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.add_role_to_store(cmd.clone()).await.err(),
|
||||
Some(IdentityError::DuplicateRoleName)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,12 @@ impl DeleteUserCommand {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
impl DeleteUserCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
DeleteUserCommand
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd() {
|
||||
let config = argon2_creds::Config::default();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
pub mod command;
|
||||
pub mod events;
|
||||
|
@ -8,6 +10,7 @@ pub mod service;
|
|||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait DeleteUserUseCase: Send + Sync {
|
||||
async fn delete_user(
|
||||
|
|
|
@ -20,6 +20,23 @@ impl DeleteUserUseCase for DeleteUserService {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
impl DeleteUserService {
|
||||
pub fn mock_service(
|
||||
times: Option<usize>,
|
||||
cmd: command::DeleteUserCommand,
|
||||
) -> DeleteUserServiceObj {
|
||||
let mut m = MockDeleteUserUseCase::default();
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_delete_user().times(times).return_const(());
|
||||
} else {
|
||||
m.expect_delete_user().return_const(());
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let config = argon2_creds::Config::default();
|
||||
|
|
|
@ -32,6 +32,11 @@ pub enum IdentityError {
|
|||
DuplicateStoreName,
|
||||
StoreIDNotFound,
|
||||
DuplicateStoreID,
|
||||
DuplicateUserID,
|
||||
DuplicateRoleID,
|
||||
DuplicateRoleName,
|
||||
RoleIDNotFound,
|
||||
RoleNotFound,
|
||||
}
|
||||
|
||||
pub type IdentityCommandResult<V> = Result<V, IdentityCommandError>;
|
||||
|
@ -80,6 +85,8 @@ impl From<OutDBPortError> for IdentityError {
|
|||
OutDBPortError::DuplicateStoreName => Self::DuplicateStoreName,
|
||||
OutDBPortError::DuplicateStoreID => Self::DuplicateStoreID,
|
||||
OutDBPortError::StoreIDNotFound => Self::StoreIDNotFound,
|
||||
OutDBPortError::RoleIDNotFound => Self::RoleIDNotFound,
|
||||
OutDBPortError::DuplicateRoleName => Self::DuplicateRoleName,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,17 +12,13 @@ use super::update_email::events::*;
|
|||
use super::update_password::events::*;
|
||||
|
||||
use crate::identity::domain::{
|
||||
employee_logged_in_event::*,
|
||||
employee_registered_event::*, //invite_accepted_event::*,
|
||||
login_otp_sent_event::*,
|
||||
organization_exited_event::*,
|
||||
phone_number_changed_event::*,
|
||||
phone_number_verified_event::*,
|
||||
resend_login_otp_event::*,
|
||||
store_added_event::*,
|
||||
store_updated_event::*,
|
||||
verification_otp_resent_event::*,
|
||||
verification_otp_sent_event::*,
|
||||
employee_logged_in_event::*, employee_registered_event::*,
|
||||
employee_removed_from_role::EmployeeRemovedFromRoleEvent, login_otp_sent_event::*,
|
||||
organization_exited_event::*, owner_added_employee_to_store_event::*,
|
||||
owner_removed_employee_from_store_event::*, phone_number_changed_event::*,
|
||||
phone_number_verified_event::*, resend_login_otp_event::*, role_added_event::*,
|
||||
role_set_to_employee_event::RoleSetToEmployeeEvent, store_added_event::*,
|
||||
store_updated_event::*, verification_otp_resent_event::*, verification_otp_sent_event::*,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
|
||||
|
@ -35,6 +31,11 @@ pub enum IdentityEvent {
|
|||
UserVerified,
|
||||
VerificationEmailResent,
|
||||
UserPromotedToAdmin(UserPromotedToAdminEvent),
|
||||
OwnerAddedEmployeeToStore(OwnerAddedEmployeeToStoreEvent),
|
||||
OwnerRemovedEmployeeFromStore(OwnerRemovedEmployeeFromStoreEvent),
|
||||
RoleAdded(RoleAddedEvent),
|
||||
RoleSetToEmployee(RoleSetToEmployeeEvent),
|
||||
EmployeeRemovedFromRole(EmployeeRemovedFromRoleEvent),
|
||||
|
||||
// employee
|
||||
EmployeeRegistered(EmployeeRegisteredEvent),
|
||||
|
@ -70,6 +71,13 @@ impl DomainEvent for IdentityEvent {
|
|||
IdentityEvent::UserVerified => "IdentityUserIsVerified",
|
||||
IdentityEvent::UserPromotedToAdmin { .. } => "IdentityUserPromotedToAdmin",
|
||||
IdentityEvent::VerificationEmailResent => "IdentityVerficationEmailResent",
|
||||
IdentityEvent::OwnerAddedEmployeeToStore { .. } => "IdentityOwnerAddedEmployeeToStore",
|
||||
IdentityEvent::OwnerRemovedEmployeeFromStore { .. } => {
|
||||
"IdentityOwnerRemovedEmployeeFromStore"
|
||||
}
|
||||
IdentityEvent::RoleAdded { .. } => "IdentityRoleAddedEvent",
|
||||
IdentityEvent::RoleSetToEmployee { .. } => "IdentityRoleSetToEmployee",
|
||||
IdentityEvent::EmployeeRemovedFromRole { .. } => "IdentityEmployeeRemovedFromRole",
|
||||
// employee
|
||||
IdentityEvent::EmployeeRegistered { .. } => "EmployeeRegistered",
|
||||
IdentityEvent::EmployeeLoggedIn { .. } => "EmployeeLoggedIn",
|
||||
|
|
121
src/identity/application/services/get_roles_for_store_service.rs
Normal file
121
src/identity/application/services/get_roles_for_store_service.rs
Normal file
|
@ -0,0 +1,121 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use derive_builder::Builder;
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
use super::errors::*;
|
||||
use crate::identity::{
|
||||
application::port::output::db::{get_roles_for_store::*, store_id_exists::*},
|
||||
domain::{role_aggregate::*, store_aggregate::*},
|
||||
};
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait GetRolesForStoreUseCase: Send + Sync {
|
||||
async fn get_roles_for_store(&self, store: &Store) -> IdentityResult<Vec<Role>>;
|
||||
}
|
||||
|
||||
pub type GetRolesForStoreServiceObj = Arc<dyn GetRolesForStoreUseCase>;
|
||||
|
||||
#[derive(Clone, Builder)]
|
||||
pub struct GetRolesForStoreService {
|
||||
db_store_id_exists: StoreIDExistsDBPortObj,
|
||||
db_get_roles_for_store: GetRolesForStoreDBPortObj,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl GetRolesForStoreUseCase for GetRolesForStoreService {
|
||||
async fn get_roles_for_store(&self, store: &Store) -> IdentityResult<Vec<Role>> {
|
||||
if !self
|
||||
.db_store_id_exists
|
||||
.store_id_exists(store.store_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::StoreNotFound);
|
||||
}
|
||||
|
||||
Ok(self
|
||||
.db_get_roles_for_store
|
||||
.get_roles_for_store(*store.store_id())
|
||||
.await?)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::tests::bdd::*;
|
||||
use crate::utils::uuid::tests::*;
|
||||
|
||||
pub fn mock_get_roles_for_store_service(
|
||||
times: Option<usize>,
|
||||
store: &Store,
|
||||
) -> GetRolesForStoreServiceObj {
|
||||
let mut m = MockGetRolesForStoreUseCase::new();
|
||||
|
||||
let res = vec![Role::default()];
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_get_roles_for_store()
|
||||
.times(times)
|
||||
.return_const(Ok(res));
|
||||
} else {
|
||||
m.expect_get_roles_for_store().return_const(Ok(res));
|
||||
}
|
||||
|
||||
Arc::new(m)
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
// with mock that returns Vec<Role>.len() = 1
|
||||
async fn test_service() {
|
||||
let store = Store::default();
|
||||
|
||||
let s = GetRolesForStoreServiceBuilder::default()
|
||||
.db_store_id_exists(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_get_roles_for_store(mock_get_roles_for_store_db_port(IS_CALLED_ONLY_ONCE))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let roles = s.get_roles_for_store(&store).await.unwrap();
|
||||
assert_eq!(roles.len(), 1);
|
||||
assert_eq!(roles.first().unwrap().to_owned(), Role::get_role());
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
// with mock that returns empty Vec<Role>
|
||||
async fn test_service_empty_res() {
|
||||
let store = Store::default();
|
||||
|
||||
{
|
||||
let s = GetRolesForStoreServiceBuilder::default()
|
||||
.db_store_id_exists(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_get_roles_for_store(mock_get_roles_for_store_db_port_empty(IS_CALLED_ONLY_ONCE))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let roles = s.get_roles_for_store(&store).await.unwrap();
|
||||
assert!(roles.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_store_id_no_exist() {
|
||||
let store = Store::default();
|
||||
let s = GetRolesForStoreServiceBuilder::default()
|
||||
.db_store_id_exists(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_get_roles_for_store(mock_get_roles_for_store_db_port_empty(IS_NEVER_CALLED))
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
s.get_roles_for_store(&store).await.err(),
|
||||
Some(IdentityError::StoreNotFound)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -27,6 +27,12 @@ impl LoginCommand {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
impl LoginCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
LoginCommand { success: true }
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd() {
|
||||
let config = argon2_creds::Config::default();
|
||||
|
|
|
@ -15,3 +15,17 @@ impl LoginEvent {
|
|||
Self { success }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::identity::application::services::login::command::*;
|
||||
|
||||
impl LoginEvent {
|
||||
pub fn get_event(cmd: &LoginCommand) -> Self {
|
||||
Self {
|
||||
success: *cmd.success(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
pub mod command;
|
||||
pub mod events;
|
||||
|
@ -8,6 +10,7 @@ pub mod service;
|
|||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait LoginUseCase: Send + Sync {
|
||||
async fn login(
|
||||
|
|
|
@ -21,6 +21,21 @@ impl LoginUseCase for LoginService {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
impl LoginService {
|
||||
pub fn mock_service(times: Option<usize>, cmd: command::LoginCommand) -> LoginServiceObj {
|
||||
let mut m = MockLoginUseCase::default();
|
||||
let res = events::LoginEvent::get_event(&cmd);
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_login().times(times).return_const(res);
|
||||
} else {
|
||||
m.expect_login().return_const(res);
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let config = argon2_creds::Config::default();
|
||||
|
@ -31,9 +46,7 @@ mod tests {
|
|||
let s = LoginService;
|
||||
|
||||
{
|
||||
let cmd =
|
||||
command::LoginCommand::new(username.into(), password.into(), &hashed_password)
|
||||
.unwrap();
|
||||
let cmd = command::LoginCommand::get_cmd();
|
||||
let res = s.login(cmd.clone()).await;
|
||||
assert_eq!(res.success(), cmd.success());
|
||||
}
|
||||
|
|
|
@ -33,4 +33,12 @@ mod tests {
|
|||
assert_eq!(cmd.user_id(), &user_id);
|
||||
assert_eq!(cmd.secret(), secret);
|
||||
}
|
||||
|
||||
impl MarkUserVerifiedCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
let user_id = UUID;
|
||||
let secret = "asdfasdf";
|
||||
MarkUserVerifiedCommand::new(user_id, secret.into()).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
pub mod command;
|
||||
pub mod service;
|
||||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait MarkUserVerifiedUseCase: Send + Sync {
|
||||
async fn mark_user_verified(&self, cmd: command::MarkUserVerifiedCommand)
|
||||
|
|
|
@ -52,9 +52,7 @@ mod tests {
|
|||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let user_id = UUID;
|
||||
let secret = "password";
|
||||
let cmd = command::MarkUserVerifiedCommand::new(user_id, secret.into()).unwrap();
|
||||
let cmd = command::MarkUserVerifiedCommand::get_cmd();
|
||||
|
||||
// happy case
|
||||
{
|
||||
|
@ -91,4 +89,24 @@ mod tests {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl MarkUserVerifiedService {
|
||||
pub fn mock_service(
|
||||
times: Option<usize>,
|
||||
cmd: command::MarkUserVerifiedCommand,
|
||||
) -> MarkUserVerifiedServiceObj {
|
||||
let mut m = MockMarkUserVerifiedUseCase::default();
|
||||
let res = ();
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_mark_user_verified()
|
||||
.times(times)
|
||||
.return_const(Ok(res));
|
||||
} else {
|
||||
m.expect_mark_user_verified().return_const(Ok(res));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
pub mod delete_user;
|
||||
//pub mod employee_accept_invite_service;
|
||||
pub mod add_role_to_store_service;
|
||||
pub mod employee_exit_organization_service;
|
||||
pub mod employee_login_service;
|
||||
pub mod employee_register_service;
|
||||
|
@ -21,18 +22,24 @@ pub mod errors;
|
|||
pub mod events;
|
||||
pub mod login;
|
||||
pub mod mark_user_verified;
|
||||
pub mod owner_manage_store_employee_service;
|
||||
pub mod register_user;
|
||||
pub mod resend_verification_email;
|
||||
pub mod set_user_admin;
|
||||
pub mod update_email;
|
||||
pub mod update_password;
|
||||
//pub mod owner_set_role_to_employee_service;
|
||||
//pub mod owner_remove_employee_from_role_service;
|
||||
|
||||
pub mod add_store_service;
|
||||
pub mod get_roles_for_store_service;
|
||||
pub mod update_store_service;
|
||||
|
||||
use add_store_service::*;
|
||||
use delete_user::{service::*, *};
|
||||
//use employee_accept_invite_service::*;
|
||||
use add_role_to_store_service::*;
|
||||
use add_role_to_store_service::*;
|
||||
use employee_exit_organization_service::*;
|
||||
use employee_login_service::*;
|
||||
use employee_register_service::*;
|
||||
|
@ -52,14 +59,19 @@ use errors::*;
|
|||
use events::*;
|
||||
|
||||
use crate::identity::domain::{
|
||||
add_role_command::*,
|
||||
// accept_invite_command::*,
|
||||
add_store_command::*,
|
||||
change_phone_number_command::*,
|
||||
employee_login_command::*,
|
||||
employee_register_command::*,
|
||||
exit_organization_command::*,
|
||||
owner_add_employee_to_store_command::*,
|
||||
owner_remove_employee_from_store_command::*,
|
||||
remove_employee_from_role_command::*,
|
||||
resend_login_otp_command::*,
|
||||
resend_verification_otp_command::*,
|
||||
set_role_to_employee_command::*,
|
||||
update_store_command::*,
|
||||
verify_phone_number_command::*,
|
||||
};
|
||||
|
@ -69,8 +81,10 @@ use crate::utils::{
|
|||
uuid::*,
|
||||
};
|
||||
use delete_user::command::*;
|
||||
use get_roles_for_store_service::*;
|
||||
use login::command::*;
|
||||
use mark_user_verified::command::*;
|
||||
use owner_manage_store_employee_service::*;
|
||||
use register_user::command::*;
|
||||
use resend_verification_email::command::*;
|
||||
use set_user_admin::command::*;
|
||||
|
@ -82,7 +96,8 @@ use crate::identity::application::port::output::{
|
|||
create_login_otp::*, create_verification_otp::*, create_verification_secret::*,
|
||||
delete_login_otp::*, delete_verification_otp::*, delete_verification_secret::*,
|
||||
email_exists::*, emp_id_exists::*, get_emp_id_from_phone_number::*, get_login_otp::*,
|
||||
get_verification_otp::*, get_verification_secret::*, phone_exists::*, store_id_exists::*,
|
||||
get_roles_for_store::*, get_verification_otp::*, get_verification_secret::*,
|
||||
phone_exists::*, role_id_exists::*, role_name_exists_for_store::*, store_id_exists::*,
|
||||
store_name_exists::*, user_id_exists::*, verification_secret_exists::*,
|
||||
},
|
||||
mailer::account_validation_link::*,
|
||||
|
@ -99,6 +114,12 @@ pub enum IdentityCommand {
|
|||
MarkUserVerified(MarkUserVerifiedCommand),
|
||||
SetAdmin(SetAdminCommand),
|
||||
ResendVerificationEmail(ResendVerificationEmailCommand),
|
||||
OwnerAddEmployeeToStore(OwnerAddEmployeeToStoreCommand),
|
||||
OwnerRemoveEmployeeFromStore(OwnerRemoveEmployeeFromStoreCommand),
|
||||
AddRole(AddRoleCommand),
|
||||
SetRoleToEmployee(SetRoleToEmployeeCommand),
|
||||
RemoveEmployeeFromRole(RemoveEmployeeFromRoleCommand),
|
||||
|
||||
// employee
|
||||
EmployeeRegister(EmployeeRegisterCommand),
|
||||
EmployeeInitLogin(EmployeeInitLoginCommand),
|
||||
|
@ -124,6 +145,9 @@ pub trait IdentityServicesInterface: Send + Sync {
|
|||
fn set_user_admin(&self) -> SetUserAdminServiceObj;
|
||||
fn update_email(&self) -> UpdateEmailServiceObj;
|
||||
fn update_password(&self) -> UpdatePasswordServiceObj;
|
||||
fn owner_manage_employee(&self) -> OwnerManageStoreEmployeesServiceObj;
|
||||
fn add_role_to_store(&self) -> AddRoleToStoreServiceObj;
|
||||
fn get_roles_for_store(&self) -> GetRolesForStoreServiceObj;
|
||||
|
||||
// employee
|
||||
// fn employee_accept_invite_service(&self) -> EmployeeAcceptInviteServiceObj;
|
||||
|
@ -151,6 +175,9 @@ pub struct IdentityServices {
|
|||
set_user_admin: SetUserAdminServiceObj,
|
||||
update_email: UpdateEmailServiceObj,
|
||||
update_password: UpdatePasswordServiceObj,
|
||||
owner_manage_store_employee: OwnerManageStoreEmployeesServiceObj,
|
||||
add_role_to_store: AddRoleToStoreServiceObj,
|
||||
get_roles_for_store: GetRolesForStoreServiceObj,
|
||||
|
||||
// employee_accept_invite_service: EmployeeAcceptInviteServiceObj,
|
||||
employee_exit_organization_service: EmployeeExitOrganizationServiceObj,
|
||||
|
@ -190,6 +217,17 @@ impl IdentityServicesInterface for IdentityServices {
|
|||
self.update_password.clone()
|
||||
}
|
||||
|
||||
fn owner_manage_employee(&self) -> OwnerManageStoreEmployeesServiceObj {
|
||||
self.owner_manage_store_employee.clone()
|
||||
}
|
||||
|
||||
fn add_role_to_store(&self) -> AddRoleToStoreServiceObj {
|
||||
self.add_role_to_store.clone()
|
||||
}
|
||||
fn get_roles_for_store(&self) -> GetRolesForStoreServiceObj {
|
||||
self.get_roles_for_store.clone()
|
||||
}
|
||||
|
||||
// employee
|
||||
// fn employee_accept_invite_service(&self) -> EmployeeAcceptInviteServiceObj {
|
||||
// self.employee_accept_invite_service.clone()
|
||||
|
@ -240,6 +278,9 @@ impl IdentityServices {
|
|||
out_db_store_name_exists: StoreNameExistsDBPortObj,
|
||||
out_db_user_id_exists: UserIDExistsOutDBPortObj,
|
||||
out_db_verification_secret_exists: VerificationSecretExistsOutDBPortObj,
|
||||
out_db_role_id_exists: RoleIDExistsDBPortObj,
|
||||
out_db_role_name_exists_for_store: RoleNameExistsForStoreDBPortObj,
|
||||
out_db_get_roles_for_store: GetRolesForStoreDBPortObj,
|
||||
|
||||
out_mailer_account_validating_link: AccountValidationLinkOutMailerPortObj,
|
||||
|
||||
|
@ -266,7 +307,6 @@ impl IdentityServices {
|
|||
.db_user_id_exists_adapter(out_db_user_id_exists.clone())
|
||||
.db_create_verification_secret_adapter(out_db_create_verification_secret.clone())
|
||||
.mailer_account_validation_link_adapter(out_mailer_account_validating_link.clone())
|
||||
.get_uuid(get_uuid.clone())
|
||||
.random_string_adapter(random_string.clone())
|
||||
.build()
|
||||
.unwrap(),
|
||||
|
@ -295,6 +335,33 @@ impl IdentityServices {
|
|||
|
||||
let update_password: UpdatePasswordServiceObj = Arc::new(UpdatePasswordService);
|
||||
|
||||
let owner_manage_store_employee: OwnerManageStoreEmployeesServiceObj = Arc::new(
|
||||
OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_store_id_exists_adapter(out_db_store_id_exists.clone())
|
||||
.db_emp_id_exists_adapter(out_db_emp_id_exists.clone())
|
||||
.db_role_id_exists_adapter(out_db_role_id_exists.clone())
|
||||
.db_role_name_exists_for_store_adapter(out_db_role_name_exists_for_store.clone())
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let add_role_to_store: AddRoleToStoreServiceObj = Arc::new(
|
||||
AddRoleToStoreServiceBuilder::default()
|
||||
.db_store_id_exists_adapter(out_db_store_id_exists.clone())
|
||||
.db_role_id_exists_adapter(out_db_role_id_exists.clone())
|
||||
.db_role_name_exists_for_store_adapter(out_db_role_name_exists_for_store.clone())
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let get_roles_for_store: GetRolesForStoreServiceObj = Arc::new(
|
||||
GetRolesForStoreServiceBuilder::default()
|
||||
.db_store_id_exists(out_db_store_id_exists.clone())
|
||||
.db_get_roles_for_store(out_db_get_roles_for_store.clone())
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
// let employee_accept_invite_service: EmployeeAcceptInviteServiceObj = Arc::new(
|
||||
// EmployeeAcceptInviteServiceBuilder::default()
|
||||
// .db_get_invite_adapter(unimplemented!())
|
||||
|
@ -397,6 +464,9 @@ impl IdentityServices {
|
|||
set_user_admin,
|
||||
update_email,
|
||||
update_password,
|
||||
owner_manage_store_employee,
|
||||
add_role_to_store,
|
||||
get_roles_for_store,
|
||||
|
||||
// employee_accept_invite_service,
|
||||
employee_exit_organization_service,
|
||||
|
@ -417,7 +487,8 @@ mod tests {
|
|||
use random_number::tests::mock_generate_random_number;
|
||||
|
||||
use crate::{
|
||||
tests::bdd::IS_NEVER_CALLED,
|
||||
identity::adapters::output::db::postgres::DBOutPostgresAdapter,
|
||||
tests::bdd::{IGNORE_CALL_COUNT, IS_NEVER_CALLED},
|
||||
utils::{random_string::tests::mock_generate_random_string, uuid::tests::mock_get_uuid},
|
||||
};
|
||||
|
||||
|
@ -443,6 +514,9 @@ mod tests {
|
|||
mock_store_name_exists_db_port_true(IS_NEVER_CALLED),
|
||||
mock_user_id_exists_db_port(IS_NEVER_CALLED, false),
|
||||
mock_verification_secret_exists_db_port(IS_NEVER_CALLED, false),
|
||||
mock_role_id_exists_db_port_true(IS_NEVER_CALLED),
|
||||
mock_role_name_exists_for_store_db_port_true(IS_NEVER_CALLED),
|
||||
mock_get_roles_for_store_db_port(IS_NEVER_CALLED),
|
||||
mock_account_validation_link_mailer_port(IS_NEVER_CALLED),
|
||||
mock_account_validation_otp_phone_port(IS_NEVER_CALLED),
|
||||
mock_account_login_otp_phone_port(IS_NEVER_CALLED),
|
||||
|
|
|
@ -0,0 +1,654 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use derive_builder::Builder;
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
use crate::identity::application::port::output::db::{
|
||||
emp_id_exists::*, role_id_exists::*, role_name_exists_for_store::*, store_id_exists::*,
|
||||
};
|
||||
use crate::identity::domain::{
|
||||
employee_removed_from_role::*, owner_add_employee_to_store_command::*,
|
||||
owner_added_employee_to_store_event::*, owner_remove_employee_from_store_command::*,
|
||||
owner_removed_employee_from_store_event::*, remove_employee_from_role_command::*,
|
||||
role_set_to_employee_event::*, set_role_to_employee_command::*,
|
||||
};
|
||||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait OwnerManageStoreEmployeesUseCase: Send + Sync {
|
||||
async fn add_employee_to_store(
|
||||
&self,
|
||||
cmd: OwnerAddEmployeeToStoreCommand,
|
||||
) -> IdentityResult<OwnerAddedEmployeeToStoreEvent>;
|
||||
async fn remove_employee_from_store(
|
||||
&self,
|
||||
cmd: OwnerRemoveEmployeeFromStoreCommand,
|
||||
) -> IdentityResult<OwnerRemovedEmployeeFromStoreEvent>;
|
||||
async fn set_role_to_employee(
|
||||
&self,
|
||||
cmd: SetRoleToEmployeeCommand,
|
||||
) -> IdentityResult<RoleSetToEmployeeEvent>;
|
||||
async fn remove_employee_from_role(
|
||||
&self,
|
||||
cmd: RemoveEmployeeFromRoleCommand,
|
||||
) -> IdentityResult<EmployeeRemovedFromRoleEvent>;
|
||||
}
|
||||
|
||||
pub type OwnerManageStoreEmployeesServiceObj = std::sync::Arc<dyn OwnerManageStoreEmployeesUseCase>;
|
||||
|
||||
#[derive(Clone, Builder)]
|
||||
pub struct OwnerManageStoreEmployeesService {
|
||||
db_emp_id_exists_adapter: EmpIDExistsOutDBPortObj,
|
||||
db_store_id_exists_adapter: StoreIDExistsDBPortObj,
|
||||
db_role_id_exists_adapter: RoleIDExistsDBPortObj,
|
||||
db_role_name_exists_for_store_adapter: RoleNameExistsForStoreDBPortObj,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl OwnerManageStoreEmployeesUseCase for OwnerManageStoreEmployeesService {
|
||||
async fn remove_employee_from_store(
|
||||
&self,
|
||||
cmd: OwnerRemoveEmployeeFromStoreCommand,
|
||||
) -> IdentityResult<OwnerRemovedEmployeeFromStoreEvent> {
|
||||
if !self
|
||||
.db_store_id_exists_adapter
|
||||
.store_id_exists(cmd.store_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::StoreNotFound);
|
||||
}
|
||||
|
||||
if !self
|
||||
.db_emp_id_exists_adapter
|
||||
.emp_id_exists(cmd.emp_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::EmployeeNotFound);
|
||||
}
|
||||
|
||||
Ok(OwnerRemovedEmployeeFromStoreEventBuilder::default()
|
||||
.emp_id(*cmd.emp_id())
|
||||
.store_id(*cmd.store_id())
|
||||
.added_by(*cmd.adding_by())
|
||||
.build()
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
async fn add_employee_to_store(
|
||||
&self,
|
||||
cmd: OwnerAddEmployeeToStoreCommand,
|
||||
) -> IdentityResult<OwnerAddedEmployeeToStoreEvent> {
|
||||
if !self
|
||||
.db_store_id_exists_adapter
|
||||
.store_id_exists(cmd.store_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::StoreNotFound);
|
||||
}
|
||||
|
||||
if !self
|
||||
.db_emp_id_exists_adapter
|
||||
.emp_id_exists(cmd.emp_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::EmployeeNotFound);
|
||||
}
|
||||
|
||||
Ok(OwnerAddedEmployeeToStoreEventBuilder::default()
|
||||
.emp_id(*cmd.emp_id())
|
||||
.store_id(*cmd.store_id())
|
||||
.added_by(*cmd.adding_by())
|
||||
.build()
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
async fn set_role_to_employee(
|
||||
&self,
|
||||
cmd: SetRoleToEmployeeCommand,
|
||||
) -> IdentityResult<RoleSetToEmployeeEvent> {
|
||||
if !self
|
||||
.db_store_id_exists_adapter
|
||||
.store_id_exists(cmd.store_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::StoreNotFound);
|
||||
}
|
||||
|
||||
if !self
|
||||
.db_emp_id_exists_adapter
|
||||
.emp_id_exists(cmd.emp_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::EmployeeNotFound);
|
||||
}
|
||||
|
||||
if !self
|
||||
.db_role_id_exists_adapter
|
||||
.role_id_exists(cmd.role().role_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::RoleIDNotFound);
|
||||
}
|
||||
|
||||
if !self
|
||||
.db_role_name_exists_for_store_adapter
|
||||
.role_name_exists_for_store(cmd.role())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::RoleNotFound);
|
||||
}
|
||||
|
||||
Ok(RoleSetToEmployeeEventBuilder::default()
|
||||
.emp_id(*cmd.emp_id())
|
||||
.store_id(*cmd.store_id())
|
||||
.added_by(*cmd.adding_by())
|
||||
.role(cmd.role().clone())
|
||||
.build()
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
async fn remove_employee_from_role(
|
||||
&self,
|
||||
cmd: RemoveEmployeeFromRoleCommand,
|
||||
) -> IdentityResult<EmployeeRemovedFromRoleEvent> {
|
||||
if !self
|
||||
.db_store_id_exists_adapter
|
||||
.store_id_exists(cmd.store_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::StoreNotFound);
|
||||
}
|
||||
|
||||
if !self
|
||||
.db_emp_id_exists_adapter
|
||||
.emp_id_exists(cmd.employee().emp_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::EmployeeNotFound);
|
||||
}
|
||||
|
||||
if cmd.employee().role_id() != &Some(*cmd.role().role_id()) {
|
||||
return Err(IdentityError::RoleNotFound);
|
||||
}
|
||||
|
||||
if !self
|
||||
.db_role_id_exists_adapter
|
||||
.role_id_exists(cmd.role().role_id())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::RoleIDNotFound);
|
||||
}
|
||||
|
||||
if !self
|
||||
.db_role_name_exists_for_store_adapter
|
||||
.role_name_exists_for_store(cmd.role())
|
||||
.await?
|
||||
{
|
||||
return Err(IdentityError::RoleNotFound);
|
||||
}
|
||||
|
||||
Ok(EmployeeRemovedFromRoleEventBuilder::default()
|
||||
.emp_id(*cmd.employee().emp_id())
|
||||
.store_id(*cmd.store_id())
|
||||
.added_by(*cmd.adding_by())
|
||||
.role(cmd.role().clone())
|
||||
.build()
|
||||
.unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
identity::domain::employee_aggregate::Employee, tests::bdd::*, utils::uuid::tests::*,
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
impl OwnerManageStoreEmployeesService {
|
||||
pub fn mock_service_add_employee_to_store(
|
||||
times: Option<usize>,
|
||||
cmd: OwnerAddEmployeeToStoreCommand,
|
||||
) -> OwnerManageStoreEmployeesServiceObj {
|
||||
let res = OwnerAddedEmployeeToStoreEvent::get_event(&cmd);
|
||||
let mut m = MockOwnerManageStoreEmployeesUseCase::default();
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_add_employee_to_store()
|
||||
.times(times)
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
} else {
|
||||
m.expect_add_employee_to_store()
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
pub fn mock_service_remove_employee_from_store(
|
||||
times: Option<usize>,
|
||||
cmd: OwnerRemoveEmployeeFromStoreCommand,
|
||||
) -> OwnerManageStoreEmployeesServiceObj {
|
||||
let res = OwnerRemovedEmployeeFromStoreEvent::get_event(&cmd);
|
||||
let mut m = MockOwnerManageStoreEmployeesUseCase::default();
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_remove_employee_from_store()
|
||||
.times(times)
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
} else {
|
||||
m.expect_remove_employee_from_store()
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
|
||||
pub fn mock_service_remove_employee_from_role(
|
||||
times: Option<usize>,
|
||||
cmd: RemoveEmployeeFromRoleCommand,
|
||||
) -> OwnerManageStoreEmployeesServiceObj {
|
||||
let res = EmployeeRemovedFromRoleEvent::get_event(&cmd);
|
||||
let mut m = MockOwnerManageStoreEmployeesUseCase::default();
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_remove_employee_from_role()
|
||||
.times(times)
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
} else {
|
||||
m.expect_remove_employee_from_role()
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
|
||||
pub fn mock_service_set_role_to_employee(
|
||||
times: Option<usize>,
|
||||
cmd: SetRoleToEmployeeCommand,
|
||||
) -> OwnerManageStoreEmployeesServiceObj {
|
||||
let res = RoleSetToEmployeeEvent::get_event(&cmd);
|
||||
let mut m = MockOwnerManageStoreEmployeesUseCase::default();
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_set_role_to_employee()
|
||||
.times(times)
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
} else {
|
||||
m.expect_set_role_to_employee()
|
||||
.returning(move |_| Ok(res.clone()));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_add_employee() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_true(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = OwnerAddEmployeeToStoreCommand::get_cmd();
|
||||
|
||||
let res = s.add_employee_to_store(cmd.clone()).await.unwrap();
|
||||
assert_eq!(*res.emp_id(), *cmd.emp_id());
|
||||
assert_eq!(*res.added_by(), *cmd.adding_by());
|
||||
assert_eq!(*res.store_id(), *cmd.store_id());
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_add_employee_emp_no_exist() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, false))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = OwnerAddEmployeeToStoreCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.add_employee_to_store(cmd.clone()).await.err(),
|
||||
Some(IdentityError::EmployeeNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_add_employee_store_no_exist() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_NEVER_CALLED, false))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = OwnerAddEmployeeToStoreCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.add_employee_to_store(cmd.clone()).await.err(),
|
||||
Some(IdentityError::StoreNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// remove employee
|
||||
#[actix_rt::test]
|
||||
async fn test_service_remove_employee() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = OwnerRemoveEmployeeFromStoreCommand::get_cmd();
|
||||
|
||||
let res = s.remove_employee_from_store(cmd.clone()).await.unwrap();
|
||||
assert_eq!(*res.emp_id(), *cmd.emp_id());
|
||||
assert_eq!(*res.added_by(), *cmd.adding_by());
|
||||
assert_eq!(*res.store_id(), *cmd.store_id());
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_remove_employee_emp_no_exist() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, false))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = OwnerRemoveEmployeeFromStoreCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.remove_employee_from_store(cmd.clone()).await.err(),
|
||||
Some(IdentityError::EmployeeNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_remove_employee_store_no_exist() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_NEVER_CALLED, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = OwnerRemoveEmployeeFromStoreCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.remove_employee_from_store(cmd.clone()).await.err(),
|
||||
Some(IdentityError::StoreNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// set role to employee
|
||||
#[actix_rt::test]
|
||||
async fn test_service_set_role_to_employee() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = SetRoleToEmployeeCommand::get_cmd();
|
||||
|
||||
let res = s.set_role_to_employee(cmd.clone()).await.unwrap();
|
||||
|
||||
assert_eq!(cmd.emp_id(), res.emp_id());
|
||||
assert_eq!(cmd.adding_by(), res.added_by());
|
||||
assert_eq!(cmd.store_id(), res.store_id());
|
||||
assert_eq!(cmd.role(), res.role());
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_set_role_to_employee_store_no_exist() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_NEVER_CALLED, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_true(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = SetRoleToEmployeeCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.set_role_to_employee(cmd.clone()).await.err(),
|
||||
Some(IdentityError::StoreNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_set_role_to_employee_role_id_no_exist() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = SetRoleToEmployeeCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.set_role_to_employee(cmd.clone()).await.err(),
|
||||
Some(IdentityError::RoleIDNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_set_role_to_employee_role_name_no_exist_for_store() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_false(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = SetRoleToEmployeeCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.set_role_to_employee(cmd.clone()).await.err(),
|
||||
Some(IdentityError::RoleNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// remove employee from role
|
||||
#[actix_rt::test]
|
||||
async fn test_service_remove_employee_from_role() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = RemoveEmployeeFromRoleCommand::get_cmd();
|
||||
|
||||
let res = s.remove_employee_from_role(cmd.clone()).await.unwrap();
|
||||
|
||||
assert_eq!(cmd.employee().emp_id(), res.emp_id());
|
||||
assert_eq!(cmd.adding_by(), res.added_by());
|
||||
assert_eq!(cmd.store_id(), res.store_id());
|
||||
assert_eq!(cmd.role(), res.role());
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_remove_employee_from_role_no_store() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_NEVER_CALLED, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_true(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = RemoveEmployeeFromRoleCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.remove_employee_from_role(cmd.clone()).await.err(),
|
||||
Some(IdentityError::StoreNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_remove_employee_from_role_no_employee() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, false))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_true(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = RemoveEmployeeFromRoleCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.remove_employee_from_role(cmd.clone()).await.err(),
|
||||
Some(IdentityError::EmployeeNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_remove_employee_from_role_unremovable_role() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_NEVER_CALLED))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = RemoveEmployeeFromRoleCommand::get_cmd();
|
||||
let cmd = RemoveEmployeeFromRoleCommandBuilder::default()
|
||||
.role(cmd.role().clone())
|
||||
.store_id(*cmd.store_id())
|
||||
.adding_by(*cmd.adding_by())
|
||||
.employee(Employee::default())
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
s.remove_employee_from_role(cmd.clone()).await.err(),
|
||||
Some(IdentityError::RoleNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_remove_employee_from_role_no_role() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_false(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_true(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = RemoveEmployeeFromRoleCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.remove_employee_from_role(cmd.clone()).await.err(),
|
||||
Some(IdentityError::RoleIDNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service_remove_employee_from_role_no_role_for_store() {
|
||||
let s = OwnerManageStoreEmployeesServiceBuilder::default()
|
||||
.db_emp_id_exists_adapter(mock_emp_id_exists_db_port(IS_CALLED_ONLY_ONCE, true))
|
||||
.db_store_id_exists_adapter(mock_store_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_id_exists_adapter(mock_role_id_exists_db_port_true(IS_CALLED_ONLY_ONCE))
|
||||
.db_role_name_exists_for_store_adapter(mock_role_name_exists_for_store_db_port_false(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cmd = RemoveEmployeeFromRoleCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
s.remove_employee_from_role(cmd.clone()).await.err(),
|
||||
Some(IdentityError::RoleNotFound)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ use super::*;
|
|||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Builder, Eq, PartialEq, Ord, PartialOrd, Getters,
|
||||
|
@ -16,6 +17,7 @@ pub struct UnvalidatedRegisterUserCommand {
|
|||
email: String,
|
||||
password: String,
|
||||
confirm_password: String,
|
||||
user_id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters)]
|
||||
|
@ -24,6 +26,7 @@ pub struct RegisterUserCommand {
|
|||
last_name: String,
|
||||
email: String,
|
||||
hashed_password: String,
|
||||
user_id: Uuid,
|
||||
}
|
||||
|
||||
impl UnvalidatedRegisterUserCommand {
|
||||
|
@ -43,13 +46,38 @@ impl UnvalidatedRegisterUserCommand {
|
|||
last_name: self.last_name,
|
||||
email: self.email,
|
||||
hashed_password,
|
||||
user_id: self.user_id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
pub const PASSWORD: &str = "sadfasdfasdf";
|
||||
|
||||
impl RegisterUserCommand {
|
||||
pub fn get_command() -> Self {
|
||||
let config = argon2_creds::Config::default();
|
||||
let first_name = "John";
|
||||
let last_name = "Doe";
|
||||
let email = "john@example.com";
|
||||
|
||||
UnvalidatedRegisterUserCommandBuilder::default()
|
||||
.user_id(UUID)
|
||||
.first_name(first_name.into())
|
||||
.last_name(last_name.into())
|
||||
.email(email.into())
|
||||
.password(PASSWORD.into())
|
||||
.confirm_password(PASSWORD.into())
|
||||
.build()
|
||||
.unwrap()
|
||||
.validate(&config)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd() {
|
||||
|
@ -61,6 +89,7 @@ mod tests {
|
|||
let wrong_password = "sadfasdfasdf--wrong";
|
||||
|
||||
UnvalidatedRegisterUserCommandBuilder::default()
|
||||
.user_id(UUID)
|
||||
.first_name(first_name.into())
|
||||
.last_name(last_name.into())
|
||||
.email(email.into())
|
||||
|
@ -73,6 +102,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
UnvalidatedRegisterUserCommandBuilder::default()
|
||||
.user_id(UUID)
|
||||
.first_name(first_name.into())
|
||||
.last_name(last_name.into())
|
||||
.email(first_name.into())
|
||||
|
|
|
@ -20,3 +20,25 @@ pub struct UserRegisteredEvent {
|
|||
is_admin: bool,
|
||||
email_verified: bool,
|
||||
}
|
||||
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::{
|
||||
identity::application::services::register_user::command::*, utils::uuid::tests::*,
|
||||
};
|
||||
|
||||
impl UserRegisteredEvent {
|
||||
pub fn get_event(cmd: &RegisterUserCommand) -> Self {
|
||||
Self {
|
||||
first_name: cmd.first_name().clone(),
|
||||
last_name: cmd.last_name().clone(),
|
||||
user_id: UUID,
|
||||
email: cmd.email().clone(),
|
||||
hashed_password: cmd.hashed_password().clone(),
|
||||
is_verified: false,
|
||||
is_admin: false,
|
||||
email_verified: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
pub mod command;
|
||||
pub mod events;
|
||||
|
@ -8,6 +10,7 @@ pub mod service;
|
|||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait RegisterUserUseCase: Send + Sync {
|
||||
async fn register_user(
|
||||
|
|
|
@ -15,13 +15,12 @@ use crate::utils::{random_string::*, uuid::*};
|
|||
pub const SECRET_LEN: usize = 20;
|
||||
pub const REGISTRATION_SECRET_PURPOSE: &str = "account_validation";
|
||||
|
||||
#[derive(Builder)]
|
||||
#[derive(Builder, Clone)]
|
||||
pub struct RegisterUserService {
|
||||
db_email_exists_adapter: EmailExistsOutDBPortObj,
|
||||
db_user_id_exists_adapter: UserIDExistsOutDBPortObj,
|
||||
db_create_verification_secret_adapter: CreateVerificationSecretOutDBPortObj,
|
||||
mailer_account_validation_link_adapter: AccountValidationLinkOutMailerPortObj,
|
||||
get_uuid: GetUUIDInterfaceObj,
|
||||
random_string_adapter: GenerateRandomStringInterfaceObj,
|
||||
}
|
||||
|
||||
|
@ -40,18 +39,13 @@ impl RegisterUserUseCase for RegisterUserService {
|
|||
return Err(IdentityError::DuplicateEmail);
|
||||
}
|
||||
|
||||
let mut user_id = self.get_uuid.get_uuid();
|
||||
loop {
|
||||
if self
|
||||
.db_user_id_exists_adapter
|
||||
.user_id_exists(&user_id)
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
user_id = self.get_uuid.get_uuid();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
if self
|
||||
.db_user_id_exists_adapter
|
||||
.user_id_exists(cmd.user_id())
|
||||
.await
|
||||
.unwrap()
|
||||
{
|
||||
return Err(IdentityError::DuplicateUserID);
|
||||
}
|
||||
|
||||
let secret = self.random_string_adapter.get_random(SECRET_LEN);
|
||||
|
@ -60,7 +54,7 @@ impl RegisterUserUseCase for RegisterUserService {
|
|||
.create_verification_secret(
|
||||
CreateSecretMsgBuilder::default()
|
||||
.secret(secret.clone())
|
||||
.user_id(user_id)
|
||||
.user_id(*cmd.user_id())
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
|
@ -75,7 +69,7 @@ impl RegisterUserUseCase for RegisterUserService {
|
|||
Ok(events::UserRegisteredEventBuilder::default()
|
||||
.first_name(cmd.first_name().into())
|
||||
.last_name(cmd.last_name().into())
|
||||
.user_id(user_id)
|
||||
.user_id(*cmd.user_id())
|
||||
.email(cmd.email().into())
|
||||
.hashed_password(cmd.hashed_password().into())
|
||||
.is_verified(false)
|
||||
|
@ -94,22 +88,29 @@ mod tests {
|
|||
use crate::utils::random_string::tests::*;
|
||||
use crate::utils::uuid::tests::*;
|
||||
|
||||
impl RegisterUserService {
|
||||
pub fn mock_service(
|
||||
times: Option<usize>,
|
||||
cmd: command::RegisterUserCommand,
|
||||
) -> RegisterUserServiceObj {
|
||||
let res = events::UserRegisteredEvent::get_event(&cmd);
|
||||
let mut m = MockRegisterUserUseCase::default();
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_register_user()
|
||||
.times(times)
|
||||
.return_const(Ok(res.clone()));
|
||||
} else {
|
||||
m.expect_register_user().return_const(Ok(res.clone()));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let username = "realaravinth";
|
||||
let email = format!("{username}@example.com");
|
||||
let password = "password";
|
||||
let config = argon2_creds::Config::default();
|
||||
let cmd = command::UnvalidatedRegisterUserCommandBuilder::default()
|
||||
.first_name(username.into())
|
||||
.last_name(username.into())
|
||||
.email(email)
|
||||
.password(password.into())
|
||||
.confirm_password(password.into())
|
||||
.build()
|
||||
.unwrap()
|
||||
.validate(&config)
|
||||
.unwrap();
|
||||
let cmd = command::RegisterUserCommand::get_command();
|
||||
|
||||
let s = RegisterUserServiceBuilder::default()
|
||||
.db_user_id_exists_adapter(mock_user_id_exists_db_port(
|
||||
|
@ -130,7 +131,6 @@ mod tests {
|
|||
.mailer_account_validation_link_adapter(mock_account_validation_link_mailer_port(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
))
|
||||
.get_uuid(mock_get_uuid(IS_CALLED_ONLY_ONCE))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
@ -140,28 +140,17 @@ mod tests {
|
|||
assert_eq!(res.user_id(), &UUID);
|
||||
assert_eq!(res.email(), cmd.email());
|
||||
assert!(!res.is_admin());
|
||||
assert!(argon2_creds::Config::verify(res.hashed_password(), password).unwrap())
|
||||
assert!(
|
||||
argon2_creds::Config::verify(res.hashed_password(), command::tests::PASSWORD).unwrap()
|
||||
)
|
||||
}
|
||||
#[actix_rt::test]
|
||||
async fn test_service_email_exists() {
|
||||
let username = "realaravinth";
|
||||
let email = format!("{username}@example.com");
|
||||
let password = "password";
|
||||
let config = argon2_creds::Config::default();
|
||||
let cmd = command::UnvalidatedRegisterUserCommandBuilder::default()
|
||||
.first_name(username.into())
|
||||
.last_name(username.into())
|
||||
.email(email)
|
||||
.password(password.into())
|
||||
.confirm_password(password.into())
|
||||
.build()
|
||||
.unwrap()
|
||||
.validate(&config)
|
||||
.unwrap();
|
||||
let cmd = command::RegisterUserCommand::get_command();
|
||||
|
||||
let s = RegisterUserServiceBuilder::default()
|
||||
.db_user_id_exists_adapter(mock_user_id_exists_db_port(
|
||||
IGNORE_CALL_COUNT,
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
RETURNS_FALSE,
|
||||
))
|
||||
.db_create_verification_secret_adapter(mock_create_verification_secret_db_port(
|
||||
|
@ -175,7 +164,6 @@ mod tests {
|
|||
.mailer_account_validation_link_adapter(mock_account_validation_link_mailer_port(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.get_uuid(mock_get_uuid(IS_NEVER_CALLED))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
|
@ -184,4 +172,36 @@ mod tests {
|
|||
Some(IdentityError::DuplicateEmail)
|
||||
);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_register_user_service_user_id_exists() {
|
||||
let cmd = command::RegisterUserCommand::get_command();
|
||||
|
||||
let s = RegisterUserServiceBuilder::default()
|
||||
.db_user_id_exists_adapter(mock_user_id_exists_db_port(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
RETURNS_TRUE,
|
||||
))
|
||||
.db_create_verification_secret_adapter(mock_create_verification_secret_db_port(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.db_email_exists_adapter(mock_email_exists_db_port(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
RETURNS_FALSE,
|
||||
))
|
||||
.random_string_adapter(mock_generate_random_string(
|
||||
IS_NEVER_CALLED,
|
||||
RETURNS_RANDOM_STRING.into(),
|
||||
))
|
||||
.mailer_account_validation_link_adapter(mock_account_validation_link_mailer_port(
|
||||
IS_NEVER_CALLED,
|
||||
))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(
|
||||
s.register_user(cmd.clone()).await.err(),
|
||||
Some(IdentityError::DuplicateUserID)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,4 +53,15 @@ mod tests {
|
|||
Some(IdentityCommandError::BadEmail)
|
||||
);
|
||||
}
|
||||
|
||||
impl ResendVerificationEmailCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
let u = crate::identity::domain::aggregate::User::default();
|
||||
Self {
|
||||
user_id: *u.user_id(),
|
||||
first_name: u.first_name().clone(),
|
||||
email: u.email().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
pub mod command;
|
||||
pub mod service;
|
||||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait ResendVerificationEmailUseCase: Send + Sync {
|
||||
async fn resend_verification_email(
|
||||
|
|
|
@ -116,4 +116,24 @@ mod tests {
|
|||
Some(IdentityError::DuplicateEmail)
|
||||
);
|
||||
}
|
||||
|
||||
impl ResendVerificationEmailService {
|
||||
pub fn mock_service(
|
||||
times: Option<usize>,
|
||||
cmd: command::ResendVerificationEmailCommand,
|
||||
) -> ResendVerificationEmailServiceObj {
|
||||
let mut m = MockResendVerificationEmailUseCase::default();
|
||||
let res = ();
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_resend_verification_email()
|
||||
.times(times)
|
||||
.return_const(Ok(res));
|
||||
} else {
|
||||
m.expect_resend_verification_email().return_const(Ok(res));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,21 +34,7 @@ mod tests {
|
|||
async fn test_cmd() {
|
||||
let username = "realaravinth";
|
||||
|
||||
SetAdminCommand::new(
|
||||
UserBuilder::default()
|
||||
.first_name(username.into())
|
||||
.last_name(username.into())
|
||||
.email(username.into())
|
||||
.hashed_password(username.into())
|
||||
.is_verified(true)
|
||||
.email_verified(false)
|
||||
.is_admin(true)
|
||||
.deleted(false)
|
||||
.user_id(UUID)
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
SetAdminCommand::get_cmd();
|
||||
|
||||
assert_eq!(
|
||||
SetAdminCommand::new(
|
||||
|
@ -69,4 +55,25 @@ mod tests {
|
|||
Some(IdentityCommandError::PermissionDenied)
|
||||
);
|
||||
}
|
||||
|
||||
impl SetAdminCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
let u = User::default();
|
||||
Self::new(
|
||||
UserBuilder::default()
|
||||
.first_name(u.first_name().clone())
|
||||
.last_name(u.last_name().clone())
|
||||
.email(u.email().clone())
|
||||
.hashed_password(u.hashed_password().clone())
|
||||
.is_verified(true)
|
||||
.email_verified(false)
|
||||
.is_admin(true)
|
||||
.deleted(false)
|
||||
.user_id(*u.user_id())
|
||||
.build()
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,3 +17,18 @@ impl UserPromotedToAdminEvent {
|
|||
Self { promoted_by_user }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::identity::application::services::set_user_admin::command::SetAdminCommand;
|
||||
|
||||
impl UserPromotedToAdminEvent {
|
||||
pub fn get_event(cmd: &SetAdminCommand) -> Self {
|
||||
Self {
|
||||
promoted_by_user: cmd.promoted_by_user().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
pub mod command;
|
||||
pub mod events;
|
||||
|
@ -8,6 +10,7 @@ pub mod service;
|
|||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait SetUserAdminUseCase: Send + Sync {
|
||||
async fn set_user_admin(
|
||||
|
|
|
@ -26,26 +26,30 @@ mod tests {
|
|||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let username = "realaravinth";
|
||||
|
||||
let s = SetUserAdminService;
|
||||
let u = UserBuilder::default()
|
||||
.first_name(username.into())
|
||||
.last_name(username.into())
|
||||
.email(username.into())
|
||||
.hashed_password(username.into())
|
||||
.is_verified(true)
|
||||
.email_verified(false)
|
||||
.is_admin(true)
|
||||
.deleted(false)
|
||||
.user_id(UUID)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let cmd = command::SetAdminCommand::new(u).unwrap();
|
||||
let cmd = command::SetAdminCommand::get_cmd();
|
||||
assert_eq!(
|
||||
s.set_user_admin(cmd.clone()).await.promoted_by_user(),
|
||||
cmd.promoted_by_user()
|
||||
)
|
||||
}
|
||||
|
||||
impl SetUserAdminService {
|
||||
pub fn mock_service(
|
||||
times: Option<usize>,
|
||||
cmd: command::SetAdminCommand,
|
||||
) -> SetUserAdminServiceObj {
|
||||
let mut m = MockSetUserAdminUseCase::default();
|
||||
let res = events::UserPromotedToAdminEvent::get_event(&cmd);
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_set_user_admin().times(times).return_const(res);
|
||||
} else {
|
||||
m.expect_set_user_admin().return_const(res);
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -93,4 +93,27 @@ mod tests {
|
|||
Some(IdentityCommandError::WrongPassword)
|
||||
);
|
||||
}
|
||||
|
||||
// command
|
||||
|
||||
impl UpdateEmailCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
let config = argon2_creds::Config::default();
|
||||
let password = "adsfasdfasd";
|
||||
let first_name = "john";
|
||||
let user_id = UUID;
|
||||
let new_email = "newemail@example.com".to_string();
|
||||
let hashed_password = config.password(password).unwrap();
|
||||
|
||||
UpdateEmailCommand::new(
|
||||
new_email.clone(),
|
||||
user_id,
|
||||
first_name.into(),
|
||||
password.into(),
|
||||
&hashed_password,
|
||||
&config,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,3 +15,18 @@ impl EmailUpdatedEvent {
|
|||
Self { new_email }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::identity::application::services::update_email::command::UpdateEmailCommand;
|
||||
|
||||
impl EmailUpdatedEvent {
|
||||
pub fn get_event(cmd: &UpdateEmailCommand) -> Self {
|
||||
Self {
|
||||
new_email: cmd.new_email().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
pub mod command;
|
||||
pub mod events;
|
||||
|
@ -8,6 +10,7 @@ pub mod service;
|
|||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait UpdateEmailUseCase: Send + Sync {
|
||||
async fn update_email(
|
||||
|
|
|
@ -68,23 +68,27 @@ mod tests {
|
|||
use crate::tests::bdd::*;
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
impl UpdateEmailService {
|
||||
pub fn mock_service(
|
||||
times: Option<usize>,
|
||||
cmd: command::UpdateEmailCommand,
|
||||
) -> UpdateEmailServiceObj {
|
||||
let mut m = MockUpdateEmailUseCase::default();
|
||||
let res = events::EmailUpdatedEvent::get_event(&cmd);
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_update_email().times(times).return_const(Ok(res));
|
||||
} else {
|
||||
m.expect_update_email().return_const(Ok(res));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let user_id = UUID;
|
||||
let new_email = "john@example.com".to_string();
|
||||
let password = "password";
|
||||
let config = argon2_creds::Config::default();
|
||||
let hashed_password = config.password(password).unwrap();
|
||||
|
||||
let cmd = command::UpdateEmailCommand::new(
|
||||
new_email.clone(),
|
||||
user_id,
|
||||
"john".into(),
|
||||
password.into(),
|
||||
&hashed_password,
|
||||
&config,
|
||||
)
|
||||
.unwrap();
|
||||
let cmd = command::UpdateEmailCommand::get_cmd();
|
||||
|
||||
// happy case
|
||||
{
|
||||
|
|
|
@ -37,6 +37,14 @@ impl UpdatePasswordCommand {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
impl UpdatePasswordCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
Self {
|
||||
hashed_new_passowrd: "foo".into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cmd() {
|
||||
let config = argon2_creds::Config::default();
|
||||
|
|
|
@ -15,3 +15,19 @@ impl PasswordUpdatedEvent {
|
|||
Self { hashed_password }
|
||||
}
|
||||
}
|
||||
|
||||
// events
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::identity::application::services::update_password::command::UpdatePasswordCommand;
|
||||
|
||||
impl PasswordUpdatedEvent {
|
||||
pub fn get_event(cmd: &UpdatePasswordCommand) -> Self {
|
||||
Self {
|
||||
hashed_password: cmd.hashed_new_passowrd().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
|
||||
pub mod command;
|
||||
pub mod events;
|
||||
|
@ -8,6 +10,7 @@ pub mod service;
|
|||
|
||||
use super::errors::*;
|
||||
|
||||
#[automock]
|
||||
#[async_trait::async_trait]
|
||||
pub trait UpdatePasswordUseCase: Send + Sync {
|
||||
async fn update_password(
|
||||
|
|
|
@ -21,6 +21,24 @@ impl UpdatePasswordUseCase for UpdatePasswordService {
|
|||
mod tests {
|
||||
use super::*;
|
||||
|
||||
impl UpdatePasswordService {
|
||||
pub fn mock_service(
|
||||
times: Option<usize>,
|
||||
cmd: command::UpdatePasswordCommand,
|
||||
) -> UpdatePasswordServiceObj {
|
||||
let mut m = MockUpdatePasswordUseCase::default();
|
||||
let res = events::PasswordUpdatedEvent::get_event(&cmd);
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_update_password().times(times).return_const(res);
|
||||
} else {
|
||||
m.expect_update_password().return_const(res);
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
async fn test_service() {
|
||||
let username = "realaravinth";
|
||||
|
|
39
src/identity/domain/add_role_command.rs
Normal file
39
src/identity/domain/add_role_command.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cqrs_es::Aggregate;
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use events::IdentityEvent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::identity::application::services::{errors::*, *};
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct AddRoleCommand {
|
||||
name: String,
|
||||
role_id: Uuid,
|
||||
store_id: Uuid,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl AddRoleCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
Self {
|
||||
name: "some role".into(),
|
||||
role_id: UUID,
|
||||
store_id: UUID,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -134,6 +134,38 @@ impl Aggregate for User {
|
|||
.await?;
|
||||
Ok(vec![IdentityEvent::VerificationEmailResent])
|
||||
}
|
||||
IdentityCommand::OwnerAddEmployeeToStore(cmd) => {
|
||||
let res = services
|
||||
.owner_manage_employee()
|
||||
.add_employee_to_store(cmd)
|
||||
.await?;
|
||||
Ok(vec![IdentityEvent::OwnerAddedEmployeeToStore(res)])
|
||||
}
|
||||
IdentityCommand::OwnerRemoveEmployeeFromStore(cmd) => {
|
||||
let res = services
|
||||
.owner_manage_employee()
|
||||
.remove_employee_from_store(cmd)
|
||||
.await?;
|
||||
Ok(vec![IdentityEvent::OwnerRemovedEmployeeFromStore(res)])
|
||||
}
|
||||
IdentityCommand::AddRole(cmd) => {
|
||||
let res = services.add_role_to_store().add_role_to_store(cmd).await?;
|
||||
Ok(vec![IdentityEvent::RoleAdded(res)])
|
||||
}
|
||||
IdentityCommand::SetRoleToEmployee(cmd) => {
|
||||
let res = services
|
||||
.owner_manage_employee()
|
||||
.set_role_to_employee(cmd)
|
||||
.await?;
|
||||
Ok(vec![IdentityEvent::RoleSetToEmployee(res)])
|
||||
}
|
||||
IdentityCommand::RemoveEmployeeFromRole(cmd) => {
|
||||
let res = services
|
||||
.owner_manage_employee()
|
||||
.remove_employee_from_role(cmd)
|
||||
.await?;
|
||||
Ok(vec![IdentityEvent::EmployeeRemovedFromRole(res)])
|
||||
}
|
||||
_ => Ok(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
@ -195,4 +227,331 @@ mod tests {
|
|||
assert!(!u.email_verified());
|
||||
assert!(u.set_email_verified(true).deleted());
|
||||
}
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use cqrs_es::test::TestFramework;
|
||||
|
||||
use super::*;
|
||||
use crate::identity::{
|
||||
application::services::{
|
||||
add_role_to_store_service::AddRoleToStoreService,
|
||||
delete_user::{command::DeleteUserCommand, service::DeleteUserService},
|
||||
events::IdentityEvent,
|
||||
login::{command::LoginCommand, events::LoginEvent, service::LoginService},
|
||||
owner_manage_store_employee_service::*,
|
||||
register_user::{
|
||||
command::RegisterUserCommand, events::UserRegisteredEvent,
|
||||
service::RegisterUserService,
|
||||
},
|
||||
IdentityCommand, MockIdentityServicesInterface,
|
||||
},
|
||||
domain::{
|
||||
add_role_command::AddRoleCommand,
|
||||
employee_removed_from_role::EmployeeRemovedFromRoleEvent,
|
||||
owner_add_employee_to_store_command::*, owner_added_employee_to_store_event::*,
|
||||
owner_remove_employee_from_store_command::OwnerRemoveEmployeeFromStoreCommand,
|
||||
owner_removed_employee_from_store_event::OwnerRemovedEmployeeFromStoreEvent,
|
||||
remove_employee_from_role_command::RemoveEmployeeFromRoleCommand,
|
||||
role_added_event::RoleAddedEvent, role_set_to_employee_event::RoleSetToEmployeeEvent,
|
||||
set_role_to_employee_command::SetRoleToEmployeeCommand,
|
||||
},
|
||||
};
|
||||
use crate::tests::bdd::*;
|
||||
|
||||
type UserTestFramework = TestFramework<User>;
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_register_user() {
|
||||
let cmd = RegisterUserCommand::get_command();
|
||||
let expected = UserRegisteredEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::UserRegistered(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_register_user()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(RegisterUserService::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::RegisterUser(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_delete_user() {
|
||||
let cmd = DeleteUserCommand::get_cmd();
|
||||
let expected = IdentityEvent::UserDeleted;
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_delete_user()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(DeleteUserService::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::DeleteUser(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_login_user() {
|
||||
let cmd = LoginCommand::get_cmd();
|
||||
let expected = IdentityEvent::Loggedin(LoginEvent::get_event(&cmd));
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_login()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(LoginService::mock_service(IS_CALLED_ONLY_ONCE, cmd.clone()));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::Login(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_update_password() {
|
||||
use crate::identity::application::services::update_password::{
|
||||
command::*, events::*, service::*, *,
|
||||
};
|
||||
|
||||
let cmd = UpdatePasswordCommand::get_cmd();
|
||||
let expected = PasswordUpdatedEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::PasswordUpdated(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_update_password()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(UpdatePasswordService::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::UpdatePassword(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_update_email() {
|
||||
use crate::identity::application::services::update_email::{
|
||||
command::*, events::*, service::*, *,
|
||||
};
|
||||
|
||||
let cmd = UpdateEmailCommand::get_cmd();
|
||||
let expected = EmailUpdatedEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::EmailUpdated(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_update_email()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(UpdateEmailService::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::UpdateEmail(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_mark_user_verified() {
|
||||
use crate::identity::application::services::mark_user_verified::{
|
||||
command::*, service::*, *,
|
||||
};
|
||||
|
||||
let cmd = MarkUserVerifiedCommand::get_cmd();
|
||||
let expected = IdentityEvent::UserVerified;
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_mark_user_verified()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(MarkUserVerifiedService::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::MarkUserVerified(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_set_admin() {
|
||||
use crate::identity::application::services::set_user_admin::{
|
||||
command::*, events::*, service::*, *,
|
||||
};
|
||||
|
||||
let cmd = SetAdminCommand::get_cmd();
|
||||
let expected = UserPromotedToAdminEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::UserPromotedToAdmin(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_set_user_admin()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(SetUserAdminService::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::SetAdmin(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_resend_verification_email() {
|
||||
use crate::identity::application::services::resend_verification_email::{
|
||||
command::*, service::*, *,
|
||||
};
|
||||
|
||||
let cmd = ResendVerificationEmailCommand::get_cmd();
|
||||
let expected = IdentityEvent::VerificationEmailResent;
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_resend_verification_email()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(ResendVerificationEmailService::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::ResendVerificationEmail(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_owner_added_employee_to_store() {
|
||||
let cmd = OwnerAddEmployeeToStoreCommand::get_cmd();
|
||||
let expected = OwnerAddedEmployeeToStoreEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::OwnerAddedEmployeeToStore(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_owner_manage_employee()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(
|
||||
OwnerManageStoreEmployeesService::mock_service_add_employee_to_store(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
),
|
||||
);
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::OwnerAddEmployeeToStore(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_owner_remove_employee_to_store() {
|
||||
let cmd = OwnerRemoveEmployeeFromStoreCommand::get_cmd();
|
||||
let expected = OwnerRemovedEmployeeFromStoreEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::OwnerRemovedEmployeeFromStore(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_owner_manage_employee()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(
|
||||
OwnerManageStoreEmployeesService::mock_service_remove_employee_from_store(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
),
|
||||
);
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::OwnerRemoveEmployeeFromStore(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_owner_set_role_to_employee() {
|
||||
let cmd = SetRoleToEmployeeCommand::get_cmd();
|
||||
let expected = RoleSetToEmployeeEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::RoleSetToEmployee(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_owner_manage_employee()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(
|
||||
OwnerManageStoreEmployeesService::mock_service_set_role_to_employee(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
),
|
||||
);
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::SetRoleToEmployee(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_owner_remove_employee_from_role() {
|
||||
let cmd = RemoveEmployeeFromRoleCommand::get_cmd();
|
||||
let expected = EmployeeRemovedFromRoleEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::EmployeeRemovedFromRole(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_owner_manage_employee()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(
|
||||
OwnerManageStoreEmployeesService::mock_service_remove_employee_from_role(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
),
|
||||
);
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::RemoveEmployeeFromRole(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_owner_add_roles_to_store() {
|
||||
let cmd = AddRoleCommand::get_cmd();
|
||||
let expected = RoleAddedEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::RoleAdded(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_add_role_to_store()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(AddRoleToStoreService::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::AddRole(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ pub struct Employee {
|
|||
#[builder(default = "None")]
|
||||
store_id: Option<Uuid>,
|
||||
deleted: bool,
|
||||
#[builder(default = "None")]
|
||||
role_id: Option<Uuid>,
|
||||
}
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
|
@ -62,6 +64,7 @@ impl Default for Employee {
|
|||
first_name: "".to_string(),
|
||||
last_name: "".to_string(),
|
||||
phone_number: Default::default(),
|
||||
role_id: None,
|
||||
phone_verified: false,
|
||||
deleted: false,
|
||||
emp_id: Uuid::new_v4(),
|
||||
|
@ -160,6 +163,21 @@ impl Aggregate for Employee {
|
|||
IdentityEvent::PhoneNumberChanged(e) => unimplemented!(),
|
||||
// IdentityEvent::InviteAccepted(e) => self.store_id = Some(*e.store_id()),
|
||||
IdentityEvent::OrganizationExited(e) => self.store_id = None,
|
||||
IdentityEvent::OwnerAddedEmployeeToStore(e) => {
|
||||
self.store_id = Some(*e.store_id());
|
||||
self.role_id = None;
|
||||
}
|
||||
IdentityEvent::OwnerRemovedEmployeeFromStore(e) => {
|
||||
self.store_id = None;
|
||||
self.role_id = None;
|
||||
}
|
||||
IdentityEvent::RoleAdded(e) => {
|
||||
self.store_id = Some(*e.store_id());
|
||||
self.role_id = Some(*e.role_id());
|
||||
}
|
||||
IdentityEvent::EmployeeRemovedFromRole(e) => {
|
||||
self.role_id = None;
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
|
@ -185,8 +203,9 @@ mod tests {
|
|||
employee_register_command::EmployeeRegisterCommand,
|
||||
employee_registered_event::EmployeeRegisteredEvent,
|
||||
exit_organization_command::ExitOrganizationCommand,
|
||||
// invite_accepted_event::InviteAcceptedEvent,
|
||||
organization_exited_event::OrganizationExitedEvent,
|
||||
owner_add_employee_to_store_command::OwnerAddEmployeeToStoreCommand,
|
||||
owner_added_employee_to_store_event::OwnerAddedEmployeeToStoreEvent,
|
||||
phone_number_verified_event::PhoneNumberVerifiedEvent,
|
||||
resend_login_otp_command::ResendLoginOTPCommand,
|
||||
resend_login_otp_event::ResentLoginOTPEvent,
|
||||
|
|
40
src/identity/domain/employee_removed_from_role.rs
Normal file
40
src/identity/domain/employee_removed_from_role.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::role_aggregate::*;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct EmployeeRemovedFromRoleEvent {
|
||||
emp_id: Uuid,
|
||||
added_by: Uuid,
|
||||
store_id: Uuid,
|
||||
role: Role,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
identity::domain::remove_employee_from_role_command::RemoveEmployeeFromRoleCommand,
|
||||
utils::uuid::tests::UUID,
|
||||
};
|
||||
|
||||
impl EmployeeRemovedFromRoleEvent {
|
||||
pub fn get_event(cmd: &RemoveEmployeeFromRoleCommand) -> Self {
|
||||
Self {
|
||||
emp_id: *cmd.employee().emp_id(),
|
||||
added_by: *cmd.adding_by(),
|
||||
store_id: *cmd.store_id(),
|
||||
role: cmd.role().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
pub mod aggregate;
|
||||
pub mod employee_aggregate;
|
||||
pub mod role_aggregate;
|
||||
//pub mod employee_commands;
|
||||
pub mod store_aggregate;
|
||||
// pub mod invite;
|
||||
|
@ -12,11 +13,16 @@ pub mod store_aggregate;
|
|||
pub mod employee_logged_in_event;
|
||||
pub mod employee_registered_event;
|
||||
//pub mod invite_accepted_event;
|
||||
pub mod employee_removed_from_role;
|
||||
pub mod login_otp_sent_event;
|
||||
pub mod organization_exited_event;
|
||||
pub mod owner_added_employee_to_store_event;
|
||||
pub mod owner_removed_employee_from_store_event;
|
||||
pub mod phone_number_changed_event;
|
||||
pub mod phone_number_verified_event;
|
||||
pub mod resend_login_otp_event;
|
||||
pub mod role_added_event;
|
||||
pub mod role_set_to_employee_event;
|
||||
pub mod store_added_event;
|
||||
pub mod store_updated_event;
|
||||
pub mod verification_otp_resent_event;
|
||||
|
@ -24,12 +30,17 @@ pub mod verification_otp_sent_event;
|
|||
|
||||
// commands
|
||||
//pub mod accept_invite_command;
|
||||
pub mod add_role_command;
|
||||
pub mod add_store_command;
|
||||
pub mod change_phone_number_command;
|
||||
pub mod employee_login_command;
|
||||
pub mod employee_register_command;
|
||||
pub mod exit_organization_command;
|
||||
pub mod owner_add_employee_to_store_command;
|
||||
pub mod owner_remove_employee_from_store_command;
|
||||
pub mod remove_employee_from_role_command;
|
||||
pub mod resend_login_otp_command;
|
||||
pub mod resend_verification_otp_command;
|
||||
pub mod set_role_to_employee_command;
|
||||
pub mod update_store_command;
|
||||
pub mod verify_phone_number_command;
|
||||
|
|
36
src/identity/domain/owner_add_employee_to_store_command.rs
Normal file
36
src/identity/domain/owner_add_employee_to_store_command.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::employee_aggregate::*;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct OwnerAddEmployeeToStoreCommand {
|
||||
adding_by: Uuid,
|
||||
emp_id: Uuid,
|
||||
store_id: Uuid,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl OwnerAddEmployeeToStoreCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
Self {
|
||||
emp_id: UUID,
|
||||
adding_by: UUID,
|
||||
store_id: UUID,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
38
src/identity/domain/owner_added_employee_to_store_event.rs
Normal file
38
src/identity/domain/owner_added_employee_to_store_event.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::role_aggregate::*;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct OwnerAddedEmployeeToStoreEvent {
|
||||
emp_id: Uuid,
|
||||
added_by: Uuid,
|
||||
store_id: Uuid,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
identity::domain::owner_add_employee_to_store_command::OwnerAddEmployeeToStoreCommand,
|
||||
utils::uuid::tests::UUID,
|
||||
};
|
||||
|
||||
impl OwnerAddedEmployeeToStoreEvent {
|
||||
pub fn get_event(cmd: &OwnerAddEmployeeToStoreCommand) -> Self {
|
||||
Self {
|
||||
emp_id: *cmd.emp_id(),
|
||||
added_by: *cmd.adding_by(),
|
||||
store_id: *cmd.store_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::employee_aggregate::*;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct OwnerRemoveEmployeeFromStoreCommand {
|
||||
adding_by: Uuid,
|
||||
emp_id: Uuid,
|
||||
store_id: Uuid,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl OwnerRemoveEmployeeFromStoreCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
Self {
|
||||
emp_id: UUID,
|
||||
adding_by: UUID,
|
||||
store_id: UUID,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::employee_aggregate::*;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct OwnerRemovedEmployeeFromStoreEvent {
|
||||
emp_id: Uuid,
|
||||
added_by: Uuid,
|
||||
store_id: Uuid,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
identity::domain::owner_remove_employee_from_store_command::OwnerRemoveEmployeeFromStoreCommand,
|
||||
utils::uuid::tests::UUID,
|
||||
};
|
||||
|
||||
impl OwnerRemovedEmployeeFromStoreEvent {
|
||||
pub fn get_event(cmd: &OwnerRemoveEmployeeFromStoreCommand) -> Self {
|
||||
Self {
|
||||
emp_id: *cmd.emp_id(),
|
||||
added_by: *cmd.adding_by(),
|
||||
store_id: *cmd.store_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
53
src/identity/domain/remove_employee_from_role_command.rs
Normal file
53
src/identity/domain/remove_employee_from_role_command.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::employee_aggregate::*;
|
||||
use super::role_aggregate::Role;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct RemoveEmployeeFromRoleCommand {
|
||||
adding_by: Uuid,
|
||||
store_id: Uuid,
|
||||
role: Role,
|
||||
employee: Employee,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl RemoveEmployeeFromRoleCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
let role = Role::default();
|
||||
let employee = Employee::default();
|
||||
let employee = EmployeeBuilder::default()
|
||||
.first_name(employee.first_name().clone())
|
||||
.last_name(employee.last_name().clone())
|
||||
.emp_id(*employee.emp_id())
|
||||
.phone_number(employee.phone_number().clone())
|
||||
.phone_verified(employee.phone_verified().clone())
|
||||
.store_id(Some(UUID))
|
||||
.deleted(false)
|
||||
.role_id(Some(*role.role_id()))
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
adding_by: UUID,
|
||||
store_id: UUID,
|
||||
role,
|
||||
employee,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
35
src/identity/domain/role_added_event.rs
Normal file
35
src/identity/domain/role_added_event.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::role_aggregate::*;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct RoleAddedEvent {
|
||||
name: String,
|
||||
role_id: Uuid,
|
||||
store_id: Uuid,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{identity::domain::add_role_command::AddRoleCommand, utils::uuid::tests::UUID};
|
||||
|
||||
impl RoleAddedEvent {
|
||||
pub fn get_event(cmd: &AddRoleCommand) -> Self {
|
||||
Self {
|
||||
name: cmd.name().clone(),
|
||||
role_id: *cmd.role_id(),
|
||||
store_id: *cmd.store_id(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
129
src/identity/domain/role_aggregate.rs
Normal file
129
src/identity/domain/role_aggregate.rs
Normal file
|
@ -0,0 +1,129 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use async_trait::async_trait;
|
||||
use cqrs_es::Aggregate;
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use events::IdentityEvent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::identity::application::services::{errors::*, *};
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct Role {
|
||||
name: String,
|
||||
role_id: Uuid,
|
||||
store_id: Uuid,
|
||||
#[builder(default = "false")]
|
||||
deleted: bool,
|
||||
}
|
||||
|
||||
impl Default for Role {
|
||||
fn default() -> Self {
|
||||
Role {
|
||||
name: "".to_string(),
|
||||
store_id: Uuid::new_v4(),
|
||||
role_id: Uuid::new_v4(),
|
||||
deleted: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Aggregate for Role {
|
||||
type Command = IdentityCommand;
|
||||
type Event = IdentityEvent;
|
||||
type Error = IdentityError;
|
||||
type Services = std::sync::Arc<dyn IdentityServicesInterface>;
|
||||
|
||||
// This identifier should be unique to the system.
|
||||
fn aggregate_type() -> String {
|
||||
"identity.role".to_string()
|
||||
}
|
||||
|
||||
// The aggregate logic goes here. Note that this will be the _bulk_ of a CQRS system
|
||||
// so expect to use helper functions elsewhere to keep the code clean.
|
||||
async fn handle(
|
||||
&self,
|
||||
command: Self::Command,
|
||||
services: &Self::Services,
|
||||
) -> Result<Vec<Self::Event>, Self::Error> {
|
||||
match command {
|
||||
IdentityCommand::AddRole(cmd) => Ok(vec![IdentityEvent::RoleAdded(
|
||||
services.add_role_to_store().add_role_to_store(cmd).await?,
|
||||
)]),
|
||||
_ => Ok(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn apply(&mut self, event: Self::Event) {
|
||||
match event {
|
||||
IdentityEvent::RoleAdded(event) => {
|
||||
self.name = event.name().clone();
|
||||
self.role_id = *self.role_id();
|
||||
self.store_id = *self.store_id();
|
||||
self.deleted = false;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use cqrs_es::test::TestFramework;
|
||||
|
||||
use crate::{
|
||||
identity::domain::{
|
||||
add_role_command::*,
|
||||
// accept_invite_command::AcceptInviteCommand,
|
||||
role_added_event::*,
|
||||
},
|
||||
utils::uuid::tests::UUID,
|
||||
};
|
||||
use add_role_to_store_service::*;
|
||||
|
||||
use super::*;
|
||||
use crate::tests::bdd::*;
|
||||
|
||||
type RoleTestFramework = TestFramework<Role>;
|
||||
|
||||
impl Role {
|
||||
pub fn get_role() -> Self {
|
||||
Role {
|
||||
name: "test_role_should_never_exist_in_prod".to_string(),
|
||||
store_id: UUID,
|
||||
role_id: UUID,
|
||||
deleted: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_role_aggregate_add_role() {
|
||||
let cmd = AddRoleCommand::get_cmd();
|
||||
let expected = RoleAddedEvent::get_event(&cmd);
|
||||
let expected = IdentityEvent::RoleAdded(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_add_role_to_store()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(AddRoleToStoreService::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
RoleTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::AddRole(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
}
|
40
src/identity/domain/role_set_to_employee_event.rs
Normal file
40
src/identity/domain/role_set_to_employee_event.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::role_aggregate::*;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct RoleSetToEmployeeEvent {
|
||||
emp_id: Uuid,
|
||||
added_by: Uuid,
|
||||
store_id: Uuid,
|
||||
role: Role,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
identity::domain::set_role_to_employee_command::SetRoleToEmployeeCommand,
|
||||
utils::uuid::tests::UUID,
|
||||
};
|
||||
|
||||
impl RoleSetToEmployeeEvent {
|
||||
pub fn get_event(cmd: &SetRoleToEmployeeCommand) -> Self {
|
||||
Self {
|
||||
emp_id: *cmd.emp_id(),
|
||||
added_by: *cmd.adding_by(),
|
||||
store_id: *cmd.store_id(),
|
||||
role: cmd.role().clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
39
src/identity/domain/set_role_to_employee_command.rs
Normal file
39
src/identity/domain/set_role_to_employee_command.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use derive_builder::Builder;
|
||||
use derive_getters::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::employee_aggregate::*;
|
||||
use super::role_aggregate::Role;
|
||||
|
||||
#[derive(
|
||||
Clone, Debug, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd, Getters, Builder,
|
||||
)]
|
||||
pub struct SetRoleToEmployeeCommand {
|
||||
adding_by: Uuid,
|
||||
emp_id: Uuid,
|
||||
store_id: Uuid,
|
||||
role: Role,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use crate::utils::uuid::tests::UUID;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl SetRoleToEmployeeCommand {
|
||||
pub fn get_cmd() -> Self {
|
||||
Self {
|
||||
emp_id: UUID,
|
||||
adding_by: UUID,
|
||||
store_id: UUID,
|
||||
role: Role::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
149
utils/gen_cmd_event_inits.sh
Executable file
149
utils/gen_cmd_event_inits.sh
Executable file
|
@ -0,0 +1,149 @@
|
|||
#!/bin/bash
|
||||
|
||||
help() {
|
||||
echo "Usage: gen_cmd_event_inits.sh
|
||||
<domain name>
|
||||
<service name>
|
||||
<cmd struct name>
|
||||
<event struct name>
|
||||
|
||||
<service struct name>
|
||||
<service obj>
|
||||
<service method>
|
||||
<service use case>
|
||||
"
|
||||
}
|
||||
|
||||
run() {
|
||||
echo "//service
|
||||
use mockall::predicate::*;
|
||||
use mockall::*;
|
||||
#[automock]
|
||||
|
||||
// command
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
impl $3 {
|
||||
pub fn get_cmd() -> Self {
|
||||
Self {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// events
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::$1::application::services::$2::command::$3;
|
||||
|
||||
impl $4 {
|
||||
pub fn get_event(cmd: &${3}) -> Self {
|
||||
Self {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// service
|
||||
|
||||
impl $5 {
|
||||
pub fn mock_service(
|
||||
times: Option<usize>,
|
||||
cmd: command::$3,
|
||||
) -> $6 {
|
||||
let mut m = Mock${8}::default();
|
||||
let res = events::$4::get_event(&cmd);
|
||||
|
||||
if let Some(times) = times {
|
||||
m.expect_${7}().times(times).return_const(Ok(res));
|
||||
} else {
|
||||
m.expect_${7}().return_const(Ok(res));
|
||||
}
|
||||
|
||||
std::sync::Arc::new(m)
|
||||
}
|
||||
}
|
||||
|
||||
// aggregate test
|
||||
|
||||
#[test]
|
||||
fn test_user_aggregate_${2}() {
|
||||
use crate::${1}::application::services::${2}::{*, service::*, command::*, events::*};
|
||||
|
||||
let cmd = ${3}::get_cmd();
|
||||
let expected = ${4}::get_event(&cmd);
|
||||
let expected = IdentityEvent::$(echo $4 | sed 's/Event//')(expected);
|
||||
|
||||
let mut services = MockIdentityServicesInterface::new();
|
||||
services
|
||||
.expect_${2}()
|
||||
.times(IS_CALLED_ONLY_ONCE.unwrap())
|
||||
.return_const(${5}::mock_service(
|
||||
IS_CALLED_ONLY_ONCE,
|
||||
cmd.clone(),
|
||||
));
|
||||
|
||||
UserTestFramework::with(Arc::new(services))
|
||||
.given_no_previous_events()
|
||||
.when(IdentityCommand::$(echo $3 | sed 's/Command//')(cmd))
|
||||
.then_expect_events(vec![expected]);
|
||||
}
|
||||
|
||||
"
|
||||
|
||||
|
||||
}
|
||||
# echo "Usage: gen_cmd_event_inits.sh
|
||||
# <domain name>
|
||||
# <service name>
|
||||
# <cmd struct name>
|
||||
# <event struct name>
|
||||
#
|
||||
# <service struct name>
|
||||
# <service obj>
|
||||
# <service method>
|
||||
|
||||
|
||||
if [ -z $1 ]
|
||||
then
|
||||
help
|
||||
elif [ -z $2 ]
|
||||
then
|
||||
help
|
||||
elif [ -z $3 ]
|
||||
then
|
||||
help
|
||||
|
||||
elif [ -z $4 ]
|
||||
then
|
||||
help
|
||||
|
||||
elif [ -z $5 ]
|
||||
then
|
||||
help
|
||||
|
||||
elif [ -z $6 ]
|
||||
then
|
||||
help
|
||||
|
||||
elif [ -z $7 ]
|
||||
then
|
||||
help
|
||||
|
||||
elif [ -z $8 ]
|
||||
then
|
||||
help
|
||||
|
||||
|
||||
|
||||
|
||||
else
|
||||
run "${@}"
|
||||
run "${@}" | wl-copy
|
||||
fi
|
Loading…
Reference in a new issue