From 1433045276ba42ef7e7ea02ab7285c4118dd74a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Parreira?= <158507411+joaofrparreira@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:42:00 +0000 Subject: [PATCH] feat: issue 1594 synchronize users ad app (#1596) --- .../mocks/factories/azure-user-factory.ts | 7 +- backend/src/modules/azure/azure.module.ts | 14 ++- backend/src/modules/azure/azure.providers.ts | 13 +- backend/src/modules/azure/constants.ts | 2 + ...-ad-users.cron.azure.use-case.interface.ts | 5 + .../services/auth.azure.service.interface.ts | 1 + ...onize-ad-users.cron.azure.use-case.spec.ts | 94 ++++++++++++++ ...ynchronize-ad-users.cron.azure.use-case.ts | 117 ++++++++++++++++++ .../azure/services/auth.azure.service.ts | 34 ++++- ...all-users-include-deleted.use-case.spec.ts | 34 +++++ .../get-all-users-include-deleted.use-case.ts | 17 +++ backend/src/modules/users/constants.ts | 2 + .../repository/user.repository.interface.ts | 1 + .../users/repository/user.repository.ts | 7 ++ backend/src/modules/users/users.module.ts | 13 +- backend/src/modules/users/users.providers.ts | 7 ++ 16 files changed, 360 insertions(+), 8 deletions(-) create mode 100644 backend/src/modules/azure/interfaces/schedules/synchronize-ad-users.cron.azure.use-case.interface.ts create mode 100644 backend/src/modules/azure/schedules/synchronize-ad-users.cron.azure.use-case.spec.ts create mode 100644 backend/src/modules/azure/schedules/synchronize-ad-users.cron.azure.use-case.ts create mode 100644 backend/src/modules/users/applications/get-all-users-include-deleted.use-case.spec.ts create mode 100644 backend/src/modules/users/applications/get-all-users-include-deleted.use-case.ts diff --git a/backend/src/libs/test-utils/mocks/factories/azure-user-factory.ts b/backend/src/libs/test-utils/mocks/factories/azure-user-factory.ts index 2df3dfdbf..c3cb93c0c 100644 --- a/backend/src/libs/test-utils/mocks/factories/azure-user-factory.ts +++ b/backend/src/libs/test-utils/mocks/factories/azure-user-factory.ts @@ -3,11 +3,14 @@ import { buildTestFactory } from './generic-factory.mock'; import { AzureUserDTO } from 'src/modules/azure/dto/azure-user.dto'; const mockUserData = (): AzureUserDTO => { - const mail = faker.internet.email(); + //xGeeks AD style, the '.' is mandatory for some tests + const firstName = faker.name.firstName(); + const lastName = faker.name.lastName(); + const mail = firstName[0].toLowerCase() + '.' + lastName.toLowerCase() + '@xgeeks.com'; return { id: faker.datatype.uuid(), - displayName: faker.name.firstName() + faker.name.lastName(), + displayName: firstName + ' ' + lastName, mail: mail, userPrincipalName: mail, createdDateTime: faker.date.past(5), diff --git a/backend/src/modules/azure/azure.module.ts b/backend/src/modules/azure/azure.module.ts index ded8b6d86..46f2c6bb3 100644 --- a/backend/src/modules/azure/azure.module.ts +++ b/backend/src/modules/azure/azure.module.ts @@ -3,13 +3,23 @@ import AuthModule from '../auth/auth.module'; import { CommunicationModule } from '../communication/communication.module'; import { StorageModule } from '../storage/storage.module'; import UsersModule from '../users/users.module'; -import { authAzureService, checkUserUseCase, registerOrLoginUseCase } from './azure.providers'; +import { + authAzureService, + checkUserUseCase, + registerOrLoginUseCase, + synchronizeADUsersCronUseCase +} from './azure.providers'; import AzureController from './controller/azure.controller'; import { JwtRegister } from 'src/infrastructure/config/jwt.register'; @Module({ imports: [UsersModule, AuthModule, CommunicationModule, StorageModule, JwtRegister], controllers: [AzureController], - providers: [authAzureService, checkUserUseCase, registerOrLoginUseCase] + providers: [ + authAzureService, + checkUserUseCase, + registerOrLoginUseCase, + synchronizeADUsersCronUseCase + ] }) export default class AzureModule {} diff --git a/backend/src/modules/azure/azure.providers.ts b/backend/src/modules/azure/azure.providers.ts index 80308a3af..96cbfb90d 100644 --- a/backend/src/modules/azure/azure.providers.ts +++ b/backend/src/modules/azure/azure.providers.ts @@ -1,6 +1,12 @@ import { CheckUserAzureUseCase } from './applications/check-user.azure.use-case'; import { RegisterOrLoginAzureUseCase } from './applications/register-or-login.azure.use-case'; -import { AUTH_AZURE_SERVICE, CHECK_USER_USE_CASE, REGISTER_OR_LOGIN_USE_CASE } from './constants'; +import { + AUTH_AZURE_SERVICE, + CHECK_USER_USE_CASE, + REGISTER_OR_LOGIN_USE_CASE, + SYNCHRONIZE_AD_USERS_CRON_USE_CASE +} from './constants'; +import { SynchronizeADUsersCronUseCase } from './schedules/synchronize-ad-users.cron.azure.use-case'; import AuthAzureService from './services/auth.azure.service'; /* SERVICE */ @@ -21,3 +27,8 @@ export const registerOrLoginUseCase = { provide: REGISTER_OR_LOGIN_USE_CASE, useClass: RegisterOrLoginAzureUseCase }; + +export const synchronizeADUsersCronUseCase = { + provide: SYNCHRONIZE_AD_USERS_CRON_USE_CASE, + useClass: SynchronizeADUsersCronUseCase +}; diff --git a/backend/src/modules/azure/constants.ts b/backend/src/modules/azure/constants.ts index 94c2ff7c8..dd791f57c 100644 --- a/backend/src/modules/azure/constants.ts +++ b/backend/src/modules/azure/constants.ts @@ -7,3 +7,5 @@ export const AUTH_AZURE_SERVICE = 'AuthAzureService'; export const REGISTER_OR_LOGIN_USE_CASE = 'RegisterOrLoginUseCase'; export const CHECK_USER_USE_CASE = 'CheckUserUseCase'; + +export const SYNCHRONIZE_AD_USERS_CRON_USE_CASE = 'SynchronizeADUsersCronUseCase'; diff --git a/backend/src/modules/azure/interfaces/schedules/synchronize-ad-users.cron.azure.use-case.interface.ts b/backend/src/modules/azure/interfaces/schedules/synchronize-ad-users.cron.azure.use-case.interface.ts new file mode 100644 index 000000000..1c9371f57 --- /dev/null +++ b/backend/src/modules/azure/interfaces/schedules/synchronize-ad-users.cron.azure.use-case.interface.ts @@ -0,0 +1,5 @@ +import { UseCase } from 'src/libs/interfaces/use-case.interface'; + +export interface SynchronizeADUsersCronUseCaseInterface extends UseCase { + execute(): Promise; +} diff --git a/backend/src/modules/azure/interfaces/services/auth.azure.service.interface.ts b/backend/src/modules/azure/interfaces/services/auth.azure.service.interface.ts index f705e88c2..1b20a218c 100644 --- a/backend/src/modules/azure/interfaces/services/auth.azure.service.interface.ts +++ b/backend/src/modules/azure/interfaces/services/auth.azure.service.interface.ts @@ -3,4 +3,5 @@ import { AzureUserDTO } from '../../dto/azure-user.dto'; export interface AuthAzureServiceInterface { getUserFromAzure(email: string): Promise; fetchUserPhoto(userId: string): Promise; + getADUsers(): Promise>; } diff --git a/backend/src/modules/azure/schedules/synchronize-ad-users.cron.azure.use-case.spec.ts b/backend/src/modules/azure/schedules/synchronize-ad-users.cron.azure.use-case.spec.ts new file mode 100644 index 000000000..8c675bf8b --- /dev/null +++ b/backend/src/modules/azure/schedules/synchronize-ad-users.cron.azure.use-case.spec.ts @@ -0,0 +1,94 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AUTH_AZURE_SERVICE } from '../constants'; +import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { AuthAzureServiceInterface } from '../interfaces/services/auth.azure.service.interface'; +import { DeleteUserUseCase } from 'src/modules/users/applications/delete-user.use-case'; +import { + CREATE_USER_SERVICE, + DELETE_USER_USE_CASE, + GET_ALL_USERS_INCLUDE_DELETED_USE_CASE +} from 'src/modules/users/constants'; +import { UserFactory } from 'src/libs/test-utils/mocks/factories/user-factory'; +import { UseCase } from 'src/libs/interfaces/use-case.interface'; +import { AzureUserFactory } from 'src/libs/test-utils/mocks/factories/azure-user-factory'; +import { CreateUserServiceInterface } from 'src/modules/users/interfaces/services/create.user.service.interface'; +import GetAllUsersIncludeDeletedUseCase from 'src/modules/users/applications/get-all-users-include-deleted.use-case'; +import { SynchronizeADUsersCronUseCase } from './synchronize-ad-users.cron.azure.use-case'; + +const usersAD = AzureUserFactory.createMany(4, () => ({ + deletedDateTime: null, + employeeLeaveDateTime: null +})); +const users = UserFactory.createMany( + 4, + usersAD.map((u) => ({ + email: u.mail, + firstName: u.displayName.split(' ')[0], + lastName: u.displayName.split(' ')[1] + })) as never +); + +describe('SynchronizeAdUsersCronUseCase', () => { + let synchronizeADUsers: UseCase; + let authAzureServiceMock: DeepMocked; + let getAllUsersMock: DeepMocked; + let deleteUserMock: DeepMocked; + let createUserServiceMock: DeepMocked; + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + SynchronizeADUsersCronUseCase, + { + provide: AUTH_AZURE_SERVICE, + useValue: createMock() + }, + { + provide: GET_ALL_USERS_INCLUDE_DELETED_USE_CASE, + useValue: createMock() + }, + { + provide: DELETE_USER_USE_CASE, + useValue: createMock() + }, + { + provide: CREATE_USER_SERVICE, + useValue: createMock() + } + ] + }).compile(); + + synchronizeADUsers = module.get(SynchronizeADUsersCronUseCase); + authAzureServiceMock = module.get(AUTH_AZURE_SERVICE); + getAllUsersMock = module.get(GET_ALL_USERS_INCLUDE_DELETED_USE_CASE); + deleteUserMock = module.get(DELETE_USER_USE_CASE); + createUserServiceMock = module.get(CREATE_USER_SERVICE); + }); + beforeEach(() => { + jest.clearAllMocks(); + jest.resetAllMocks(); + }); + + it('should be defined', () => { + expect(synchronizeADUsers).toBeDefined(); + }); + it('execute', async () => { + const userNotInApp = AzureUserFactory.create({ + employeeLeaveDateTime: null, + deletedDateTime: null + }); + const finalADUsers = [userNotInApp, ...usersAD]; + authAzureServiceMock.getADUsers.mockResolvedValueOnce(finalADUsers); + const userNotInAD = UserFactory.create(); + const finalAppUsers = [userNotInAD, ...users]; + getAllUsersMock.execute.mockResolvedValueOnce(finalAppUsers); + await synchronizeADUsers.execute(); + expect(deleteUserMock.execute).toBeCalledWith(userNotInAD._id); + expect(deleteUserMock.execute.mock.calls).toEqual([[userNotInAD._id]]); + expect(createUserServiceMock.create).toHaveBeenCalledWith({ + email: userNotInApp.mail, + firstName: userNotInApp.displayName.split(' ')[0], + lastName: userNotInApp.displayName.split(' ')[1], + providerAccountCreatedAt: userNotInApp.createdDateTime + }); + }); +}); diff --git a/backend/src/modules/azure/schedules/synchronize-ad-users.cron.azure.use-case.ts b/backend/src/modules/azure/schedules/synchronize-ad-users.cron.azure.use-case.ts new file mode 100644 index 000000000..039055e27 --- /dev/null +++ b/backend/src/modules/azure/schedules/synchronize-ad-users.cron.azure.use-case.ts @@ -0,0 +1,117 @@ +import { Inject, Injectable, Logger } from '@nestjs/common'; +import { Cron } from '@nestjs/schedule'; +import { SynchronizeADUsersCronUseCaseInterface } from '../interfaces/schedules/synchronize-ad-users.cron.azure.use-case.interface'; +import { AUTH_AZURE_SERVICE } from '../constants'; +import { AuthAzureServiceInterface } from '../interfaces/services/auth.azure.service.interface'; +import { + CREATE_USER_SERVICE, + DELETE_USER_USE_CASE, + GET_ALL_USERS_INCLUDE_DELETED_USE_CASE +} from 'src/modules/users/constants'; +import { UseCase } from 'src/libs/interfaces/use-case.interface'; +import User from 'src/modules/users/entities/user.schema'; +import { AzureUserDTO } from '../dto/azure-user.dto'; +import { CreateUserServiceInterface } from 'src/modules/users/interfaces/services/create.user.service.interface'; + +@Injectable() +export class SynchronizeADUsersCronUseCase implements SynchronizeADUsersCronUseCaseInterface { + private readonly logger: Logger = new Logger(SynchronizeADUsersCronUseCase.name); + constructor( + @Inject(AUTH_AZURE_SERVICE) + private readonly authAzureService: AuthAzureServiceInterface, + @Inject(GET_ALL_USERS_INCLUDE_DELETED_USE_CASE) + private readonly getAllUsersIncludeDeletedUseCase: UseCase>, + @Inject(DELETE_USER_USE_CASE) + private readonly deleteUserUseCase: UseCase, + @Inject(CREATE_USER_SERVICE) + private readonly createUserService: CreateUserServiceInterface + ) {} + + //Runs every saturday at mid-night + //@Cron('0 0 * * 6') + @Cron('0 14 * * *') + async execute() { + try { + const usersADAll = await this.authAzureService.getADUsers(); + + if (!usersADAll.length) { + throw new Error('Azure AD users list is empty.'); + } + + const usersApp = await this.getAllUsersIncludeDeletedUseCase.execute(); + + if (!usersApp.length) { + throw new Error('Split app users list is empty.'); + } + + const today = new Date(); + //Filter out users that don't have a '.' in the beggining of the email + let usersADFiltered = usersADAll.filter((u) => + /[a-z]+\.[a-zA-Z0-9]+@/.test(u.userPrincipalName) + ); + + //Filter out users that have a deletedDateTime bigger than 'today' + usersADFiltered = usersADFiltered.filter((u) => + 'deletedDateTime' in u ? u.deletedDateTime === null || u.deletedDateTime >= today : true + ); + + //Filter out users that have a employeeLeaveDateTime bigger than 'today' + usersADFiltered = usersADFiltered.filter((u) => + 'employeeLeaveDateTime' in u + ? u.employeeLeaveDateTime === null || u.employeeLeaveDateTime >= today + : true + ); + + await this.removeUsersFromApp(usersADFiltered, usersApp); + await this.addUsersToApp(usersADFiltered, usersApp); + } catch (err) { + this.logger.error( + `An error occurred while synchronizing users between AD and Aplit Application. Message: ${err.message}` + ); + } + } + + private async removeUsersFromApp(usersADFiltered: Array, usersApp: Array) { + const notIntersectedUsers = usersApp.filter( + (userApp) => + userApp.isDeleted === false && + usersADFiltered.findIndex( + (userAd) => (userAd.mail ?? userAd.userPrincipalName) === userApp.email + ) === -1 + ); + + for (const user of notIntersectedUsers) { + try { + await this.deleteUserUseCase.execute(user._id); + } catch (err) { + this.logger.error( + `An error occurred while deleting user with id '${user._id}' through the syncronize AD Users Cron. Message: ${err.message}` + ); + } + } + } + private async addUsersToApp(usersADFiltered: Array, usersApp: Array) { + const notIntersectedUsers = usersADFiltered.filter( + (userAd) => + usersApp.findIndex( + (userApp) => userApp.email === (userAd.mail ?? userAd.userPrincipalName) + ) === -1 + ); + + for (const user of notIntersectedUsers) { + try { + const splittedName = user.displayName.split(' '); + await this.createUserService.create({ + email: user.mail, + firstName: splittedName[0], + lastName: splittedName.at(-1), + providerAccountCreatedAt: user.createdDateTime + }); + } catch (err) { + this.logger.error( + `An error as occurred while creating user with email '${user.mail}' through the syncronize AD Users Cron. Message: ${err.message}` + ); + } + } + } +} diff --git a/backend/src/modules/azure/services/auth.azure.service.ts b/backend/src/modules/azure/services/auth.azure.service.ts index 0467f7eac..934cb97b8 100644 --- a/backend/src/modules/azure/services/auth.azure.service.ts +++ b/backend/src/modules/azure/services/auth.azure.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { AuthAzureServiceInterface } from '../interfaces/services/auth.azure.service.interface'; import { ConfidentialClientApplication } from '@azure/msal-node'; -import { Client } from '@microsoft/microsoft-graph-client'; +import { Client, PageCollection } from '@microsoft/microsoft-graph-client'; import { ConfigService } from '@nestjs/config'; import { AZURE_AUTHORITY, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET } from 'src/libs/constants/azure'; import { AzureUserDTO } from '../dto/azure-user.dto'; @@ -70,4 +70,36 @@ export default class AuthAzureService implements AuthAzureServiceInterface { fetchUserPhoto(userId: string) { return this.graphClient.api(`/users/${userId}/photo/$value`).get(); } + + async getADUsers(): Promise> { + let response: PageCollection = await this.graphClient + .api('/users') + .header('ConsistencyLevel', 'eventual') + .count(true) + .filter("endswith(userPrincipalName,'xgeeks.com') AND accountEnabled eq true") + .select([ + 'id', + 'mail', + 'displayName', + 'userPrincipalName', + 'createdDateTime', + 'accountEnabled', + 'deletedDateTime', + 'employeeLeaveDateTime' + ]) + .get(); + + let users = []; + while (response.value.length > 0) { + users = users.concat(response.value); + + if (response['@odata.nextLink']) { + response = await this.graphClient.api(response['@odata.nextLink']).get(); + } else { + break; + } + } + + return users; + } } diff --git a/backend/src/modules/users/applications/get-all-users-include-deleted.use-case.spec.ts b/backend/src/modules/users/applications/get-all-users-include-deleted.use-case.spec.ts new file mode 100644 index 000000000..e39db79f9 --- /dev/null +++ b/backend/src/modules/users/applications/get-all-users-include-deleted.use-case.spec.ts @@ -0,0 +1,34 @@ +import { createMock } from '@golevelup/ts-jest'; +import { UseCase } from 'src/libs/interfaces/use-case.interface'; +import { UserRepositoryInterface } from '../repository/user.repository.interface'; +import { Test, TestingModule } from '@nestjs/testing'; +import User from '../entities/user.schema'; +import { USER_REPOSITORY } from 'src/modules/users/constants'; +import GetAllUsersIncludeDeletedUseCase from './get-all-users-include-deleted.use-case'; + +describe('GetAllUsersIncludeDeletedUseCase', () => { + let getAllUsers: UseCase; + + beforeAll(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [ + GetAllUsersIncludeDeletedUseCase, + { + provide: USER_REPOSITORY, + useValue: createMock() + } + ] + }).compile(); + + getAllUsers = module.get(GetAllUsersIncludeDeletedUseCase); + }); + + beforeEach(() => { + jest.clearAllMocks(); + jest.resetAllMocks(); + }); + + it('should be defined', () => { + expect(getAllUsers).toBeDefined(); + }); +}); diff --git a/backend/src/modules/users/applications/get-all-users-include-deleted.use-case.ts b/backend/src/modules/users/applications/get-all-users-include-deleted.use-case.ts new file mode 100644 index 000000000..d2c59efe4 --- /dev/null +++ b/backend/src/modules/users/applications/get-all-users-include-deleted.use-case.ts @@ -0,0 +1,17 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { USER_REPOSITORY } from '../constants'; +import { UserRepositoryInterface } from '../repository/user.repository.interface'; +import { UseCase } from 'src/libs/interfaces/use-case.interface'; +import User from '../entities/user.schema'; + +@Injectable() +export default class GetAllUsersIncludeDeletedUseCase implements UseCase { + constructor( + @Inject(USER_REPOSITORY) + private readonly userRepository: UserRepositoryInterface + ) {} + + execute() { + return this.userRepository.getAllUsersIncludeDeleted(); + } +} diff --git a/backend/src/modules/users/constants.ts b/backend/src/modules/users/constants.ts index 851993420..067182faf 100644 --- a/backend/src/modules/users/constants.ts +++ b/backend/src/modules/users/constants.ts @@ -9,6 +9,8 @@ export const UPDATE_USER_SERVICE = 'UpdateUserService'; export const GET_ALL_USERS_USE_CASE = 'GetAllUsersUseCase'; +export const GET_ALL_USERS_INCLUDE_DELETED_USE_CASE = 'GetAllUsersIncludeDeletedUseCase'; + export const GET_ALL_USERS_WITH_TEAM_USE_CASE = 'GetAllUsersWithTeamsUseCase'; export const GET_USER_USE_CASE = 'GetUserUseCase'; diff --git a/backend/src/modules/users/repository/user.repository.interface.ts b/backend/src/modules/users/repository/user.repository.interface.ts index 0b8f6bcd6..a612cbfbf 100644 --- a/backend/src/modules/users/repository/user.repository.interface.ts +++ b/backend/src/modules/users/repository/user.repository.interface.ts @@ -14,6 +14,7 @@ export interface UserRepositoryInterface extends BaseInterfaceRepository { getAllWithPagination(page: number, size: number, searchUser?: string): Promise; getAllSignedUpUsers(): Promise; getSignedUpUsersCount(): Promise; + getAllUsersIncludeDeleted(): Promise>>; updateUserUpdatedAt(user: string): Promise; findDeleted(): Promise>>; forceDelete(query: FilterQuery, options?: QueryOptions): Promise; diff --git a/backend/src/modules/users/repository/user.repository.ts b/backend/src/modules/users/repository/user.repository.ts index 4d89fd8bb..68f4ab1db 100644 --- a/backend/src/modules/users/repository/user.repository.ts +++ b/backend/src/modules/users/repository/user.repository.ts @@ -99,6 +99,13 @@ export class UserRepository .exec(); } + getAllUsersIncludeDeleted() { + return this.model + .find({ isDeleted: { $in: [true, false] } }) + .select('-password -__v -currentHashedRefreshToken -strategy -updatedAt') + .exec(); + } + updateUserUpdatedAt(user: string) { return this.findOneByFieldAndUpdate({ _id: user }, { $set: { updatedAt: new Date() } }); } diff --git a/backend/src/modules/users/users.module.ts b/backend/src/modules/users/users.module.ts index d6c7cc0d3..f616a458d 100644 --- a/backend/src/modules/users/users.module.ts +++ b/backend/src/modules/users/users.module.ts @@ -10,6 +10,7 @@ import UsersController from './controller/users.controller'; import { createUserService, deleteUserUseCase, + getAllUsersIncludeDeletedUseCase, getAllUsersUseCase, getAllUsersWithTeamsUseCase, getUserService, @@ -40,9 +41,17 @@ import BoardUsersModule from '../boardUsers/boardusers.module'; getUserService, deleteUserUseCase, updateUserService, - userRepository + userRepository, + getAllUsersIncludeDeletedUseCase, + deleteUserUseCase ], controllers: [UsersController], - exports: [createUserService, getUserService, updateUserService] + exports: [ + createUserService, + getUserService, + updateUserService, + getAllUsersIncludeDeletedUseCase, + deleteUserUseCase + ] }) export default class UsersModule {} diff --git a/backend/src/modules/users/users.providers.ts b/backend/src/modules/users/users.providers.ts index 7263b1f7e..659865c16 100644 --- a/backend/src/modules/users/users.providers.ts +++ b/backend/src/modules/users/users.providers.ts @@ -2,6 +2,7 @@ import { DeleteUserUseCase } from './applications/delete-user.use-case'; import { CREATE_USER_SERVICE, DELETE_USER_USE_CASE, + GET_ALL_USERS_INCLUDE_DELETED_USE_CASE, GET_ALL_USERS_USE_CASE, GET_ALL_USERS_WITH_TEAM_USE_CASE, GET_USER_SERVICE, @@ -18,6 +19,7 @@ import { GetUserUseCase } from './applications/get-user.use-case'; import GetAllUsersUseCase from './applications/get-all-users.use-case'; import UpdateSAdminUseCase from './applications/update-sadmin.use-case'; import UpdateUserService from './services/update.user.service'; +import GetAllUsersIncludeDeletedUseCase from './applications/get-all-users-include-deleted.use-case'; /* SERVICES */ export const createUserService = { @@ -68,3 +70,8 @@ export const userRepository = { provide: USER_REPOSITORY, useClass: UserRepository }; + +export const getAllUsersIncludeDeletedUseCase = { + provide: GET_ALL_USERS_INCLUDE_DELETED_USE_CASE, + useClass: GetAllUsersIncludeDeletedUseCase +};