dcb-backoffice/src/app/modules/hub-users-management/merchant-users.service.ts

329 lines
10 KiB
TypeScript

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '@environments/environment';
import { Observable, map, catchError, throwError, of } from 'rxjs';
import {
User,
CreateUserDto,
UpdateUserDto,
ResetPasswordDto,
PaginatedUserResponse,
AvailableRolesResponse,
SearchUsersParams,
UserRole,
UserType,
UserUtils
} from '@core/models/dcb-bo-hub-user.model';
// Interfaces pour les nouvelles réponses
export interface TokenResponse {
access_token: string;
expires_in: number;
refresh_token: string;
refresh_expires_in: number;
token_type: string;
'not-before-policy': number;
session_state: string;
scope: string;
}
export interface UserProfileResponse {
id: string;
username: string;
email: string;
firstName: string;
lastName: string;
emailVerified: boolean;
enabled: boolean;
role: string[];
merchantPartnerId?: string;
createdBy?: string;
createdByUsername?: string;
}
export interface MessageResponse {
message: string;
}
export interface MerchantPartnerIdResponse {
merchantPartnerId: string | null;
}
// ===== SERVICE UTILISATEURS MERCHANT =====
@Injectable({ providedIn: 'root' })
export class MerchantUsersService {
private http = inject(HttpClient);
private baseApiUrl = `${environment.iamApiUrl}/merchant-users`;
// === MÉTHODES SPÉCIFIQUES MERCHANT ===
createMerchantUser(createUserDto: CreateUserDto): Observable<User> {
// Utiliser la validation centralisée
const errors = UserUtils.validateUserCreation(createUserDto);
if (errors.length > 0) {
return throwError(() => errors.join(', '));
}
if (!createUserDto.username?.trim()) {
return throwError(() => 'Username is required and cannot be empty');
}
if (!createUserDto.email?.trim()) {
return throwError(() => 'Email is required and cannot be empty');
}
if (!createUserDto.password || createUserDto.password.length < 8) {
return throwError(() => 'Password must be at least 8 characters');
}
const payload = {
username: createUserDto.username.trim(),
email: createUserDto.email.trim(),
firstName: (createUserDto.firstName || '').trim(),
lastName: (createUserDto.lastName || '').trim(),
password: createUserDto.password,
role: createUserDto.role,
enabled: createUserDto.enabled !== undefined ? createUserDto.enabled : true,
emailVerified: createUserDto.emailVerified !== undefined ? createUserDto.emailVerified : true,
userType: createUserDto.userType.trim()
};
return this.http.post<User>(`${this.baseApiUrl}`, payload).pipe(
map(user => this.mapToUserModel(user, UserType.MERCHANT_PARTNER)),
catchError(error => {
console.error('Error creating merchant user:', error);
return throwError(() => error);
})
);
}
getMyMerchantUsers(): Observable<User[]> {
return this.http.get<User[]>(`${this.baseApiUrl}`).pipe(
map(users => users.map(user => this.mapToUserModel(user, UserType.MERCHANT_PARTNER))),
catchError(error => {
console.error('Error loading my merchant users:', error);
return throwError(() => error);
})
);
}
getMerchantUserById(id: string | undefined): Observable<User> {
return this.http.get<User>(`${this.baseApiUrl}/${id}`).pipe(
map(user => this.mapToUserModel(user, UserType.MERCHANT_PARTNER)),
catchError(error => {
console.error(`Error loading merchant user ${id}:`, error);
return throwError(() => error);
})
);
}
getMerchantUsers(page: number = 1, limit: number = 10, filters?: SearchUsersParams): Observable<PaginatedUserResponse> {
return this.getMyMerchantUsers().pipe(
map(users => this.filterAndPaginateUsers(users, page, limit, filters)),
catchError(error => {
console.error('Error loading merchant users:', error);
return throwError(() => error);
})
);
}
updateMerchantUser(id: string, updateUserDto: UpdateUserDto): Observable<User> {
const payload: any = {
firstName: updateUserDto.firstName,
lastName: updateUserDto.lastName,
email: updateUserDto.email,
enabled: updateUserDto.enabled
};
return this.http.put<User>(`${this.baseApiUrl}/${id}`, payload).pipe(
map(user => this.mapToUserModel(user, UserType.MERCHANT_PARTNER)),
catchError(error => {
console.error(`Error updating merchant user ${id}:`, error);
return throwError(() => error);
})
);
}
deleteMerchantUser(id: string): Observable<MessageResponse> {
return this.http.delete<MessageResponse>(`${this.baseApiUrl}/${id}`).pipe(
catchError(error => {
console.error(`Error deleting merchant user ${id}:`, error);
return throwError(() => error);
})
);
}
resetMerchantUserPassword(id: string, resetPasswordDto: ResetPasswordDto): Observable<MessageResponse> {
const payload = {
newPassword: resetPasswordDto.newPassword,
temporary: resetPasswordDto.temporary !== undefined ? resetPasswordDto.temporary : true
};
return this.http.post<MessageResponse>(
`${this.baseApiUrl}/${id}/reset-password`,
payload
).pipe(
catchError(error => {
console.error(`Error resetting password for merchant user ${id}:`, error);
return throwError(() => error);
})
);
}
updateMerchantUserRole(id: string, role: UserRole): Observable<User> {
const merchantRoles = [UserRole.DCB_PARTNER_ADMIN, UserRole.DCB_PARTNER_SUPPORT, UserRole.DCB_PARTNER_MANAGER];
if (!merchantRoles.includes(role)) {
return throwError(() => 'Invalid role for Merchant user');
}
return this.http.put<User>(`${this.baseApiUrl}/${id}/role`, { role }).pipe(
map(user => this.mapToUserModel(user, UserType.MERCHANT_PARTNER)),
catchError(error => {
console.error(`Error updating role for merchant user ${id}:`, error);
return throwError(() => error);
})
);
}
enableMerchantUser(id: string): Observable<User> {
return this.updateMerchantUser(id, { enabled: true });
}
disableMerchantUser(id: string): Observable<User> {
return this.updateMerchantUser(id, { enabled: false });
}
getAvailableMerchantRoles(): Observable<AvailableRolesResponse> {
return of({
roles: [
{
value: UserRole.DCB_PARTNER_ADMIN,
label: 'Partner Admin',
description: 'Full administrative access within the merchant partner',
allowedForCreation: true,
userType: UserType.MERCHANT_PARTNER
},
{
value: UserRole.DCB_PARTNER_MANAGER,
label: 'Partner Manager',
description: 'Manager access with limited administrative capabilities',
allowedForCreation: true,
userType: UserType.MERCHANT_PARTNER
},
{
value: UserRole.DCB_PARTNER_SUPPORT,
label: 'Partner Support',
description: 'Support role with read-only and basic operational access',
allowedForCreation: true,
userType: UserType.MERCHANT_PARTNER
}
]
} as AvailableRolesResponse);
}
searchMerchantUsers(params: SearchUsersParams): Observable<User[]> {
return this.getMerchantUsers(1, 1000, params).pipe(
map(response => response.users)
);
}
merchantUserExists(username: string): Observable<{ exists: boolean }> {
return this.searchMerchantUsers({ query: username }).pipe(
map(users => ({
exists: users.some(user => user.username === username)
})),
catchError(error => {
console.error('Error checking if merchant user exists:', error);
return of({ exists: false });
})
);
}
getMerchantUsersByRole(role: UserRole): Observable<User[]> {
return this.searchMerchantUsers({ role });
}
getActiveMerchantUsers(): Observable<User[]> {
return this.searchMerchantUsers({ enabled: true });
}
getInactiveMerchantUsers(): Observable<User[]> {
return this.searchMerchantUsers({ enabled: false });
}
// === MÉTHODES UTILITAIRES ===
isValidRoleForMerchant(role: UserRole): boolean {
const merchantRoles = [UserRole.DCB_PARTNER_ADMIN, UserRole.DCB_PARTNER_MANAGER, UserRole.DCB_PARTNER_SUPPORT];
return merchantRoles.includes(role);
}
// === MAPPING ET FILTRAGE ===
private mapToUserModel(apiUser: any, userType: UserType): User {
return {
id: apiUser.id,
username: apiUser.username,
email: apiUser.email,
firstName: apiUser.firstName,
lastName: apiUser.lastName,
enabled: apiUser.enabled,
emailVerified: apiUser.emailVerified,
userType: userType,
role: apiUser.role,
createdBy: apiUser.createdBy,
createdByUsername: apiUser.createdByUsername,
createdTimestamp: apiUser.createdTimestamp,
lastLogin: apiUser.lastLogin
};
}
private filterAndPaginateUsers(
users: User[],
page: number,
limit: number,
filters?: SearchUsersParams
): PaginatedUserResponse {
let filteredUsers = users;
if (filters) {
if (filters.query) {
const query = filters.query.toLowerCase();
filteredUsers = filteredUsers.filter(user =>
user.username.toLowerCase().includes(query) ||
user.email.toLowerCase().includes(query) ||
user.firstName?.toLowerCase().includes(query) ||
user.lastName?.toLowerCase().includes(query)
);
}
if (filters.role) {
filteredUsers = filteredUsers.filter(user => user.role.includes(filters.role!));
}
if (filters.enabled !== undefined) {
filteredUsers = filteredUsers.filter(user => user.enabled === filters.enabled);
}
if (filters.userType) {
filteredUsers = filteredUsers.filter(user => user.userType === filters.userType);
}
}
// Pagination côté client
const startIndex = (page - 1) * limit;
const endIndex = startIndex + limit;
const paginatedUsers = filteredUsers.slice(startIndex, endIndex);
return {
users: paginatedUsers,
total: filteredUsers.length,
page,
limit,
totalPages: Math.ceil(filteredUsers.length / limit)
};
}
}