feat: add DCB User Service API - Authentication system with KEYCLOAK - Modular architecture with services for each feature
This commit is contained in:
parent
2093e50b27
commit
572b54bcfa
@ -13,7 +13,15 @@ export enum UserRole {
|
|||||||
// Rôles Merchant Partner (avec merchantPartnerId obligatoire = ID du DCB_PARTNER)
|
// Rôles Merchant Partner (avec merchantPartnerId obligatoire = ID du DCB_PARTNER)
|
||||||
DCB_PARTNER_ADMIN = 'dcb-partner-admin',
|
DCB_PARTNER_ADMIN = 'dcb-partner-admin',
|
||||||
DCB_PARTNER_MANAGER = 'dcb-partner-manager',
|
DCB_PARTNER_MANAGER = 'dcb-partner-manager',
|
||||||
DCB_PARTNER_SUPPORT = 'dcb-partner-support'
|
DCB_PARTNER_SUPPORT = 'dcb-partner-support',
|
||||||
|
|
||||||
|
MERCHANT_CONFIG_ADMIN = 'ADMIN',
|
||||||
|
MERCHANT_CONFIG_MANAGER = 'MANAGER',
|
||||||
|
MERCHANT_CONFIG_TECHNICAL = 'TECHNICAL',
|
||||||
|
MERCHANT_CONFIG_VIEWER = 'VIEWER',
|
||||||
|
|
||||||
|
//ADMIN, MANAGER, TECHNICAL, VIEWER
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enum pour le contexte Angular (identique à l'ancien)
|
// Enum pour le contexte Angular (identique à l'ancien)
|
||||||
@ -39,7 +47,7 @@ export interface UsersStatistics {
|
|||||||
totalUsers: number;
|
totalUsers: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
// === MODÈLE USER PRINCIPAL ===
|
// dcb-bo-hub-user.model.ts - MIS À JOUR
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string; // UUID Keycloak
|
id: string; // UUID Keycloak
|
||||||
username: string;
|
username: string;
|
||||||
@ -49,10 +57,13 @@ export interface User {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
emailVerified: boolean;
|
emailVerified: boolean;
|
||||||
userType: UserType; // HUB ou MERCHANT_PARTNER
|
userType: UserType; // HUB ou MERCHANT_PARTNER
|
||||||
merchantPartnerId?: string; // Pour les users merchant: ID du DCB_PARTNER propriétaire
|
|
||||||
|
// C'est l'ID du "merchant" (DCB_PARTNER) qui est propriétaire
|
||||||
|
merchantPartnerId?: string; // Référence à l'ID Keycloak du DCB_PARTNER
|
||||||
|
|
||||||
role: UserRole;
|
role: UserRole;
|
||||||
// Merchant Config
|
// Merchant Config - Stocker l'ID du merchant dans l'autre système
|
||||||
merchantConfigId?: number; // ID INT dans Merchant Config
|
merchantConfigId?: string; // ID INT dans Merchant Config pour CE merchant
|
||||||
createdBy?: string;
|
createdBy?: string;
|
||||||
createdByUsername?: string;
|
createdByUsername?: string;
|
||||||
createdTimestamp: number;
|
createdTimestamp: number;
|
||||||
@ -79,6 +90,7 @@ export interface CreateUserDto {
|
|||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
emailVerified?: boolean;
|
emailVerified?: boolean;
|
||||||
merchantPartnerId?: string;
|
merchantPartnerId?: string;
|
||||||
|
merchantConfigId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateUserDto {
|
export interface UpdateUserDto {
|
||||||
@ -138,6 +150,7 @@ export interface SearchUsersParams {
|
|||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
userType?: UserType;
|
userType?: UserType;
|
||||||
merchantPartnerId?: string;
|
merchantPartnerId?: string;
|
||||||
|
merchantConfigId?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
limit?: number;
|
limit?: number;
|
||||||
}
|
}
|
||||||
@ -164,7 +177,13 @@ export class UserUtils {
|
|||||||
[UserRole.DCB_PARTNER]: 'DCB Partner',
|
[UserRole.DCB_PARTNER]: 'DCB Partner',
|
||||||
[UserRole.DCB_PARTNER_ADMIN]: 'Partner Admin',
|
[UserRole.DCB_PARTNER_ADMIN]: 'Partner Admin',
|
||||||
[UserRole.DCB_PARTNER_MANAGER]: 'Partner Manager',
|
[UserRole.DCB_PARTNER_MANAGER]: 'Partner Manager',
|
||||||
[UserRole.DCB_PARTNER_SUPPORT]: 'Partner Support'
|
[UserRole.DCB_PARTNER_SUPPORT]: 'Partner Support',
|
||||||
|
|
||||||
|
[UserRole.MERCHANT_CONFIG_ADMIN]: 'ADMIN',
|
||||||
|
[UserRole.MERCHANT_CONFIG_MANAGER]: 'MANAGER',
|
||||||
|
[UserRole.MERCHANT_CONFIG_TECHNICAL]: 'TECHNICAL',
|
||||||
|
[UserRole.MERCHANT_CONFIG_VIEWER]: 'VIEWER',
|
||||||
|
|
||||||
};
|
};
|
||||||
return roleNames[role] || role;
|
return roleNames[role] || role;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,12 @@ export enum UserRole {
|
|||||||
// Rôles Merchant Partner (avec merchantPartnerId obligatoire)
|
// Rôles Merchant Partner (avec merchantPartnerId obligatoire)
|
||||||
DCB_PARTNER_ADMIN = 'dcb-partner-admin',
|
DCB_PARTNER_ADMIN = 'dcb-partner-admin',
|
||||||
DCB_PARTNER_MANAGER = 'dcb-partner-manager',
|
DCB_PARTNER_MANAGER = 'dcb-partner-manager',
|
||||||
DCB_PARTNER_SUPPORT = 'dcb-partner-support'
|
DCB_PARTNER_SUPPORT = 'dcb-partner-support',
|
||||||
|
|
||||||
|
MERCHANT_CONFIG_ADMIN = 'ADMIN',
|
||||||
|
MERCHANT_CONFIG_MANAGER = 'MANAGER',
|
||||||
|
MERCHANT_CONFIG_TECHNICAL = 'TECHNICAL',
|
||||||
|
MERCHANT_CONFIG_VIEWER = 'VIEWER',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ConfigType {
|
export enum ConfigType {
|
||||||
@ -32,38 +37,38 @@ export enum Operator {
|
|||||||
|
|
||||||
// === MODÈLES PRINCIPAUX ===
|
// === MODÈLES PRINCIPAUX ===
|
||||||
export interface MerchantConfig {
|
export interface MerchantConfig {
|
||||||
id?: string;
|
id?: number
|
||||||
name: ConfigType | string;
|
name: ConfigType | string;
|
||||||
value: string;
|
value: string;
|
||||||
operatorId: Operator | null;
|
operatorId: Operator | 1;
|
||||||
merchantPartnerId?: string;
|
merchantPartnerId?: number
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
updatedAt?: string;
|
updatedAt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TechnicalContact {
|
export interface TechnicalContact {
|
||||||
id?: string;
|
id?: number
|
||||||
firstName: string;
|
firstName: string;
|
||||||
lastName: string;
|
lastName: string;
|
||||||
phone: string;
|
phone: string;
|
||||||
email: string;
|
email: string;
|
||||||
merchantPartnerId?: string;
|
merchantPartnerId?: number
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
updatedAt?: string;
|
updatedAt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MerchantUser {
|
export interface MerchantUser {
|
||||||
userId: string;
|
userId: number;
|
||||||
role: UserRole; // Utilisation de vos rôles existants
|
role: UserRole;
|
||||||
username?: string;
|
username?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
firstName?: string;
|
firstName?: string;
|
||||||
lastName?: string;
|
lastName?: string;
|
||||||
merchantPartnerId?: string;
|
merchantPartnerId?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Merchant {
|
export interface Merchant {
|
||||||
id?: string;
|
id?: number
|
||||||
name: string;
|
name: string;
|
||||||
logo?: string;
|
logo?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
@ -81,7 +86,7 @@ export interface ApiMerchantConfig {
|
|||||||
id?: number;
|
id?: number;
|
||||||
name: ConfigType | string;
|
name: ConfigType | string;
|
||||||
value: string;
|
value: string;
|
||||||
operatorId: Operator | null;
|
operatorId: Operator | 1;
|
||||||
merchantPartnerId?: number;
|
merchantPartnerId?: number;
|
||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
updatedAt?: string;
|
updatedAt?: string;
|
||||||
@ -99,7 +104,7 @@ export interface ApiTechnicalContact {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiMerchantUser {
|
export interface ApiMerchantUser {
|
||||||
userId: string;
|
userId: number;
|
||||||
role: UserRole;
|
role: UserRole;
|
||||||
username?: string;
|
username?: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
@ -139,13 +144,13 @@ export interface UpdateMerchantDto extends Partial<CreateMerchantDto> {}
|
|||||||
export interface UpdateMerchantConfigDto {
|
export interface UpdateMerchantConfigDto {
|
||||||
name?: string;
|
name?: string;
|
||||||
value?: string;
|
value?: string;
|
||||||
operatorId?: Operator | null;
|
operatorId?: Operator | 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AddUserToMerchantDto {
|
export interface AddUserToMerchantDto {
|
||||||
userId: string;
|
userId: string;
|
||||||
role: UserRole;
|
role: UserRole;
|
||||||
merchantPartnerId: string;
|
merchantPartnerId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateUserRoleDto {
|
export interface UpdateUserRoleDto {
|
||||||
|
|||||||
@ -1,116 +0,0 @@
|
|||||||
// === MODÈLES POUR LA SYNCHRONISATION ===
|
|
||||||
|
|
||||||
import { User, UserRole } from "./dcb-bo-hub-user.model";
|
|
||||||
|
|
||||||
export interface MerchantUserSyncDto {
|
|
||||||
// Données de base pour Keycloak
|
|
||||||
username: string;
|
|
||||||
email: string;
|
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
password: string;
|
|
||||||
role: UserRole;
|
|
||||||
enabled?: boolean;
|
|
||||||
emailVerified?: boolean;
|
|
||||||
|
|
||||||
// Référence au DCB_PARTNER propriétaire
|
|
||||||
merchantPartnerId: string; // ID Keycloak du DCB_PARTNER
|
|
||||||
|
|
||||||
// Données pour Merchant Config
|
|
||||||
merchantConfig?: {
|
|
||||||
phone?: string;
|
|
||||||
technicalContacts?: Array<{
|
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
email: string;
|
|
||||||
phone: string;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IdMapping {
|
|
||||||
id?: number;
|
|
||||||
keycloakId: string; // UUID Keycloak
|
|
||||||
merchantConfigId: number; // INT Merchant Config
|
|
||||||
merchantPartnerId: string; // ID du DCB_PARTNER propriétaire
|
|
||||||
entityType: 'merchant' | 'user';
|
|
||||||
username?: string;
|
|
||||||
email?: string;
|
|
||||||
createdAt?: Date;
|
|
||||||
updatedAt?: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SyncResult {
|
|
||||||
success: boolean;
|
|
||||||
keycloakUser?: User;
|
|
||||||
merchantConfigUser?: any;
|
|
||||||
mapping?: IdMapping;
|
|
||||||
errors?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DCBPartnerInfo {
|
|
||||||
id: string;
|
|
||||||
username: string;
|
|
||||||
email: string;
|
|
||||||
enabled: boolean;
|
|
||||||
merchantPartnerId?: string; // Pour DCB_PARTNER, ce doit être undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
// === UTILITAIRES DE SYNCHRONISATION ===
|
|
||||||
export class SyncUtils {
|
|
||||||
static validateDCBPartner(dcbPartner: User): string[] {
|
|
||||||
const errors: string[] = [];
|
|
||||||
|
|
||||||
if (!dcbPartner) {
|
|
||||||
errors.push('DCB_PARTNER non trouvé');
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dcbPartner.role !== UserRole.DCB_PARTNER) {
|
|
||||||
errors.push(`L'utilisateur ${dcbPartner.username} n'est pas un DCB_PARTNER`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dcbPartner.enabled) {
|
|
||||||
errors.push(`Le DCB_PARTNER ${dcbPartner.username} est désactivé`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dcbPartner.merchantPartnerId) {
|
|
||||||
errors.push(`Un DCB_PARTNER ne doit pas avoir de merchantPartnerId`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
static validateMerchantUserCreation(dto: MerchantUserSyncDto): string[] {
|
|
||||||
const errors: string[] = [];
|
|
||||||
|
|
||||||
if (!dto.merchantPartnerId) {
|
|
||||||
errors.push('merchantPartnerId est obligatoire');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dto.username?.trim()) {
|
|
||||||
errors.push('Username est obligatoire');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dto.email?.trim()) {
|
|
||||||
errors.push('Email est obligatoire');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dto.password || dto.password.length < 8) {
|
|
||||||
errors.push('Le mot de passe doit contenir au moins 8 caractères');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validation du rôle
|
|
||||||
const merchantRoles = [
|
|
||||||
UserRole.DCB_PARTNER_ADMIN,
|
|
||||||
UserRole.DCB_PARTNER_MANAGER,
|
|
||||||
UserRole.DCB_PARTNER_SUPPORT
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!merchantRoles.includes(dto.role)) {
|
|
||||||
errors.push(`Rôle invalide pour un utilisateur merchant: ${dto.role}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return errors;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -332,7 +332,11 @@ export class RoleManagementService {
|
|||||||
[UserRole.DCB_PARTNER]: 'Partenaire commercial principal',
|
[UserRole.DCB_PARTNER]: 'Partenaire commercial principal',
|
||||||
[UserRole.DCB_PARTNER_ADMIN]: 'Administrateur de partenaire marchand',
|
[UserRole.DCB_PARTNER_ADMIN]: 'Administrateur de partenaire marchand',
|
||||||
[UserRole.DCB_PARTNER_MANAGER]: 'Manager opérationnel partenaire',
|
[UserRole.DCB_PARTNER_MANAGER]: 'Manager opérationnel partenaire',
|
||||||
[UserRole.DCB_PARTNER_SUPPORT]: 'Support technique partenaire'
|
[UserRole.DCB_PARTNER_SUPPORT]: 'Support technique partenaire',
|
||||||
|
[UserRole.MERCHANT_CONFIG_ADMIN]: 'Administrateur de partenaire marchand',
|
||||||
|
[UserRole.MERCHANT_CONFIG_MANAGER]: 'Manager opérationnel partenaire',
|
||||||
|
[UserRole.MERCHANT_CONFIG_TECHNICAL]: 'Support technique partenaire',
|
||||||
|
[UserRole.MERCHANT_CONFIG_VIEWER]: 'Support technique partenaire'
|
||||||
};
|
};
|
||||||
return roleDescriptions[userRole] || 'Description non disponible';
|
return roleDescriptions[userRole] || 'Description non disponible';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,12 +83,16 @@ export class UserProfileComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Map role to display name
|
// Map role to display name
|
||||||
const roleDisplayNames: { [key in UserRole]: string } = {
|
const roleDisplayNames: { [key in UserRole]: string } = {
|
||||||
[UserRole.DCB_ADMIN]: 'Administrateur',
|
[UserRole.DCB_ADMIN]: 'Administrateur système avec tous les accès',
|
||||||
[UserRole.DCB_SUPPORT]: 'Support Technique',
|
[UserRole.DCB_SUPPORT]: 'Support technique avec accès étendus',
|
||||||
[UserRole.DCB_PARTNER]: 'Partenaire',
|
[UserRole.DCB_PARTNER]: 'Partenaire commercial principal',
|
||||||
[UserRole.DCB_PARTNER_ADMIN]: 'Admin Partenaire',
|
[UserRole.DCB_PARTNER_ADMIN]: 'Administrateur de partenaire marchand',
|
||||||
[UserRole.DCB_PARTNER_MANAGER]: 'Manager Partenaire',
|
[UserRole.DCB_PARTNER_MANAGER]: 'Manager opérationnel partenaire',
|
||||||
[UserRole.DCB_PARTNER_SUPPORT]: 'Support Partenaire',
|
[UserRole.DCB_PARTNER_SUPPORT]: 'Support technique partenaire',
|
||||||
|
[UserRole.MERCHANT_CONFIG_ADMIN]: 'Administrateur de partenaire marchand',
|
||||||
|
[UserRole.MERCHANT_CONFIG_MANAGER]: 'Manager opérationnel partenaire',
|
||||||
|
[UserRole.MERCHANT_CONFIG_TECHNICAL]: 'Support technique partenaire',
|
||||||
|
[UserRole.MERCHANT_CONFIG_VIEWER]: 'Support technique partenaire'
|
||||||
};
|
};
|
||||||
|
|
||||||
const primaryRole = role;
|
const primaryRole = role;
|
||||||
|
|||||||
@ -52,15 +52,20 @@ export interface MerchantPartnerIdResponse {
|
|||||||
merchantPartnerId: string | null;
|
merchantPartnerId: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface MerchantConfigIdResponse {
|
||||||
|
merchantPartnerId: number;
|
||||||
|
}
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class HubUsersService {
|
export class HubUsersService {
|
||||||
private http = inject(HttpClient);
|
private http = inject(HttpClient);
|
||||||
private baseApiUrl = `${environment.iamApiUrl}/hub-users`;
|
private baseIamApiUrl = `${environment.iamApiUrl}/hub-users`;
|
||||||
|
private baseConfigApiUrl = `${environment.configApiUrl}/merchants`;
|
||||||
|
|
||||||
// === MÉTHODES SPÉCIFIQUES HUB ===
|
// === MÉTHODES SPÉCIFIQUES HUB ===
|
||||||
|
|
||||||
getAllUsers(): Observable<GlobalUsersOverview> {
|
getAllUsers(): Observable<GlobalUsersOverview> {
|
||||||
return this.http.get<GlobalUsersOverview>(`${this.baseApiUrl}/all-users`).pipe(
|
return this.http.get<GlobalUsersOverview>(`${this.baseIamApiUrl}/all-users`).pipe(
|
||||||
map(response => {
|
map(response => {
|
||||||
// Validation de la réponse
|
// Validation de la réponse
|
||||||
if (!response || !Array.isArray(response.hubUsers) || !Array.isArray(response.merchantUsers)) {
|
if (!response || !Array.isArray(response.hubUsers) || !Array.isArray(response.merchantUsers)) {
|
||||||
@ -89,7 +94,7 @@ export class HubUsersService {
|
|||||||
|
|
||||||
// Méthode pour les statistiques seulement
|
// Méthode pour les statistiques seulement
|
||||||
getUsersStatistics(): Observable<UsersStatistics> {
|
getUsersStatistics(): Observable<UsersStatistics> {
|
||||||
return this.http.get<GlobalUsersOverview>(`${this.baseApiUrl}`).pipe(
|
return this.http.get<GlobalUsersOverview>(`${this.baseIamApiUrl}`).pipe(
|
||||||
map(response => response.statistics),
|
map(response => response.statistics),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.error('Error loading users statistics:', error);
|
console.error('Error loading users statistics:', error);
|
||||||
@ -118,6 +123,10 @@ export class HubUsersService {
|
|||||||
return throwError(() => 'Password must be at least 8 characters');
|
return throwError(() => 'Password must be at least 8 characters');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!createUserDto.merchantConfigId) {
|
||||||
|
return throwError(() => 'Merchant Config is required and cannot be empty');
|
||||||
|
}
|
||||||
|
|
||||||
// Avant de créer le payload, valider les données
|
// Avant de créer le payload, valider les données
|
||||||
if (createUserDto.userType === UserType.MERCHANT_PARTNER && !createUserDto.merchantPartnerId) {
|
if (createUserDto.userType === UserType.MERCHANT_PARTNER && !createUserDto.merchantPartnerId) {
|
||||||
return throwError(() => 'merchantPartnerId is required for merchant users');
|
return throwError(() => 'merchantPartnerId is required for merchant users');
|
||||||
@ -133,6 +142,7 @@ export class HubUsersService {
|
|||||||
enabled: createUserDto.enabled ?? true,
|
enabled: createUserDto.enabled ?? true,
|
||||||
emailVerified: createUserDto.emailVerified ?? true,
|
emailVerified: createUserDto.emailVerified ?? true,
|
||||||
merchantPartnerId: createUserDto.merchantPartnerId,
|
merchantPartnerId: createUserDto.merchantPartnerId,
|
||||||
|
merchantConfigId: createUserDto.merchantConfigId,
|
||||||
userType: createUserDto.userType.trim()
|
userType: createUserDto.userType.trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -143,7 +153,7 @@ export class HubUsersService {
|
|||||||
|
|
||||||
console.log(payload)
|
console.log(payload)
|
||||||
|
|
||||||
return this.http.post<User>(`${this.baseApiUrl}`, payload).pipe(
|
return this.http.post<User>(`${this.baseIamApiUrl}`, payload).pipe(
|
||||||
map(user => this.mapToUserModel(user, UserType.HUB)),
|
map(user => this.mapToUserModel(user, UserType.HUB)),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.error('Error creating hub user:', error);
|
console.error('Error creating hub user:', error);
|
||||||
@ -153,7 +163,7 @@ export class HubUsersService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getHubUsers(page: number = 1, limit: number = 10, filters?: SearchUsersParams): Observable<PaginatedUserResponse> {
|
getHubUsers(page: number = 1, limit: number = 10, filters?: SearchUsersParams): Observable<PaginatedUserResponse> {
|
||||||
return this.http.get<User[]>(`${this.baseApiUrl}`).pipe(
|
return this.http.get<User[]>(`${this.baseIamApiUrl}`).pipe(
|
||||||
map(users => {
|
map(users => {
|
||||||
const mappedUsers = users.map(user => this.mapToUserModel(user, UserType.HUB));
|
const mappedUsers = users.map(user => this.mapToUserModel(user, UserType.HUB));
|
||||||
return this.filterAndPaginateUsers(mappedUsers, page, limit, filters);
|
return this.filterAndPaginateUsers(mappedUsers, page, limit, filters);
|
||||||
@ -166,7 +176,7 @@ export class HubUsersService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAllDcbPartners(page: number = 1, limit: number = 10, filters?: SearchUsersParams): Observable<PaginatedUserResponse> {
|
getAllDcbPartners(page: number = 1, limit: number = 10, filters?: SearchUsersParams): Observable<PaginatedUserResponse> {
|
||||||
return this.http.get<User[]>(`${this.baseApiUrl}/partners/dcb-partners`).pipe(
|
return this.http.get<User[]>(`${this.baseIamApiUrl}/partners/dcb-partners`).pipe(
|
||||||
map(users => {
|
map(users => {
|
||||||
const mappedUsers = users.map(user => this.mapToUserModel(user, UserType.HUB));
|
const mappedUsers = users.map(user => this.mapToUserModel(user, UserType.HUB));
|
||||||
return this.filterAndPaginateUsers(mappedUsers, page, limit, filters);
|
return this.filterAndPaginateUsers(mappedUsers, page, limit, filters);
|
||||||
@ -179,7 +189,7 @@ export class HubUsersService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getHubUserById(id: string): Observable<User> {
|
getHubUserById(id: string): Observable<User> {
|
||||||
return this.http.get<User>(`${this.baseApiUrl}/${id}`).pipe(
|
return this.http.get<User>(`${this.baseIamApiUrl}/${id}`).pipe(
|
||||||
map(user => this.mapToUserModel(user, UserType.HUB)),
|
map(user => this.mapToUserModel(user, UserType.HUB)),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.error(`Error loading hub user ${id}:`, error);
|
console.error(`Error loading hub user ${id}:`, error);
|
||||||
@ -196,7 +206,7 @@ export class HubUsersService {
|
|||||||
enabled: updateUserDto.enabled
|
enabled: updateUserDto.enabled
|
||||||
};
|
};
|
||||||
|
|
||||||
return this.http.put<User>(`${this.baseApiUrl}/${id}`, payload).pipe(
|
return this.http.put<User>(`${this.baseIamApiUrl}/${id}`, payload).pipe(
|
||||||
map(user => this.mapToUserModel(user, UserType.HUB)),
|
map(user => this.mapToUserModel(user, UserType.HUB)),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.error(`Error updating hub user ${id}:`, error);
|
console.error(`Error updating hub user ${id}:`, error);
|
||||||
@ -211,7 +221,7 @@ export class HubUsersService {
|
|||||||
return throwError(() => 'Invalid role for Hub user');
|
return throwError(() => 'Invalid role for Hub user');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.http.put<User>(`${this.baseApiUrl}/${id}/role`, { role }).pipe(
|
return this.http.put<User>(`${this.baseIamApiUrl}/${id}/role`, { role }).pipe(
|
||||||
map(user => this.mapToUserModel(user, UserType.HUB)),
|
map(user => this.mapToUserModel(user, UserType.HUB)),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.error(`Error updating role for hub user ${id}:`, error);
|
console.error(`Error updating role for hub user ${id}:`, error);
|
||||||
@ -221,7 +231,7 @@ export class HubUsersService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteHubUser(id: string): Observable<MessageResponse> {
|
deleteHubUser(id: string): Observable<MessageResponse> {
|
||||||
return this.http.delete<MessageResponse>(`${this.baseApiUrl}/${id}`).pipe(
|
return this.http.delete<MessageResponse>(`${this.baseIamApiUrl}/${id}`).pipe(
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.error(`Error deleting hub user ${id}:`, error);
|
console.error(`Error deleting hub user ${id}:`, error);
|
||||||
return throwError(() => error);
|
return throwError(() => error);
|
||||||
@ -236,7 +246,7 @@ export class HubUsersService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return this.http.post<MessageResponse>(
|
return this.http.post<MessageResponse>(
|
||||||
`${this.baseApiUrl}/${id}/reset-password`,
|
`${this.baseIamApiUrl}/${id}/reset-password`,
|
||||||
payload
|
payload
|
||||||
).pipe(
|
).pipe(
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
@ -283,7 +293,7 @@ export class HubUsersService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getHubUsersByRole(role: UserRole): Observable<User[]> {
|
getHubUsersByRole(role: UserRole): Observable<User[]> {
|
||||||
return this.http.get<User[]>(`${this.baseApiUrl}/role/${role}`).pipe(
|
return this.http.get<User[]>(`${this.baseIamApiUrl}/role/${role}`).pipe(
|
||||||
map(users => users.map(user => this.mapToUserModel(user, UserType.HUB))),
|
map(users => users.map(user => this.mapToUserModel(user, UserType.HUB))),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.error(`Error loading hub users with role ${role}:`, error);
|
console.error(`Error loading hub users with role ${role}:`, error);
|
||||||
@ -330,6 +340,7 @@ export class HubUsersService {
|
|||||||
emailVerified: apiUser.emailVerified,
|
emailVerified: apiUser.emailVerified,
|
||||||
userType: userType,
|
userType: userType,
|
||||||
merchantPartnerId: apiUser.merchantPartnerId,
|
merchantPartnerId: apiUser.merchantPartnerId,
|
||||||
|
merchantConfigId: apiUser.merchantConfigId,
|
||||||
role: apiUser.role,
|
role: apiUser.role,
|
||||||
createdBy: apiUser.createdBy,
|
createdBy: apiUser.createdBy,
|
||||||
createdByUsername: apiUser.createdByUsername,
|
createdByUsername: apiUser.createdByUsername,
|
||||||
|
|||||||
494
src/app/modules/hub-users-management/merchant-manager.ts
Normal file
494
src/app/modules/hub-users-management/merchant-manager.ts
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
import { Observable, of, throwError, firstValueFrom, lastValueFrom } from 'rxjs';
|
||||||
|
|
||||||
|
// Mock des services
|
||||||
|
const mockMerchantConfigService = {
|
||||||
|
createMerchant: (data: any): Observable<any> => of({
|
||||||
|
id: 123,
|
||||||
|
name: data.name,
|
||||||
|
users: []
|
||||||
|
}),
|
||||||
|
getMerchantById: (id: number): Observable<any> => of({
|
||||||
|
id,
|
||||||
|
name: 'Test Merchant',
|
||||||
|
users: []
|
||||||
|
}),
|
||||||
|
updateMerchant: (id: number, data: any): Observable<any> => of({
|
||||||
|
id,
|
||||||
|
...data
|
||||||
|
}),
|
||||||
|
deleteMerchant: (id: number): Observable<void> => of(void 0),
|
||||||
|
getMerchantUsers: (id: number): Observable<any[]> => of([
|
||||||
|
{ userId: 456, email: 'user1@test.com', role: 'MERCHANT_CONFIG_ADMIN' }
|
||||||
|
]),
|
||||||
|
addUserToMerchant: (data: any): Observable<any> => of({
|
||||||
|
userId: data.userId,
|
||||||
|
email: data.email,
|
||||||
|
role: data.role
|
||||||
|
}),
|
||||||
|
updateUserRole: (merchantId: number, userId: number, data: any): Observable<any> => of({
|
||||||
|
userId,
|
||||||
|
role: data.role
|
||||||
|
}),
|
||||||
|
removeUserFromMerchant: (merchantId: number, userId: number): Observable<void> => of(void 0)
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockHubUsersService = {
|
||||||
|
createHubUser: (data: any): Observable<any> => of({
|
||||||
|
id: 'keycloak-123',
|
||||||
|
username: data.username,
|
||||||
|
merchantConfigId: 123
|
||||||
|
}),
|
||||||
|
getHubUserById: (id: string): Observable<any> => of({
|
||||||
|
id,
|
||||||
|
username: 'owner',
|
||||||
|
merchantConfigId: 123
|
||||||
|
}),
|
||||||
|
updateHubUser: (id: string, data: any): Observable<any> => of({
|
||||||
|
id,
|
||||||
|
...data
|
||||||
|
}),
|
||||||
|
deleteHubUser: (id: string): Observable<void> => of(void 0),
|
||||||
|
getAllDcbPartners: (): Observable<{users: any[]}> => of({
|
||||||
|
users: [{ id: 'keycloak-123', merchantConfigId: 123 }]
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockMerchantUsersService = {
|
||||||
|
createMerchantUser: (data: any): Observable<any> => of({
|
||||||
|
id: 'keycloak-user-456',
|
||||||
|
email: data.email,
|
||||||
|
merchantPartnerId: data.merchantPartnerId
|
||||||
|
}),
|
||||||
|
updateMerchantUser: (id: string, data: any): Observable<any> => of({
|
||||||
|
id,
|
||||||
|
...data
|
||||||
|
}),
|
||||||
|
deleteMerchantUser: (id: string): Observable<void> => of(void 0),
|
||||||
|
getMerchantUsersByPartner: (partnerId: string): Observable<any[]> => of([
|
||||||
|
{ id: 'keycloak-user-456', email: 'user1@test.com' }
|
||||||
|
]),
|
||||||
|
searchMerchantUsers: (params: any): Observable<any[]> => of([
|
||||||
|
{ id: 'keycloak-user-456', email: 'user1@test.com' }
|
||||||
|
]),
|
||||||
|
resetMerchantUserPassword: (id: string, data: any): Observable<{ message: string }> =>
|
||||||
|
of({ message: 'Password reset' })
|
||||||
|
};
|
||||||
|
|
||||||
|
// Classe de test CRUD avec RxJS 7+
|
||||||
|
export class MerchantCrudTest {
|
||||||
|
private testData = {
|
||||||
|
currentMerchantId: 0,
|
||||||
|
currentDcbPartnerId: '',
|
||||||
|
currentUserId: '',
|
||||||
|
currentMerchantConfigUserId: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
// ==================== NOUVELLES MÉTHODES RxJS 7+ ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remplacer toPromise() par firstValueFrom() ou lastValueFrom()
|
||||||
|
*/
|
||||||
|
private async toPromise<T>(observable: Observable<T>): Promise<T> {
|
||||||
|
// firstValueFrom: prend la première valeur et complète
|
||||||
|
// lastValueFrom: prend la dernière valeur avant completion
|
||||||
|
return firstValueFrom(observable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== TESTS CRUD ====================
|
||||||
|
|
||||||
|
async testCreateMerchant(): Promise<any> {
|
||||||
|
console.log('🧪 TEST: CREATE Merchant');
|
||||||
|
|
||||||
|
const merchantData = {
|
||||||
|
name: 'Test Merchant',
|
||||||
|
adresse: '123 Test Street',
|
||||||
|
phone: '+33612345678',
|
||||||
|
configs: [{ name: 'API_KEY', value: 'test-key' }],
|
||||||
|
technicalContacts: [{
|
||||||
|
firstName: 'John',
|
||||||
|
lastName: 'Doe',
|
||||||
|
phone: '+33612345678',
|
||||||
|
email: 'john@test.com'
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
|
||||||
|
const ownerData = {
|
||||||
|
username: 'owner.test',
|
||||||
|
email: 'owner@test.com',
|
||||||
|
password: 'Password123!',
|
||||||
|
firstName: 'John',
|
||||||
|
lastName: 'Doe'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Créer merchant dans Merchant Config (RxJS 7+)
|
||||||
|
const merchantConfig = await firstValueFrom(
|
||||||
|
mockMerchantConfigService.createMerchant(merchantData)
|
||||||
|
);
|
||||||
|
console.log('✅ Merchant Config créé:', merchantConfig);
|
||||||
|
|
||||||
|
// 2. Créer DCB_PARTNER dans Keycloak
|
||||||
|
const dcbPartnerDto = {
|
||||||
|
...ownerData,
|
||||||
|
userType: 'HUB',
|
||||||
|
role: 'DCB_PARTNER',
|
||||||
|
merchantConfigId: merchantConfig.id
|
||||||
|
};
|
||||||
|
|
||||||
|
const keycloakMerchant = await firstValueFrom(
|
||||||
|
mockHubUsersService.createHubUser(dcbPartnerDto)
|
||||||
|
);
|
||||||
|
console.log('✅ DCB_PARTNER créé:', keycloakMerchant);
|
||||||
|
|
||||||
|
// Sauvegarder les IDs
|
||||||
|
this.testData.currentMerchantId = merchantConfig.id;
|
||||||
|
this.testData.currentDcbPartnerId = keycloakMerchant.id;
|
||||||
|
|
||||||
|
const result = {
|
||||||
|
merchantConfig,
|
||||||
|
keycloakMerchant
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('🎯 RESULTAT CREATE:', result);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR CREATE:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testCreateMerchantUser(): Promise<void> {
|
||||||
|
console.log('🧪 TEST: CREATE Merchant User');
|
||||||
|
|
||||||
|
if (!this.testData.currentMerchantId || !this.testData.currentDcbPartnerId) {
|
||||||
|
console.log('⚠️ Créez d\'abord un merchant');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const userData = {
|
||||||
|
username: 'user.test',
|
||||||
|
email: 'user@test.com',
|
||||||
|
password: 'UserPass123!',
|
||||||
|
firstName: 'Jane',
|
||||||
|
lastName: 'Smith',
|
||||||
|
role: 'DCB_PARTNER_ADMIN'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. Créer dans Keycloak
|
||||||
|
const keycloakUserDto = {
|
||||||
|
...userData,
|
||||||
|
userType: 'MERCHANT_PARTNER',
|
||||||
|
merchantPartnerId: this.testData.currentDcbPartnerId,
|
||||||
|
merchantConfigId: this.testData.currentMerchantId
|
||||||
|
};
|
||||||
|
|
||||||
|
const keycloakUser = await firstValueFrom(
|
||||||
|
mockMerchantUsersService.createMerchantUser(keycloakUserDto)
|
||||||
|
);
|
||||||
|
console.log('✅ Utilisateur Keycloak créé:', keycloakUser);
|
||||||
|
|
||||||
|
// 2. Ajouter à Merchant Config
|
||||||
|
const merchantConfigUserId = this.generateMerchantConfigId(keycloakUser.id);
|
||||||
|
|
||||||
|
const merchantConfigUserDto = {
|
||||||
|
userId: merchantConfigUserId,
|
||||||
|
role: 'MERCHANT_CONFIG_ADMIN',
|
||||||
|
merchantPartnerId: this.testData.currentMerchantId,
|
||||||
|
username: keycloakUser.username,
|
||||||
|
email: keycloakUser.email,
|
||||||
|
firstName: keycloakUser.firstName,
|
||||||
|
lastName: keycloakUser.lastName
|
||||||
|
};
|
||||||
|
|
||||||
|
const merchantConfigUser = await firstValueFrom(
|
||||||
|
mockMerchantConfigService.addUserToMerchant(merchantConfigUserDto)
|
||||||
|
);
|
||||||
|
console.log('✅ Utilisateur Merchant Config ajouté:', merchantConfigUser);
|
||||||
|
|
||||||
|
// Sauvegarder
|
||||||
|
this.testData.currentUserId = keycloakUser.id;
|
||||||
|
this.testData.currentMerchantConfigUserId = merchantConfigUserId;
|
||||||
|
|
||||||
|
console.log('🎯 RESULTAT CREATE USER:', {
|
||||||
|
keycloakUser,
|
||||||
|
merchantConfigUser
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR CREATE USER:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testReadMerchant(): Promise<void> {
|
||||||
|
console.log('🧪 TEST: READ Merchant');
|
||||||
|
|
||||||
|
if (!this.testData.currentMerchantId || !this.testData.currentDcbPartnerId) {
|
||||||
|
console.log('⚠️ Créez d\'abord un merchant');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [merchantConfig, keycloakMerchant] = await Promise.all([
|
||||||
|
firstValueFrom(
|
||||||
|
mockMerchantConfigService.getMerchantById(this.testData.currentMerchantId)
|
||||||
|
),
|
||||||
|
firstValueFrom(
|
||||||
|
mockHubUsersService.getHubUserById(this.testData.currentDcbPartnerId)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('🎯 RESULTAT READ:', {
|
||||||
|
merchantConfig,
|
||||||
|
keycloakMerchant,
|
||||||
|
coherence: keycloakMerchant.merchantConfigId === merchantConfig.id ? '✅ OK' : '❌ INCOHÉRENT'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR READ:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testUpdateMerchant(): Promise<void> {
|
||||||
|
console.log('🧪 TEST: UPDATE Merchant');
|
||||||
|
|
||||||
|
if (!this.testData.currentMerchantId || !this.testData.currentDcbPartnerId) {
|
||||||
|
console.log('⚠️ Créez d\'abord un merchant');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newName = 'Merchant Updated';
|
||||||
|
const newEmail = 'updated@test.com';
|
||||||
|
|
||||||
|
const [updatedMerchant, updatedDcbPartner] = await Promise.all([
|
||||||
|
firstValueFrom(
|
||||||
|
mockMerchantConfigService.updateMerchant(
|
||||||
|
this.testData.currentMerchantId,
|
||||||
|
{ name: newName }
|
||||||
|
)
|
||||||
|
),
|
||||||
|
firstValueFrom(
|
||||||
|
mockHubUsersService.updateHubUser(
|
||||||
|
this.testData.currentDcbPartnerId,
|
||||||
|
{ email: newEmail }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('🎯 RESULTAT UPDATE:', {
|
||||||
|
updatedMerchant,
|
||||||
|
updatedDcbPartner,
|
||||||
|
message: 'Merchant mis à jour avec succès'
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR UPDATE:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testUpdateUser(): Promise<void> {
|
||||||
|
console.log('🧪 TEST: UPDATE User');
|
||||||
|
|
||||||
|
if (!this.testData.currentUserId || !this.testData.currentMerchantConfigUserId) {
|
||||||
|
console.log('⚠️ Créez d\'abord un utilisateur');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newFirstName = 'Jane Updated';
|
||||||
|
const newRole = 'MERCHANT_CONFIG_MANAGER';
|
||||||
|
|
||||||
|
const [updatedKeycloakUser, updatedMerchantConfigUser] = await Promise.all([
|
||||||
|
firstValueFrom(
|
||||||
|
mockMerchantUsersService.updateMerchantUser(
|
||||||
|
this.testData.currentUserId,
|
||||||
|
{ firstName: newFirstName }
|
||||||
|
)
|
||||||
|
),
|
||||||
|
firstValueFrom(
|
||||||
|
mockMerchantConfigService.updateUserRole(
|
||||||
|
this.testData.currentMerchantId,
|
||||||
|
this.testData.currentMerchantConfigUserId,
|
||||||
|
{ role: newRole }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('🎯 RESULTAT UPDATE USER:', {
|
||||||
|
updatedKeycloakUser,
|
||||||
|
updatedMerchantConfigUser
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR UPDATE USER:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testDeleteUser(): Promise<void> {
|
||||||
|
console.log('🧪 TEST: DELETE User');
|
||||||
|
|
||||||
|
if (!this.testData.currentUserId || !this.testData.currentMerchantConfigUserId) {
|
||||||
|
console.log('⚠️ Créez d\'abord un utilisateur');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Promise.all([
|
||||||
|
firstValueFrom(
|
||||||
|
mockMerchantUsersService.deleteMerchantUser(this.testData.currentUserId)
|
||||||
|
),
|
||||||
|
firstValueFrom(
|
||||||
|
mockMerchantConfigService.removeUserFromMerchant(
|
||||||
|
this.testData.currentMerchantId,
|
||||||
|
this.testData.currentMerchantConfigUserId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('🎯 RESULTAT DELETE USER: Utilisateur supprimé des deux systèmes');
|
||||||
|
|
||||||
|
// Réinitialiser
|
||||||
|
this.testData.currentUserId = '';
|
||||||
|
this.testData.currentMerchantConfigUserId = 0;
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR DELETE USER:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testDeleteMerchant(): Promise<void> {
|
||||||
|
console.log('🧪 TEST: DELETE Merchant');
|
||||||
|
|
||||||
|
if (!this.testData.currentMerchantId || !this.testData.currentDcbPartnerId) {
|
||||||
|
console.log('⚠️ Créez d\'abord un merchant');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Récupérer les utilisateurs
|
||||||
|
const users = await firstValueFrom(
|
||||||
|
mockMerchantConfigService.getMerchantUsers(this.testData.currentMerchantId)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`Suppression de ${users.length} utilisateurs...`);
|
||||||
|
|
||||||
|
// Supprimer le merchant et le DCB_PARTNER
|
||||||
|
await Promise.all([
|
||||||
|
firstValueFrom(
|
||||||
|
mockMerchantConfigService.deleteMerchant(this.testData.currentMerchantId)
|
||||||
|
),
|
||||||
|
firstValueFrom(
|
||||||
|
mockHubUsersService.deleteHubUser(this.testData.currentDcbPartnerId)
|
||||||
|
)
|
||||||
|
]);
|
||||||
|
|
||||||
|
console.log('🎯 RESULTAT DELETE MERCHANT: Merchant supprimé des deux systèmes');
|
||||||
|
|
||||||
|
// Réinitialiser
|
||||||
|
this.testData.currentMerchantId = 0;
|
||||||
|
this.testData.currentDcbPartnerId = '';
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR DELETE MERCHANT:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testAllOperations(): Promise<void> {
|
||||||
|
console.log('🚀 DÉMARRAGE DES TESTS COMPLETS');
|
||||||
|
console.log('='.repeat(50));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.testCreateMerchant();
|
||||||
|
console.log('-'.repeat(30));
|
||||||
|
|
||||||
|
await this.testCreateMerchantUser();
|
||||||
|
console.log('-'.repeat(30));
|
||||||
|
|
||||||
|
await this.testReadMerchant();
|
||||||
|
console.log('-'.repeat(30));
|
||||||
|
|
||||||
|
await this.testUpdateMerchant();
|
||||||
|
console.log('-'.repeat(30));
|
||||||
|
|
||||||
|
await this.testUpdateUser();
|
||||||
|
console.log('-'.repeat(30));
|
||||||
|
|
||||||
|
await this.testDeleteUser();
|
||||||
|
console.log('-'.repeat(30));
|
||||||
|
|
||||||
|
await this.testReadMerchant();
|
||||||
|
console.log('-'.repeat(30));
|
||||||
|
|
||||||
|
await this.testDeleteMerchant();
|
||||||
|
console.log('-'.repeat(30));
|
||||||
|
|
||||||
|
console.log('✅ TOUS LES TESTS TERMINÉS AVEC SUCCÈS');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR DANS LES TESTS:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateMerchantConfigId(keycloakId: string): number {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < keycloakId.length; i++) {
|
||||||
|
hash = ((hash << 5) - hash) + keycloakId.charCodeAt(i);
|
||||||
|
hash = hash & hash;
|
||||||
|
}
|
||||||
|
return Math.abs(hash) % 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTestData() {
|
||||||
|
return { ...this.testData };
|
||||||
|
}
|
||||||
|
|
||||||
|
resetTestData() {
|
||||||
|
this.testData = {
|
||||||
|
currentMerchantId: 0,
|
||||||
|
currentDcbPartnerId: '',
|
||||||
|
currentUserId: '',
|
||||||
|
currentMerchantConfigUserId: 0
|
||||||
|
};
|
||||||
|
console.log('🧹 Données de test réinitialisées');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== EXÉCUTION ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fonction pour exécuter les tests
|
||||||
|
*/
|
||||||
|
export async function runTests() {
|
||||||
|
console.log('🚀 LANCEMENT DES TESTS');
|
||||||
|
console.log('='.repeat(60));
|
||||||
|
|
||||||
|
// Option 1: Avec RxJS 7+
|
||||||
|
console.log('\n📦 TEST AVEC RxJS 7+:');
|
||||||
|
console.log('-'.repeat(40));
|
||||||
|
const rxjsTest = new MerchantCrudTest();
|
||||||
|
await rxjsTest.testAllOperations();
|
||||||
|
|
||||||
|
console.log('\n🎉 TOUS LES TESTS SONT TERMINÉS');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exemple d'utilisation dans un environnement de test
|
||||||
|
/*
|
||||||
|
import { runIndividualTests } from './merchant-crud.test';
|
||||||
|
|
||||||
|
const tests = runIndividualTests();
|
||||||
|
|
||||||
|
// Exécuter un test spécifique
|
||||||
|
tests.testCreate().then(() => {
|
||||||
|
console.log('Test CREATE terminé');
|
||||||
|
console.log('Données:', tests.getData());
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ou exécuter tous les tests
|
||||||
|
tests.testAll().then(() => {
|
||||||
|
console.log('Tous les tests terminés');
|
||||||
|
});
|
||||||
|
*/
|
||||||
@ -0,0 +1,684 @@
|
|||||||
|
import { Injectable, inject } from '@angular/core';
|
||||||
|
import { Observable, forkJoin, map, switchMap, catchError, of, throwError, tap } from 'rxjs';
|
||||||
|
import {
|
||||||
|
CreateUserDto,
|
||||||
|
UpdateUserDto,
|
||||||
|
User,
|
||||||
|
UserType,
|
||||||
|
UserRole,
|
||||||
|
UserUtils
|
||||||
|
} from '@core/models/dcb-bo-hub-user.model';
|
||||||
|
|
||||||
|
import { HubUsersService} from '@modules/hub-users-management/hub-users.service';
|
||||||
|
import { MerchantUsersService} from '@modules/hub-users-management/merchant-users.service';
|
||||||
|
|
||||||
|
import { MerchantConfigService } from '@modules/merchant-config/merchant-config.service';
|
||||||
|
|
||||||
|
import {
|
||||||
|
CreateMerchantDto,
|
||||||
|
UpdateMerchantDto,
|
||||||
|
Merchant,
|
||||||
|
MerchantUser,
|
||||||
|
AddUserToMerchantDto,
|
||||||
|
UpdateUserRoleDto
|
||||||
|
} from '@core/models/merchant-config.model';
|
||||||
|
|
||||||
|
export interface MerchantSyncResult {
|
||||||
|
merchantConfig: Merchant;
|
||||||
|
keycloakMerchant: User;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MerchantUserSyncResult {
|
||||||
|
keycloakUser: User;
|
||||||
|
merchantConfigUser: MerchantUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface MerchantSyncStatus {
|
||||||
|
existsInKeycloak: boolean;
|
||||||
|
existsInMerchantConfig: boolean;
|
||||||
|
usersSynced: boolean;
|
||||||
|
syncedUserCount: number;
|
||||||
|
totalUserCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class MerchantSyncCrudService {
|
||||||
|
private hubUsersService = inject(HubUsersService);
|
||||||
|
private merchantUsersService = inject(MerchantUsersService);
|
||||||
|
private merchantConfigService = inject(MerchantConfigService);
|
||||||
|
|
||||||
|
// ==================== CONSTANTES ====================
|
||||||
|
|
||||||
|
private readonly KEYCLOAK_MERCHANT_ROLES = [
|
||||||
|
UserRole.DCB_PARTNER_ADMIN,
|
||||||
|
UserRole.DCB_PARTNER_MANAGER,
|
||||||
|
UserRole.DCB_PARTNER_SUPPORT
|
||||||
|
];
|
||||||
|
|
||||||
|
private readonly MERCHANT_CONFIG_ROLES = [
|
||||||
|
UserRole.MERCHANT_CONFIG_ADMIN,
|
||||||
|
UserRole.MERCHANT_CONFIG_MANAGER,
|
||||||
|
UserRole.MERCHANT_CONFIG_TECHNICAL,
|
||||||
|
UserRole.MERCHANT_CONFIG_VIEWER
|
||||||
|
];
|
||||||
|
|
||||||
|
private readonly ROLE_MAPPING: Map<UserRole, UserRole> = new Map([
|
||||||
|
// Keycloak -> Merchant Config
|
||||||
|
[UserRole.DCB_PARTNER_ADMIN, UserRole.MERCHANT_CONFIG_ADMIN],
|
||||||
|
[UserRole.DCB_PARTNER_MANAGER, UserRole.MERCHANT_CONFIG_MANAGER],
|
||||||
|
[UserRole.DCB_PARTNER_SUPPORT, UserRole.MERCHANT_CONFIG_VIEWER],
|
||||||
|
|
||||||
|
// Merchant Config -> Keycloak
|
||||||
|
[UserRole.MERCHANT_CONFIG_ADMIN, UserRole.DCB_PARTNER_ADMIN],
|
||||||
|
[UserRole.MERCHANT_CONFIG_MANAGER, UserRole.DCB_PARTNER_MANAGER],
|
||||||
|
[UserRole.MERCHANT_CONFIG_TECHNICAL, UserRole.DCB_PARTNER_SUPPORT],
|
||||||
|
[UserRole.MERCHANT_CONFIG_VIEWER, UserRole.DCB_PARTNER_SUPPORT]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ==================== CREATE ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE - Créer un merchant complet dans les deux systèmes
|
||||||
|
*/
|
||||||
|
createMerchant(
|
||||||
|
merchantData: CreateMerchantDto,
|
||||||
|
dcbPartnerData: {
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
}
|
||||||
|
): Observable<MerchantSyncResult> {
|
||||||
|
console.log('📝 CREATE Merchant...');
|
||||||
|
|
||||||
|
return this.merchantConfigService.createMerchant(merchantData).pipe(
|
||||||
|
switchMap(merchantConfig => {
|
||||||
|
console.log('✅ Merchant créé dans Merchant Config:', merchantConfig);
|
||||||
|
|
||||||
|
const merchantConfigId = String(merchantConfig.id);
|
||||||
|
|
||||||
|
const dcbPartnerDto: CreateUserDto = {
|
||||||
|
username: dcbPartnerData.username,
|
||||||
|
email: dcbPartnerData.email,
|
||||||
|
firstName: dcbPartnerData.firstName,
|
||||||
|
lastName: dcbPartnerData.lastName,
|
||||||
|
password: dcbPartnerData.password,
|
||||||
|
userType: UserType.HUB,
|
||||||
|
role: UserRole.DCB_PARTNER,
|
||||||
|
enabled: true,
|
||||||
|
emailVerified: true,
|
||||||
|
merchantConfigId: merchantConfigId
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.hubUsersService.createHubUser(dcbPartnerDto).pipe(
|
||||||
|
map(keycloakMerchant => ({
|
||||||
|
merchantConfig,
|
||||||
|
keycloakMerchant: {
|
||||||
|
...keycloakMerchant,
|
||||||
|
merchantConfigId: merchantConfigId // <<<<< ✔ string
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
catchError(keycloakError => {
|
||||||
|
console.error('❌ Échec Keycloak, rollback...');
|
||||||
|
return this.rollbackMerchantCreation(String(merchantConfig.id!), keycloakError);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('❌ Échec création merchant:', error);
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CREATE - Créer un utilisateur merchant
|
||||||
|
*/
|
||||||
|
createMerchantUser(
|
||||||
|
merchantConfigId: string,
|
||||||
|
keycloakMerchantId: string,
|
||||||
|
userData: {
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
password: string;
|
||||||
|
firstName: string;
|
||||||
|
lastName: string;
|
||||||
|
role: UserRole; // Rôle Keycloak
|
||||||
|
}
|
||||||
|
): Observable<MerchantUserSyncResult> {
|
||||||
|
console.log('📝 CREATE Merchant User...');
|
||||||
|
|
||||||
|
// Validation rôle Keycloak
|
||||||
|
if (!this.KEYCLOAK_MERCHANT_ROLES.includes(userData.role)) {
|
||||||
|
return throwError(() => new Error('Rôle Keycloak invalide'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Créer dans Keycloak
|
||||||
|
const keycloakUserDto: CreateUserDto = {
|
||||||
|
...userData,
|
||||||
|
userType: UserType.MERCHANT_PARTNER,
|
||||||
|
merchantPartnerId: keycloakMerchantId,
|
||||||
|
merchantConfigId: merchantConfigId,
|
||||||
|
enabled: true,
|
||||||
|
emailVerified: true
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.merchantUsersService.createMerchantUser(keycloakUserDto).pipe(
|
||||||
|
switchMap(keycloakUser => {
|
||||||
|
console.log('✅ Utilisateur créé dans Keycloak:', keycloakUser);
|
||||||
|
|
||||||
|
// 2. Convertir rôle et ajouter à Merchant Config
|
||||||
|
const merchantConfigRole = this.mapToMerchantConfigRole(userData.role);
|
||||||
|
const merchantConfigUserId = keycloakUser.id;
|
||||||
|
|
||||||
|
const merchantConfigUserDto: AddUserToMerchantDto = {
|
||||||
|
userId: keycloakUser.id,
|
||||||
|
role: merchantConfigRole,
|
||||||
|
merchantPartnerId: Number(merchantConfigId),
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.merchantConfigService.addUserToMerchant(merchantConfigUserDto).pipe(
|
||||||
|
map(merchantConfigUser => {
|
||||||
|
console.log('✅ Utilisateur ajouté à Merchant Config:', merchantConfigUser);
|
||||||
|
|
||||||
|
return {
|
||||||
|
keycloakUser: {
|
||||||
|
...keycloakUser,
|
||||||
|
merchantConfigUserId,
|
||||||
|
merchantConfigId
|
||||||
|
},
|
||||||
|
merchantConfigUser
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
catchError(merchantConfigError => {
|
||||||
|
console.error('❌ Échec Merchant Config, rollback Keycloak...');
|
||||||
|
return this.rollbackUserCreation(keycloakUser.id, merchantConfigError);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('❌ Échec création utilisateur:', error);
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== READ ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* READ - Récupérer un merchant par ID
|
||||||
|
*/
|
||||||
|
getMerchant(
|
||||||
|
merchantConfigId: string,
|
||||||
|
keycloakMerchantId: string
|
||||||
|
): Observable<MerchantSyncResult> {
|
||||||
|
console.log('🔍 READ Merchant...');
|
||||||
|
|
||||||
|
return forkJoin({
|
||||||
|
merchantConfig: this.merchantConfigService.getMerchantById(Number(merchantConfigId)),
|
||||||
|
keycloakMerchant: this.hubUsersService.getHubUserById(keycloakMerchantId)
|
||||||
|
}).pipe(
|
||||||
|
map(({ merchantConfig, keycloakMerchant }) => {
|
||||||
|
|
||||||
|
// Vérifier la cohérence des IDs
|
||||||
|
if (String(merchantConfig.id) !== keycloakMerchant.merchantConfigId) {
|
||||||
|
console.warn('⚠️ Incohérence: merchantConfigId ne correspond pas');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
merchantConfig,
|
||||||
|
keycloakMerchant: {
|
||||||
|
...keycloakMerchant,
|
||||||
|
merchantConfigId: String(merchantConfig.id)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('❌ Erreur récupération merchant:', error);
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* READ - Récupérer tous les merchants
|
||||||
|
*/
|
||||||
|
getAllMerchants(): Observable<MerchantSyncResult[]> {
|
||||||
|
console.log('🔍 READ All Merchants...');
|
||||||
|
|
||||||
|
return forkJoin({
|
||||||
|
merchants: this.merchantConfigService.getAllMerchants(),
|
||||||
|
dcbPartners: this.hubUsersService.getAllDcbPartners()
|
||||||
|
}).pipe(
|
||||||
|
map(({ merchants, dcbPartners }) => {
|
||||||
|
return merchants
|
||||||
|
.map(merchant => {
|
||||||
|
const dcbPartner = dcbPartners.users.find(
|
||||||
|
partner => partner.merchantConfigId === merchant.id
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!dcbPartner) {
|
||||||
|
console.warn(`⚠️ Merchant ${merchant.id} n'a pas de DCB_PARTNER correspondant`);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
merchantConfig: merchant,
|
||||||
|
keycloakMerchant: dcbPartner
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((merchant): merchant is MerchantSyncResult => merchant !== null);
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('❌ Erreur récupération merchants:', error);
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* READ - Récupérer tous les utilisateurs d'un merchant
|
||||||
|
*/
|
||||||
|
getMerchantUsers(
|
||||||
|
merchantConfigId: string,
|
||||||
|
keycloakMerchantId: string
|
||||||
|
): Observable<{
|
||||||
|
merchantConfig: Merchant;
|
||||||
|
keycloakMerchant: User;
|
||||||
|
users: Array<{
|
||||||
|
keycloakUser: User;
|
||||||
|
merchantConfigUser?: MerchantUser;
|
||||||
|
synced: boolean;
|
||||||
|
}>;
|
||||||
|
}> {
|
||||||
|
return forkJoin({
|
||||||
|
merchantConfig: this.merchantConfigService.getMerchantById(Number(merchantConfigId)),
|
||||||
|
keycloakMerchant: this.hubUsersService.getHubUserById(keycloakMerchantId),
|
||||||
|
merchantConfigUsers: this.merchantConfigService.getMerchantUsers(Number(merchantConfigId)),
|
||||||
|
keycloakUsers: this.merchantUsersService.getMerchantUsersByPartner(keycloakMerchantId)
|
||||||
|
}).pipe(
|
||||||
|
map(({ merchantConfig, keycloakMerchant, merchantConfigUsers, keycloakUsers }) => {
|
||||||
|
|
||||||
|
const users = keycloakUsers.map(keycloakUser => {
|
||||||
|
const merchantConfigUser = merchantConfigUsers.find(
|
||||||
|
mcUser => mcUser.email === keycloakUser.email
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
keycloakUser,
|
||||||
|
merchantConfigUser,
|
||||||
|
synced: !!merchantConfigUser
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
merchantConfig,
|
||||||
|
keycloakMerchant: {
|
||||||
|
...keycloakMerchant,
|
||||||
|
merchantConfigId: String(merchantConfig.id)
|
||||||
|
},
|
||||||
|
users
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== UPDATE ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UPDATE - Mettre à jour un merchant
|
||||||
|
*/
|
||||||
|
updateMerchant(
|
||||||
|
merchantConfigId: string,
|
||||||
|
keycloakMerchantId: string,
|
||||||
|
updates: {
|
||||||
|
merchantConfig?: UpdateMerchantDto;
|
||||||
|
dcbPartner?: UpdateUserDto;
|
||||||
|
}
|
||||||
|
): Observable<MerchantSyncResult> {
|
||||||
|
console.log('✏️ UPDATE Merchant...');
|
||||||
|
|
||||||
|
const operations: Observable<any>[] = [];
|
||||||
|
|
||||||
|
// Mise à jour MerchantConfig
|
||||||
|
if (updates.merchantConfig) {
|
||||||
|
operations.push(
|
||||||
|
this.merchantConfigService.updateMerchant(
|
||||||
|
Number(merchantConfigId),
|
||||||
|
updates.merchantConfig
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mise à jour du user DCB_PARTNER
|
||||||
|
if (updates.dcbPartner && keycloakMerchantId) {
|
||||||
|
operations.push(
|
||||||
|
this.hubUsersService.updateHubUser(keycloakMerchantId, updates.dcbPartner)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operations.length === 0) {
|
||||||
|
return throwError(() => new Error('Aucune donnée à mettre à jour'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return forkJoin(operations).pipe(
|
||||||
|
switchMap(() => {
|
||||||
|
// Recharger les données mises à jour
|
||||||
|
return this.getMerchant(
|
||||||
|
merchantConfigId,
|
||||||
|
keycloakMerchantId
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('❌ Erreur mise à jour merchant:', error);
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UPDATE - Mettre à jour un utilisateur merchant
|
||||||
|
*/
|
||||||
|
updateMerchantUser(
|
||||||
|
keycloakUserId: string,
|
||||||
|
merchantConfigUserId: number,
|
||||||
|
merchantConfigId: string,
|
||||||
|
updates: {
|
||||||
|
keycloak?: UpdateUserDto;
|
||||||
|
merchantConfig?: {
|
||||||
|
role?: UserRole;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
): Observable<{
|
||||||
|
keycloakUser?: User;
|
||||||
|
merchantConfigUser?: MerchantUser;
|
||||||
|
}> {
|
||||||
|
console.log('✏️ UPDATE Merchant User...');
|
||||||
|
|
||||||
|
const operations: Observable<any>[] = [];
|
||||||
|
|
||||||
|
// Mise à jour Keycloak
|
||||||
|
if (updates.keycloak) {
|
||||||
|
operations.push(
|
||||||
|
this.merchantUsersService.updateMerchantUser(keycloakUserId, updates.keycloak)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mise à jour Merchant Config
|
||||||
|
if (updates.merchantConfig?.role) {
|
||||||
|
if (!this.MERCHANT_CONFIG_ROLES.includes(updates.merchantConfig.role)) {
|
||||||
|
return throwError(() => new Error('Rôle Merchant Config invalide'));
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateRoleDto: UpdateUserRoleDto = {
|
||||||
|
role: updates.merchantConfig.role
|
||||||
|
};
|
||||||
|
|
||||||
|
operations.push(
|
||||||
|
this.merchantConfigService.updateUserRole(
|
||||||
|
Number(merchantConfigId),
|
||||||
|
merchantConfigUserId,
|
||||||
|
updateRoleDto
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operations.length === 0) {
|
||||||
|
return throwError(() => new Error('Aucune donnée à mettre à jour'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return forkJoin(operations).pipe(
|
||||||
|
map(([keycloakUpdate, merchantConfigUpdate]) => ({
|
||||||
|
keycloakUser: keycloakUpdate,
|
||||||
|
merchantConfigUser: merchantConfigUpdate
|
||||||
|
})),
|
||||||
|
catchError(error => {
|
||||||
|
console.error('❌ Erreur mise à jour utilisateur:', error);
|
||||||
|
return throwError(() => error);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* UPDATE - Activer/désactiver un utilisateur
|
||||||
|
*/
|
||||||
|
toggleUserStatus(
|
||||||
|
keycloakUserId: string,
|
||||||
|
enabled: boolean
|
||||||
|
): Observable<User> {
|
||||||
|
console.log(`🔄 ${enabled ? 'Activer' : 'Désactiver'} utilisateur...`);
|
||||||
|
|
||||||
|
return this.merchantUsersService.updateMerchantUser(keycloakUserId, { enabled });
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== DELETE ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE - Supprimer un merchant
|
||||||
|
*/
|
||||||
|
deleteMerchant(
|
||||||
|
merchantConfigId: string,
|
||||||
|
keycloakMerchantId: string
|
||||||
|
): Observable<{ success: boolean; message: string }> {
|
||||||
|
console.log('🗑️ DELETE Merchant...');
|
||||||
|
|
||||||
|
// 1. D'abord supprimer tous les utilisateurs
|
||||||
|
return this.merchantConfigService.getMerchantUsers(Number(merchantConfigId)).pipe(
|
||||||
|
switchMap(merchantConfigUsers => {
|
||||||
|
const userDeletions: Observable<any>[] = [];
|
||||||
|
|
||||||
|
// Supprimer chaque utilisateur
|
||||||
|
merchantConfigUsers.forEach(mcUser => {
|
||||||
|
if (mcUser.email) {
|
||||||
|
userDeletions.push(
|
||||||
|
this.merchantUsersService.searchMerchantUsers({ query: mcUser.email }).pipe(
|
||||||
|
switchMap(users => {
|
||||||
|
if (users.length > 0) {
|
||||||
|
return this.deleteMerchantUser(
|
||||||
|
users[0].id,
|
||||||
|
mcUser.userId,
|
||||||
|
merchantConfigId
|
||||||
|
).pipe(catchError(() => of(null)));
|
||||||
|
}
|
||||||
|
return of(null);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Supprimer le merchant
|
||||||
|
userDeletions.push(
|
||||||
|
this.merchantConfigService.deleteMerchant(Number(merchantConfigId))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Supprimer le DCB_PARTNER
|
||||||
|
userDeletions.push(
|
||||||
|
this.hubUsersService.deleteHubUser(keycloakMerchantId).pipe(
|
||||||
|
catchError(() => of({ ignored: true }))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return forkJoin(userDeletions).pipe(
|
||||||
|
map(() => ({
|
||||||
|
success: true,
|
||||||
|
message: 'Merchant et utilisateurs supprimés avec succès'
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
catchError(async (error) => ({
|
||||||
|
success: false,
|
||||||
|
message: `Erreur suppression: ${error.message}`
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE - Supprimer un utilisateur merchant
|
||||||
|
*/
|
||||||
|
deleteMerchantUser(
|
||||||
|
keycloakUserId: string,
|
||||||
|
merchantConfigUserId: number,
|
||||||
|
merchantConfigId: string,
|
||||||
|
): Observable<{ success: boolean; message: string }> {
|
||||||
|
console.log('🗑️ DELETE Merchant User...');
|
||||||
|
|
||||||
|
return forkJoin({
|
||||||
|
keycloakDeletion: this.merchantUsersService.deleteMerchantUser(keycloakUserId),
|
||||||
|
merchantConfigDeletion: this.merchantConfigService.removeUserFromMerchant(
|
||||||
|
Number(merchantConfigId),
|
||||||
|
merchantConfigUserId
|
||||||
|
).pipe(catchError(() => of({ ignored: true })))
|
||||||
|
}).pipe(
|
||||||
|
map(() => ({
|
||||||
|
success: true,
|
||||||
|
message: 'Utilisateur supprimé des deux systèmes'
|
||||||
|
})),
|
||||||
|
catchError(async (error) => ({
|
||||||
|
success: false,
|
||||||
|
message: `Erreur suppression: ${error.message}`
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== UTILITAIRES ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifier le statut de synchronisation
|
||||||
|
*/
|
||||||
|
checkSyncStatus(
|
||||||
|
merchantConfigId: string,
|
||||||
|
keycloakMerchantId: string
|
||||||
|
): Observable<MerchantSyncStatus> {
|
||||||
|
return forkJoin({
|
||||||
|
merchantConfig: this.merchantConfigService.getMerchantById(Number(merchantConfigId)).pipe(
|
||||||
|
catchError(() => of(null))
|
||||||
|
),
|
||||||
|
keycloakMerchant: this.hubUsersService.getHubUserById(keycloakMerchantId).pipe(
|
||||||
|
catchError(() => of(null))
|
||||||
|
),
|
||||||
|
merchantConfigUsers: this.merchantConfigService.getMerchantUsers(Number(merchantConfigId)).pipe(
|
||||||
|
catchError(() => of([]))
|
||||||
|
),
|
||||||
|
keycloakUsers: this.merchantUsersService.getMerchantUsersByPartner(keycloakMerchantId).pipe(
|
||||||
|
catchError(() => of([]))
|
||||||
|
)
|
||||||
|
}).pipe(
|
||||||
|
map(({ merchantConfig, keycloakMerchant, merchantConfigUsers, keycloakUsers }) => {
|
||||||
|
|
||||||
|
const syncedUsers = keycloakUsers.filter(kcUser =>
|
||||||
|
merchantConfigUsers.some(mcUser => mcUser.email === kcUser.email)
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
existsInKeycloak: !!keycloakMerchant,
|
||||||
|
existsInMerchantConfig: !!merchantConfig,
|
||||||
|
usersSynced: syncedUsers.length === keycloakUsers.length &&
|
||||||
|
syncedUsers.length === merchantConfigUsers.length,
|
||||||
|
syncedUserCount: syncedUsers.length,
|
||||||
|
totalUserCount: Math.max(keycloakUsers.length, merchantConfigUsers.length)
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rechercher des merchants
|
||||||
|
*/
|
||||||
|
searchMerchants(query: string): Observable<MerchantSyncResult[]> {
|
||||||
|
return this.merchantConfigService.getAllMerchants({ query }).pipe(
|
||||||
|
switchMap(merchants => {
|
||||||
|
if (merchants.length === 0) return of([]);
|
||||||
|
|
||||||
|
return this.hubUsersService.getAllDcbPartners().pipe(
|
||||||
|
map(dcbPartners => {
|
||||||
|
return merchants
|
||||||
|
.map(merchant => {
|
||||||
|
const dcbPartner = dcbPartners.users.find(
|
||||||
|
partner => partner.merchantConfigId === merchant.id
|
||||||
|
);
|
||||||
|
|
||||||
|
return dcbPartner ? {
|
||||||
|
merchantConfig: merchant,
|
||||||
|
keycloakMerchant: dcbPartner
|
||||||
|
} : null;
|
||||||
|
})
|
||||||
|
.filter((merchant): merchant is MerchantSyncResult => merchant !== null);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Réinitialiser le mot de passe d'un utilisateur
|
||||||
|
*/
|
||||||
|
resetUserPassword(
|
||||||
|
keycloakUserId: string,
|
||||||
|
newPassword: string,
|
||||||
|
temporary: boolean = true
|
||||||
|
): Observable<{ success: boolean; message: string }> {
|
||||||
|
return this.merchantUsersService.resetMerchantUserPassword(
|
||||||
|
keycloakUserId,
|
||||||
|
{ newPassword, temporary }
|
||||||
|
).pipe(
|
||||||
|
map(() => ({
|
||||||
|
success: true,
|
||||||
|
message: 'Mot de passe réinitialisé avec succès'
|
||||||
|
})),
|
||||||
|
catchError(async (error) => ({
|
||||||
|
success: false,
|
||||||
|
message: `Erreur réinitialisation: ${error.message}`
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== MÉTHODES PRIVÉES ====================
|
||||||
|
|
||||||
|
private mapToMerchantConfigRole(keycloakRole: UserRole): UserRole {
|
||||||
|
const mappedRole = this.ROLE_MAPPING.get(keycloakRole);
|
||||||
|
return mappedRole || UserRole.MERCHANT_CONFIG_VIEWER;
|
||||||
|
}
|
||||||
|
|
||||||
|
private mapToKeycloakRole(merchantConfigRole: UserRole): UserRole {
|
||||||
|
// Recherche inverse
|
||||||
|
for (const [key, value] of this.ROLE_MAPPING.entries()) {
|
||||||
|
if (value === merchantConfigRole) {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UserRole.DCB_PARTNER_SUPPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateMerchantConfigId(keycloakId: string): number {
|
||||||
|
let hash = 0;
|
||||||
|
for (let i = 0; i < keycloakId.length; i++) {
|
||||||
|
hash = ((hash << 5) - hash) + keycloakId.charCodeAt(i);
|
||||||
|
hash = hash & hash;
|
||||||
|
}
|
||||||
|
return Math.abs(hash) % 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
private rollbackMerchantCreation(
|
||||||
|
merchantConfigId: string,
|
||||||
|
originalError: any
|
||||||
|
): Observable<never> {
|
||||||
|
return this.merchantConfigService.deleteMerchant(Number(merchantConfigId)).pipe(
|
||||||
|
switchMap(() => throwError(() => new Error(
|
||||||
|
`Échec création DCB_PARTNER: ${originalError.message}. Rollback effectué.`
|
||||||
|
)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private rollbackUserCreation(
|
||||||
|
keycloakUserId: string,
|
||||||
|
originalError: any
|
||||||
|
): Observable<never> {
|
||||||
|
return this.merchantUsersService.deleteMerchantUser(keycloakUserId).pipe(
|
||||||
|
switchMap(() => throwError(() => new Error(
|
||||||
|
`Échec création Merchant Config: ${originalError.message}. Rollback effectué.`
|
||||||
|
)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -88,7 +88,6 @@ export class MerchantUsersService {
|
|||||||
return throwError(() => 'Password must be at least 8 characters');
|
return throwError(() => 'Password must be at least 8 characters');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adapter le payload pour le nouveau contrôleur
|
|
||||||
const payload = {
|
const payload = {
|
||||||
username: createUserDto.username.trim(),
|
username: createUserDto.username.trim(),
|
||||||
email: createUserDto.email.trim(),
|
email: createUserDto.email.trim(),
|
||||||
@ -99,6 +98,7 @@ export class MerchantUsersService {
|
|||||||
enabled: createUserDto.enabled !== undefined ? createUserDto.enabled : true,
|
enabled: createUserDto.enabled !== undefined ? createUserDto.enabled : true,
|
||||||
emailVerified: createUserDto.emailVerified !== undefined ? createUserDto.emailVerified : true,
|
emailVerified: createUserDto.emailVerified !== undefined ? createUserDto.emailVerified : true,
|
||||||
merchantPartnerId: createUserDto.merchantPartnerId?.trim() || null,
|
merchantPartnerId: createUserDto.merchantPartnerId?.trim() || null,
|
||||||
|
//merchantConfigId: createUserDto.merchantConfigId?.trim() || null,
|
||||||
userType: createUserDto.userType.trim()
|
userType: createUserDto.userType.trim()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -291,7 +291,8 @@ export class MerchantUsersService {
|
|||||||
emailVerified: apiUser.emailVerified,
|
emailVerified: apiUser.emailVerified,
|
||||||
userType: userType,
|
userType: userType,
|
||||||
merchantPartnerId: apiUser.merchantPartnerId,
|
merchantPartnerId: apiUser.merchantPartnerId,
|
||||||
role: apiUser.role, // Convertir le rôle unique en tableau
|
merchantConfigId: apiUser.merchantConfigId,
|
||||||
|
role: apiUser.role,
|
||||||
createdBy: apiUser.createdBy,
|
createdBy: apiUser.createdBy,
|
||||||
createdByUsername: apiUser.createdByUsername,
|
createdByUsername: apiUser.createdByUsername,
|
||||||
createdTimestamp: apiUser.createdTimestamp,
|
createdTimestamp: apiUser.createdTimestamp,
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import {
|
|||||||
UserType
|
UserType
|
||||||
} from '@core/models/dcb-bo-hub-user.model';
|
} from '@core/models/dcb-bo-hub-user.model';
|
||||||
import { HubUsersService } from './hub-users.service';
|
import { HubUsersService } from './hub-users.service';
|
||||||
|
import { MerchantSyncCrudService } from './merchant-sync-orchestrator.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-merchant-users',
|
selector: 'app-merchant-users',
|
||||||
@ -132,8 +133,12 @@ export class MerchantUsersManagement implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// Initialiser le formulaire selon le contexte
|
// Initialiser le formulaire selon le contexte
|
||||||
this.initializeMerchantPartner();
|
this.initializeMerchantPartner();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
// Initialiser le merchant partner selon le contexte
|
// Initialiser le merchant partner selon le contexte
|
||||||
initializeMerchantPartner() {
|
initializeMerchantPartner() {
|
||||||
if (this.currentUserRole === UserRole.DCB_PARTNER && this.currentMerchantPartnerId) {
|
if (this.currentUserRole === UserRole.DCB_PARTNER && this.currentMerchantPartnerId) {
|
||||||
|
|||||||
@ -47,7 +47,7 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
@Input() canDeleteMerchants: boolean = false;
|
@Input() canDeleteMerchants: boolean = false;
|
||||||
|
|
||||||
// Outputs
|
// Outputs
|
||||||
@Output() merchantSelected = new EventEmitter<string>();
|
@Output() merchantSelected = new EventEmitter<number>();
|
||||||
@Output() openCreateMerchantModal = new EventEmitter<void>();
|
@Output() openCreateMerchantModal = new EventEmitter<void>();
|
||||||
@Output() editMerchantRequested = new EventEmitter<Merchant>();
|
@Output() editMerchantRequested = new EventEmitter<Merchant>();
|
||||||
@Output() deleteMerchantRequested = new EventEmitter<Merchant>();
|
@Output() deleteMerchantRequested = new EventEmitter<Merchant>();
|
||||||
|
|||||||
@ -184,9 +184,9 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
readonly Operator = Operator;
|
readonly Operator = Operator;
|
||||||
readonly MerchantUtils = MerchantUtils;
|
readonly MerchantUtils = MerchantUtils;
|
||||||
|
|
||||||
@Input() merchantId!: string;
|
@Input() merchantId!: number;
|
||||||
@Output() back = new EventEmitter<void>();
|
@Output() back = new EventEmitter<void>();
|
||||||
@Output() editConfigRequested = new EventEmitter<string>();
|
@Output() editConfigRequested = new EventEmitter<number>();
|
||||||
@Output() editMerchantRequested = new EventEmitter<Merchant>();
|
@Output() editMerchantRequested = new EventEmitter<Merchant>();
|
||||||
|
|
||||||
merchant: Merchant | null = null;
|
merchant: Merchant | null = null;
|
||||||
@ -205,16 +205,18 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
currentMerchantPartnerId: string = '';
|
currentMerchantPartnerId: string = '';
|
||||||
|
|
||||||
// Édition des configurations
|
// Édition des configurations
|
||||||
editingConfigId: string | null = null;
|
editingConfigId: number | null = null;
|
||||||
editedConfig: UpdateMerchantConfigDto = {};
|
editedConfig: UpdateMerchantConfigDto = {};
|
||||||
configToDelete: MerchantConfig | null = null;
|
configToDelete: MerchantConfig | null = null;
|
||||||
private deleteModalRef: any = null;
|
private deleteModalRef: any = null;
|
||||||
|
|
||||||
// Affichage des valeurs sensibles
|
// Affichage des valeurs sensibles
|
||||||
showSensitiveValues: { [configId: string]: boolean } = {};
|
showSensitiveValues: { [configId: number]: boolean } = {};
|
||||||
|
|
||||||
// Expansion des sections
|
// Expansion des sections
|
||||||
expandedSections: { [sectionId: string]: boolean } = {};
|
expandedSections: { [sectionId: number]: boolean } = {};
|
||||||
|
|
||||||
|
expandedSection: string | null = null;
|
||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
page = 1;
|
page = 1;
|
||||||
@ -318,12 +320,12 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// ==================== GESTION DE L'EXPANSION ====================
|
// ==================== GESTION DE L'EXPANSION ====================
|
||||||
|
|
||||||
toggleSection(sectionId: string) {
|
toggleSection(section: string) {
|
||||||
this.expandedSections[sectionId] = !this.expandedSections[sectionId];
|
this.expandedSection = this.expandedSection === section ? null : section;
|
||||||
}
|
}
|
||||||
|
|
||||||
isSectionExpanded(sectionId: string): boolean {
|
isSectionExpanded(section: string): boolean {
|
||||||
return !!this.expandedSections[sectionId];
|
return this.expandedSection === section;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== ÉDITION DES CONFIGURATIONS ====================
|
// ==================== ÉDITION DES CONFIGURATIONS ====================
|
||||||
@ -408,11 +410,11 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
newConfig: Omit<MerchantConfig, 'id' | 'merchantPartnerId'> = {
|
newConfig: Omit<MerchantConfig, 'id' | 'merchantPartnerId'> = {
|
||||||
name: ConfigType.CUSTOM,
|
name: ConfigType.CUSTOM,
|
||||||
value: '',
|
value: '',
|
||||||
operatorId: null
|
operatorId: 1
|
||||||
};
|
};
|
||||||
|
|
||||||
// États pour la modification de rôle
|
// États pour la modification de rôle
|
||||||
editingUserId: string | null = null;
|
editingUserId: number | null = null;
|
||||||
newUserRole: UserRole | null = null;
|
newUserRole: UserRole | null = null;
|
||||||
|
|
||||||
// ==================== GESTION DES CONFIGURATIONS ====================
|
// ==================== GESTION DES CONFIGURATIONS ====================
|
||||||
@ -422,7 +424,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
this.newConfig = {
|
this.newConfig = {
|
||||||
name: ConfigType.CUSTOM,
|
name: ConfigType.CUSTOM,
|
||||||
value: '',
|
value: '',
|
||||||
operatorId: null
|
operatorId: 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +433,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
this.newConfig = {
|
this.newConfig = {
|
||||||
name: ConfigType.CUSTOM,
|
name: ConfigType.CUSTOM,
|
||||||
value: '',
|
value: '',
|
||||||
operatorId: null
|
operatorId: 1
|
||||||
};
|
};
|
||||||
this.error = '';
|
this.error = '';
|
||||||
}
|
}
|
||||||
@ -459,7 +461,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
this.newConfig = {
|
this.newConfig = {
|
||||||
name: ConfigType.CUSTOM,
|
name: ConfigType.CUSTOM,
|
||||||
value: '',
|
value: '',
|
||||||
operatorId: null
|
operatorId: 1
|
||||||
};
|
};
|
||||||
this.clearCache();
|
this.clearCache();
|
||||||
this.cdRef.detectChanges();
|
this.cdRef.detectChanges();
|
||||||
@ -504,7 +506,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
return this.isSensitiveConfig({
|
return this.isSensitiveConfig({
|
||||||
name: this.newConfig.name,
|
name: this.newConfig.name,
|
||||||
value: '',
|
value: '',
|
||||||
operatorId: null
|
operatorId: 1
|
||||||
} as MerchantConfig);
|
} as MerchantConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,7 +663,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isEditingUserRole(userId: string): boolean {
|
isEditingUserRole(userId: number): boolean {
|
||||||
return this.editingUserId === userId;
|
return this.editingUserId === userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,7 +725,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
return this.canManageAllMerchants();
|
return this.canManageAllMerchants();
|
||||||
}
|
}
|
||||||
|
|
||||||
isEditingConfig(configId: string): boolean {
|
isEditingConfig(configId: number): boolean {
|
||||||
return this.editingConfigId === configId;
|
return this.editingConfigId === configId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -734,7 +736,9 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
if (this.canManageAllMerchants()) return true;
|
if (this.canManageAllMerchants()) return true;
|
||||||
|
|
||||||
return config.merchantPartnerId === this.currentMerchantPartnerId;
|
return true;
|
||||||
|
|
||||||
|
//return config.merchantPartnerId === this.currentMerchantPartnerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
canManageAllMerchants(): boolean {
|
canManageAllMerchants(): boolean {
|
||||||
@ -747,12 +751,14 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
if (this.canManageAllMerchants()) return true;
|
if (this.canManageAllMerchants()) return true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
// PARTNER ne peut modifier que ses propres marchands
|
// PARTNER ne peut modifier que ses propres marchands
|
||||||
return this.merchant.configs?.some(config =>
|
// return this.merchant.configs?.some(config =>
|
||||||
config.merchantPartnerId === this.currentMerchantPartnerId
|
// config.merchantPartnerId === this.currentMerchantPartnerId
|
||||||
) || this.merchant.users?.some(user =>
|
// ) || this.merchant.users?.some(user =>
|
||||||
user.merchantPartnerId === this.currentMerchantPartnerId
|
// user.merchantPartnerId === this.currentMerchantPartnerId
|
||||||
);
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== UTILITAIRES D'AFFICHAGE ====================
|
// ==================== UTILITAIRES D'AFFICHAGE ====================
|
||||||
@ -781,7 +787,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
return this.isSensitiveConfig(config) || config.value.length > 100;
|
return this.isSensitiveConfig(config) || config.value.length > 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleSensitiveValue(configId: string) {
|
toggleSensitiveValue(configId: number) {
|
||||||
this.showSensitiveValues[configId] = !this.showSensitiveValues[configId];
|
this.showSensitiveValues[configId] = !this.showSensitiveValues[configId];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -965,7 +971,7 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
this.back.emit();
|
this.back.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
requestEditConfig(configId: string) {
|
requestEditConfig(configId: number) {
|
||||||
this.editConfigRequested.emit(configId);
|
this.editConfigRequested.emit(configId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1028,15 +1034,15 @@ export class MerchantConfigView implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// ==================== TRACKBY FUNCTIONS ====================
|
// ==================== TRACKBY FUNCTIONS ====================
|
||||||
|
|
||||||
trackByConfigId(index: number, config: MerchantConfig): string {
|
trackByConfigId(index: number, config: MerchantConfig): number {
|
||||||
return config.id || `config-${index}`;
|
return config.id || index;
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByContactId(index: number, contact: TechnicalContact): string {
|
trackByContactId(index: number, contact: TechnicalContact): number {
|
||||||
return contact.id || `contact-${index}`;
|
return contact.id || index;
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByUserId(index: number, user: MerchantUser): string {
|
trackByUserId(index: number, user: MerchantUser): number {
|
||||||
return user.userId || `user-${index}`;
|
return user.userId || index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -114,7 +114,7 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMerchantById(id: string): Observable<Merchant> {
|
getMerchantById(id: number): Observable<Merchant> {
|
||||||
const numericId = this.convertIdToNumber(id);
|
const numericId = this.convertIdToNumber(id);
|
||||||
|
|
||||||
console.log(`📥 Loading merchant ${id}`);
|
console.log(`📥 Loading merchant ${id}`);
|
||||||
@ -130,14 +130,14 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateMerchant(id: string, updateMerchantDto: UpdateMerchantDto): Observable<Merchant> {
|
updateMerchant(id: number, updateMerchantDto: UpdateMerchantDto): Observable<Merchant> {
|
||||||
const numericId = this.convertIdToNumber(id);
|
//const numericId = this.convertIdToNumber(id);
|
||||||
|
|
||||||
const apiDto = this.dataAdapter.convertUpdateMerchantToApi(updateMerchantDto);
|
const apiDto = this.dataAdapter.convertUpdateMerchantToApi(updateMerchantDto);
|
||||||
|
|
||||||
console.log(`📤 Updating merchant ${id}:`, apiDto);
|
console.log(`📤 Updating merchant ${id}:`, apiDto);
|
||||||
|
|
||||||
return this.http.patch<ApiMerchant>(`${this.baseApiUrl}/${numericId}`, apiDto).pipe(
|
return this.http.patch<ApiMerchant>(`${this.baseApiUrl}/${id}`, apiDto).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(apiMerchant => {
|
map(apiMerchant => {
|
||||||
console.log(`✅ Merchant ${id} updated successfully`);
|
console.log(`✅ Merchant ${id} updated successfully`);
|
||||||
@ -147,7 +147,7 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteMerchant(id: string): Observable<void> {
|
deleteMerchant(id: number): Observable<void> {
|
||||||
const numericId = this.convertIdToNumber(id);
|
const numericId = this.convertIdToNumber(id);
|
||||||
|
|
||||||
console.log(`🗑️ Deleting merchant ${id}`);
|
console.log(`🗑️ Deleting merchant ${id}`);
|
||||||
@ -164,30 +164,24 @@ export class MerchantConfigService {
|
|||||||
// ==================== USER MANAGEMENT ====================
|
// ==================== USER MANAGEMENT ====================
|
||||||
|
|
||||||
addUserToMerchant(addUserDto: AddUserToMerchantDto): Observable<MerchantUser> {
|
addUserToMerchant(addUserDto: AddUserToMerchantDto): Observable<MerchantUser> {
|
||||||
// CONVERSION AVEC L'ADAPTER
|
|
||||||
const apiDto = {
|
console.log('📤 Adding user to merchant:', addUserDto);
|
||||||
...addUserDto,
|
|
||||||
merchantPartnerId: addUserDto.merchantPartnerId ?
|
|
||||||
this.convertIdToNumber(addUserDto.merchantPartnerId) : undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('📤 Adding user to merchant:', apiDto);
|
return this.http.post<ApiMerchantUser>(`${this.baseApiUrl}/users`, addUserDto).pipe(
|
||||||
|
|
||||||
return this.http.post<ApiMerchantUser>(`${this.baseApiUrl}/users`, apiDto).pipe(
|
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(apiUser => {
|
map(apiUser => {
|
||||||
console.log('✅ User added to merchant successfully');
|
console.log('✅ User added to merchant successfully');
|
||||||
// ✅ UTILISATION DE L'ADAPTER
|
|
||||||
return this.dataAdapter.convertApiUserToFrontend(apiUser);
|
return this.dataAdapter.convertApiUserToFrontend(apiUser);
|
||||||
}),
|
}),
|
||||||
catchError(error => this.handleError('addUserToMerchant', error))
|
catchError(error => this.handleError('addUserToMerchant', error))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMerchantUsers(merchantId: string): Observable<MerchantUser[]> {
|
getMerchantUsers(merchantId: number): Observable<MerchantUser[]> {
|
||||||
const numericMerchantId = this.convertIdToNumber(merchantId);
|
//const merchantId = this.convertIdToNumber(merchantId);
|
||||||
|
|
||||||
return this.http.get<ApiMerchant>(`${this.baseApiUrl}/${numericMerchantId}`).pipe(
|
return this.http.get<ApiMerchant>(`${this.baseApiUrl}/${merchantId}`).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(apiMerchant => {
|
map(apiMerchant => {
|
||||||
return (apiMerchant.users || []).map(user =>
|
return (apiMerchant.users || []).map(user =>
|
||||||
@ -199,11 +193,11 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateUserRole(merchantId: string, userId: string, updateRoleDto: UpdateUserRoleDto): Observable<MerchantUser> {
|
updateUserRole(merchantId: number, userId: number, updateRoleDto: UpdateUserRoleDto): Observable<MerchantUser> {
|
||||||
const numericMerchantId = this.convertIdToNumber(merchantId);
|
//const merchantId = this.convertIdToNumber(merchantId);
|
||||||
|
|
||||||
return this.http.patch<ApiMerchantUser>(
|
return this.http.patch<ApiMerchantUser>(
|
||||||
`${this.baseApiUrl}/${numericMerchantId}/users/${userId}/role`,
|
`${this.baseApiUrl}/${merchantId}/users/${userId}/role`,
|
||||||
updateRoleDto
|
updateRoleDto
|
||||||
).pipe(
|
).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
@ -216,10 +210,10 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeUserFromMerchant(merchantId: string, userId: string): Observable<void> {
|
removeUserFromMerchant(merchantId: number, userId: number): Observable<void> {
|
||||||
const numericMerchantId = this.convertIdToNumber(merchantId);
|
//const merchantId = this.convertIdToNumber(merchantId);
|
||||||
|
|
||||||
return this.http.delete<void>(`${this.baseApiUrl}/${numericMerchantId}/users/${userId}`).pipe(
|
return this.http.delete<void>(`${this.baseApiUrl}/${merchantId}/users/${userId}`).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(() => {
|
map(() => {
|
||||||
console.log(`✅ User ${userId} removed from merchant ${merchantId} successfully`);
|
console.log(`✅ User ${userId} removed from merchant ${merchantId} successfully`);
|
||||||
@ -228,7 +222,7 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getUserMerchants(userId: string): Observable<Merchant[]> {
|
getUserMerchants(userId: number): Observable<Merchant[]> {
|
||||||
return this.http.get<ApiMerchant[]>(`${this.baseApiUrl}/user/${userId}`).pipe(
|
return this.http.get<ApiMerchant[]>(`${this.baseApiUrl}/user/${userId}`).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(apiMerchants => {
|
map(apiMerchants => {
|
||||||
@ -242,19 +236,19 @@ export class MerchantConfigService {
|
|||||||
|
|
||||||
// ==================== CONFIG MANAGEMENT ====================
|
// ==================== CONFIG MANAGEMENT ====================
|
||||||
|
|
||||||
addConfigToMerchant(merchantId: string, config: Omit<MerchantConfig, 'id' | 'merchantPartnerId'>): Observable<MerchantConfig> {
|
addConfigToMerchant(merchantId: number, config: Omit<MerchantConfig, 'id' | 'merchantPartnerId'>): Observable<MerchantConfig> {
|
||||||
const numericMerchantId = this.convertIdToNumber(merchantId);
|
//const merchantId = this.convertIdToNumber(merchantId);
|
||||||
|
|
||||||
// CONVERSION AVEC L'ADAPTER
|
// CONVERSION AVEC L'ADAPTER
|
||||||
const apiConfig: Omit<ApiMerchantConfig, 'id'> = {
|
const apiConfig: Omit<ApiMerchantConfig, 'id'> = {
|
||||||
...config,
|
...config,
|
||||||
operatorId: config.operatorId,
|
operatorId: config.operatorId,
|
||||||
merchantPartnerId: numericMerchantId
|
merchantPartnerId: merchantId
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(`📤 Adding config to merchant ${merchantId}:`, apiConfig);
|
console.log(`📤 Adding config to merchant ${merchantId}:`, apiConfig);
|
||||||
|
|
||||||
return this.http.post<ApiMerchantConfig>(`${this.baseApiUrl}/${numericMerchantId}/configs`, apiConfig).pipe(
|
return this.http.post<ApiMerchantConfig>(`${this.baseApiUrl}/${merchantId}/configs`, apiConfig).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(apiConfig => {
|
map(apiConfig => {
|
||||||
console.log('✅ Config added to merchant successfully');
|
console.log('✅ Config added to merchant successfully');
|
||||||
@ -265,14 +259,14 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateConfig(configId: string, config: Partial<MerchantConfig>): Observable<MerchantConfig> {
|
updateConfig(configId: number, config: Partial<MerchantConfig>): Observable<MerchantConfig> {
|
||||||
const numericConfigId = this.convertIdToNumber(configId);
|
//const configId = this.convertIdToNumber(configId);
|
||||||
|
|
||||||
const apiConfig = this.dataAdapter.convertConfigUpdateToApi(config);
|
const apiConfig = this.dataAdapter.convertConfigUpdateToApi(config);
|
||||||
|
|
||||||
console.log(`📤 Updating config ${configId}:`, apiConfig);
|
console.log(`📤 Updating config ${configId}:`, apiConfig);
|
||||||
|
|
||||||
return this.http.patch<ApiMerchantConfig>(`${this.baseApiUrl}/configs/${numericConfigId}`, apiConfig).pipe(
|
return this.http.patch<ApiMerchantConfig>(`${this.baseApiUrl}/configs/${configId}`, apiConfig).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(apiConfig => {
|
map(apiConfig => {
|
||||||
console.log(`✅ Config ${configId} updated successfully`);
|
console.log(`✅ Config ${configId} updated successfully`);
|
||||||
@ -283,12 +277,12 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
deleteConfig(configId: string): Observable<void> {
|
deleteConfig(configId: number): Observable<void> {
|
||||||
const numericConfigId = this.convertIdToNumber(configId);
|
//const configId = this.convertIdToNumber(configId);
|
||||||
|
|
||||||
console.log(`🗑️ Deleting config ${configId}`);
|
console.log(`🗑️ Deleting config ${configId}`);
|
||||||
|
|
||||||
return this.http.delete<void>(`${this.baseApiUrl}/configs/${numericConfigId}`).pipe(
|
return this.http.delete<void>(`${this.baseApiUrl}/configs/${configId}`).pipe(
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(() => {
|
map(() => {
|
||||||
console.log(`✅ Config ${configId} deleted successfully`);
|
console.log(`✅ Config ${configId} deleted successfully`);
|
||||||
@ -297,7 +291,7 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMerchantConfigs(merchantId: string): Observable<MerchantConfig[]> {
|
getMerchantConfigs(merchantId: number): Observable<MerchantConfig[]> {
|
||||||
return this.getMerchantById(merchantId).pipe(
|
return this.getMerchantById(merchantId).pipe(
|
||||||
map(merchant => merchant?.configs ?? [])
|
map(merchant => merchant?.configs ?? [])
|
||||||
);
|
);
|
||||||
@ -358,7 +352,7 @@ export class MerchantConfigService {
|
|||||||
return throwError(() => new Error(userMessage));
|
return throwError(() => new Error(userMessage));
|
||||||
}
|
}
|
||||||
|
|
||||||
private convertIdToNumber(id: string): number {
|
private convertIdToNumber(id: number): number {
|
||||||
if (!id) {
|
if (!id) {
|
||||||
throw new Error('ID cannot be null or undefined');
|
throw new Error('ID cannot be null or undefined');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -64,8 +64,8 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// État de l'interface
|
// État de l'interface
|
||||||
activeTab: 'list' | 'profile' = 'list';
|
activeTab: 'list' | 'profile' = 'list';
|
||||||
selectedMerchantId: string | null = null;
|
selectedMerchantId: number | null = null;
|
||||||
selectedConfigId: string | null = null;
|
selectedConfigId: number | null = null;
|
||||||
|
|
||||||
// Gestion des permissions
|
// Gestion des permissions
|
||||||
currentUserRole: UserRole | null = null;
|
currentUserRole: UserRole | null = null;
|
||||||
@ -91,8 +91,8 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
selectedMerchantForDelete: Merchant | null = null;
|
selectedMerchantForDelete: Merchant | null = null;
|
||||||
|
|
||||||
// États pour la gestion des modifications
|
// États pour la gestion des modifications
|
||||||
editingConfigs: { [configId: string]: boolean } = {};
|
editingConfigs: { [configId: number]: boolean } = {};
|
||||||
editingContacts: { [contactId: string]: boolean } = {};
|
editingContacts: { [contactId: number]: boolean } = {};
|
||||||
|
|
||||||
// Références aux templates de modals
|
// Références aux templates de modals
|
||||||
@ViewChild('createMerchantModal') createMerchantModal!: TemplateRef<any>;
|
@ViewChild('createMerchantModal') createMerchantModal!: TemplateRef<any>;
|
||||||
@ -126,8 +126,8 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
selectedMerchantPartnerId: string = '';
|
selectedMerchantPartnerId: string = '';
|
||||||
|
|
||||||
// Cache des marchands
|
// Cache des marchands
|
||||||
merchantProfiles: { [merchantId: string]: Merchant } = {};
|
merchantProfiles: { [merchantId: number]: Merchant } = {};
|
||||||
loadingMerchants: { [merchantId: string]: boolean } = {};
|
loadingMerchants: { [merchantId: number]: boolean } = {};
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.activeTab = 'list';
|
this.activeTab = 'list';
|
||||||
@ -303,11 +303,13 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
canModifyMerchant(merchant: Merchant): boolean {
|
canModifyMerchant(merchant: Merchant): boolean {
|
||||||
if (this.canManageMerchants) return true;
|
if (this.canManageMerchants) return true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
// PARTNER ne peut modifier que ses propres marchands
|
// PARTNER ne peut modifier que ses propres marchands
|
||||||
// Vérifier via les configs ou users
|
// Vérifier via les configs ou users
|
||||||
return merchant.configs?.some(config => config.merchantPartnerId === this.currentMerchantPartnerId) ||
|
//return merchant.configs?.some(config => config.merchantPartnerId === this.currentMerchantPartnerId) ||
|
||||||
merchant.users?.some(user => user.merchantPartnerId === this.currentMerchantPartnerId);
|
// merchant.users?.some(user => user.merchantPartnerId === this.currentMerchantPartnerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== GESTION DES MARCHANDS ====================
|
// ==================== GESTION DES MARCHANDS ====================
|
||||||
@ -320,8 +322,8 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMerchantSelectionChange(selectedMerchantId: string): void {
|
onMerchantSelectionChange(selectedMerchantId: number): void {
|
||||||
this.selectedMerchantPartnerId = selectedMerchantId;
|
//this.selectedMerchantPartnerId = selectedMerchantId;
|
||||||
|
|
||||||
// Recharger les marchands pour le partenaire sélectionné
|
// Recharger les marchands pour le partenaire sélectionné
|
||||||
if (this.merchantConfigsList) {
|
if (this.merchantConfigsList) {
|
||||||
@ -331,7 +333,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// ==================== GESTION DES ONGLETS ====================
|
// ==================== GESTION DES ONGLETS ====================
|
||||||
|
|
||||||
showTab(tab: 'list' | 'profile', merchantId?: string): void {
|
showTab(tab: 'list' | 'profile', merchantId?: number): void {
|
||||||
console.log(`Switching to tab: ${tab}`, merchantId ? `for merchant ${merchantId}` : '');
|
console.log(`Switching to tab: ${tab}`, merchantId ? `for merchant ${merchantId}` : '');
|
||||||
this.activeTab = tab;
|
this.activeTab = tab;
|
||||||
|
|
||||||
@ -347,7 +349,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadMerchantProfile(merchantId: string): void {
|
private loadMerchantProfile(merchantId: number): void {
|
||||||
if (this.loadingMerchants[merchantId]) return;
|
if (this.loadingMerchants[merchantId]) return;
|
||||||
|
|
||||||
this.loadingMerchants[merchantId] = true;
|
this.loadingMerchants[merchantId] = true;
|
||||||
@ -374,7 +376,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
// ==================== ÉVÉNEMENTS DES COMPOSANTS ENFANTS ====================
|
// ==================== ÉVÉNEMENTS DES COMPOSANTS ENFANTS ====================
|
||||||
|
|
||||||
onMerchantSelected(merchantId: string): void {
|
onMerchantSelected(merchantId: number): void {
|
||||||
this.showTab('profile', merchantId);
|
this.showTab('profile', merchantId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,7 +390,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
this.openDeleteMerchantModal(merchant.id!);
|
this.openDeleteMerchantModal(merchant.id!);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEditConfigRequested(configId: string): void {
|
onEditConfigRequested(configId: number): void {
|
||||||
this.openEditMerchantModal(configId);
|
this.openEditMerchantModal(configId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +415,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
this.openModal(this.createMerchantModal);
|
this.openModal(this.createMerchantModal);
|
||||||
}
|
}
|
||||||
|
|
||||||
private openEditMerchantModal(merchantId: string): void {
|
private openEditMerchantModal(merchantId: number): void {
|
||||||
|
|
||||||
this.merchantConfigService.getMerchantById(merchantId)
|
this.merchantConfigService.getMerchantById(merchantId)
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
@ -440,7 +442,7 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private openDeleteMerchantModal(merchantId: string): void {
|
private openDeleteMerchantModal(merchantId: number): void {
|
||||||
if (!this.canDeleteMerchants) {
|
if (!this.canDeleteMerchants) {
|
||||||
console.warn('User does not have permission to delete merchants');
|
console.warn('User does not have permission to delete merchants');
|
||||||
return;
|
return;
|
||||||
@ -765,12 +767,12 @@ export class MerchantConfigManagement implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Méthodes utilitaires pour le template d'édition
|
// Méthodes utilitaires pour le template d'édition
|
||||||
trackByConfigId(index: number, config: MerchantConfig): string {
|
trackByConfigId(index: number, config: MerchantConfig): number {
|
||||||
return config.id || `new-${index}`;
|
return config.id || index;
|
||||||
}
|
}
|
||||||
|
|
||||||
trackByContactId(index: number, contact: TechnicalContact): string {
|
trackByContactId(index: number, contact: TechnicalContact): number {
|
||||||
return contact.id || `new-${index}`;
|
return contact.id || index;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Méthode pour détecter les configs sensibles
|
// Méthode pour détecter les configs sensibles
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export class MerchantDataAdapter {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...apiMerchant,
|
...apiMerchant,
|
||||||
id: this.convertIdToString(apiMerchant.id),
|
id: apiMerchant.id, //this.convertIdToString(apiMerchant.id),
|
||||||
configs: (apiMerchant.configs || []).map(config =>
|
configs: (apiMerchant.configs || []).map(config =>
|
||||||
this.convertApiConfigToFrontend(config)
|
this.convertApiConfigToFrontend(config)
|
||||||
),
|
),
|
||||||
@ -48,8 +48,8 @@ export class MerchantDataAdapter {
|
|||||||
convertApiConfigToFrontend(apiConfig: ApiMerchantConfig): MerchantConfig {
|
convertApiConfigToFrontend(apiConfig: ApiMerchantConfig): MerchantConfig {
|
||||||
return {
|
return {
|
||||||
...apiConfig,
|
...apiConfig,
|
||||||
id: this.convertIdToString(apiConfig.id),
|
id: apiConfig.id, //this.convertIdToString(apiConfig.id),
|
||||||
merchantPartnerId: this.convertIdToString(apiConfig.merchantPartnerId)
|
merchantPartnerId: apiConfig.merchantPartnerId // this.convertIdToString(apiConfig.merchantPartnerId)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,8 +59,8 @@ export class MerchantDataAdapter {
|
|||||||
convertApiContactToFrontend(apiContact: ApiTechnicalContact): TechnicalContact {
|
convertApiContactToFrontend(apiContact: ApiTechnicalContact): TechnicalContact {
|
||||||
return {
|
return {
|
||||||
...apiContact,
|
...apiContact,
|
||||||
id: this.convertIdToString(apiContact.id),
|
id: apiContact.id, //this.convertIdToString(apiContact.id),
|
||||||
merchantPartnerId: this.convertIdToString(apiContact.merchantPartnerId)
|
merchantPartnerId: apiContact.merchantPartnerId //this.convertIdToString(apiContact.merchantPartnerId)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ export class MerchantDataAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (config.merchantPartnerId !== undefined) {
|
if (config.merchantPartnerId !== undefined) {
|
||||||
updateData.merchantPartnerId = this.convertIdToNumber(config.merchantPartnerId);
|
updateData.merchantPartnerId = config.merchantPartnerId //this.convertIdToNumber(config.merchantPartnerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return updateData;
|
return updateData;
|
||||||
|
|||||||
@ -291,12 +291,12 @@ const routes: Routes = [
|
|||||||
// Support & Profile (Tous les utilisateurs authentifiés)
|
// Support & Profile (Tous les utilisateurs authentifiés)
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
{
|
{
|
||||||
path: 'dcb-support',
|
path: 'support',
|
||||||
component: Support,
|
component: Support,
|
||||||
canActivate: [authGuard, roleGuard],
|
canActivate: [authGuard, roleGuard],
|
||||||
data: {
|
data: {
|
||||||
title: 'Support',
|
title: 'Support',
|
||||||
module: 'dcb-support'
|
module: 'support'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,503 @@
|
|||||||
import { Component } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { MerchantSyncCrudService } from '../hub-users-management/merchant-sync-orchestrator.service';
|
||||||
|
import { UserRole } from '@core/models/dcb-bo-hub-user.model';
|
||||||
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-support',
|
selector: 'app-support',
|
||||||
templateUrl: './support.html',
|
templateUrl: './support.html'
|
||||||
})
|
})
|
||||||
export class Support {}
|
export class Support implements OnInit {
|
||||||
|
|
||||||
|
private testData = {
|
||||||
|
merchantConfigId: '',
|
||||||
|
keycloakMerchantId: '',
|
||||||
|
testUserId: '',
|
||||||
|
testMerchantConfigUserId:''
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(private merchantCrudService: MerchantSyncCrudService) {}
|
||||||
|
|
||||||
|
ngOnInit() {
|
||||||
|
console.log('🚀 Support Component - Tests CRUD Merchants');
|
||||||
|
console.log('='.repeat(60));
|
||||||
|
|
||||||
|
// Démarrer les tests automatiquement
|
||||||
|
this.runAllTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exécuter tous les tests
|
||||||
|
*/
|
||||||
|
async runAllTests(): Promise<void> {
|
||||||
|
try {
|
||||||
|
console.log('\n🧪 DÉMARRAGE DES TESTS COMPLETS');
|
||||||
|
console.log('='.repeat(50));
|
||||||
|
|
||||||
|
// Test 1: Création
|
||||||
|
await this.testCreateOperations();
|
||||||
|
|
||||||
|
// Test 2: Lecture
|
||||||
|
await this.testReadOperations();
|
||||||
|
|
||||||
|
// Test 3: Mise à jour
|
||||||
|
await this.testUpdateOperations();
|
||||||
|
|
||||||
|
// Test 4: Gestion utilisateurs
|
||||||
|
await this.testUserOperations();
|
||||||
|
|
||||||
|
// Test 5: Suppression
|
||||||
|
await this.testDeleteOperations();
|
||||||
|
|
||||||
|
console.log('\n✅ TOUS LES TESTS TERMINÉS AVEC SUCCÈS!');
|
||||||
|
console.log('='.repeat(50));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR CRITIQUE DANS LES TESTS:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TEST 1: Opérations de création
|
||||||
|
*/
|
||||||
|
private async testCreateOperations(): Promise<void> {
|
||||||
|
console.log('\n📝 TEST 1: Opérations CREATE');
|
||||||
|
console.log('-'.repeat(40));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1.1 Créer un merchant de test
|
||||||
|
console.log('1.1 Création d\'un merchant test...');
|
||||||
|
|
||||||
|
const merchantData = {
|
||||||
|
name: 'Test Merchant ' + Date.now(),
|
||||||
|
adresse: '123 Test Street',
|
||||||
|
phone: '+336' + Math.floor(10000000 + Math.random() * 90000000),
|
||||||
|
configs: [
|
||||||
|
{
|
||||||
|
name: 'API_KEY',
|
||||||
|
value: 'test-api-key-' + Date.now(),
|
||||||
|
operatorId: 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
technicalContacts: [
|
||||||
|
{
|
||||||
|
firstName: 'Test',
|
||||||
|
lastName: 'User',
|
||||||
|
phone: '+33612345678',
|
||||||
|
email: `test.${Date.now()}@example.com`
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
const ownerData = {
|
||||||
|
username: `testowner.${Date.now()}`,
|
||||||
|
email: `owner.${Date.now()}@test-merchant.com`,
|
||||||
|
password: 'TestPassword123!',
|
||||||
|
firstName: 'Test',
|
||||||
|
lastName: 'Owner'
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('📤 Données merchant:', merchantData);
|
||||||
|
console.log('👤 Données owner:', { ...ownerData, password: '***' });
|
||||||
|
|
||||||
|
const createResult = await firstValueFrom(
|
||||||
|
this.merchantCrudService.createMerchant(merchantData, ownerData)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.testData.merchantConfigId = String(createResult.merchantConfig.id!);
|
||||||
|
this.testData.keycloakMerchantId = createResult.keycloakMerchant.id;
|
||||||
|
|
||||||
|
console.log('✅ Merchant créé avec succès!');
|
||||||
|
console.log(' Merchant Config ID:', this.testData.merchantConfigId);
|
||||||
|
console.log(' Keycloak Merchant ID:', this.testData.keycloakMerchantId);
|
||||||
|
console.log(' Merchant name:', createResult.merchantConfig.name);
|
||||||
|
console.log(' DCB Partner email:', createResult.keycloakMerchant.email);
|
||||||
|
|
||||||
|
// 1.2 Créer un utilisateur pour le merchant
|
||||||
|
console.log('\n1.2 Création d\'un utilisateur test...');
|
||||||
|
|
||||||
|
const userData = {
|
||||||
|
username: `testuser.${Date.now()}`,
|
||||||
|
email: `user.${Date.now()}@test-merchant.com`,
|
||||||
|
password: 'UserPassword123!',
|
||||||
|
firstName: 'Test',
|
||||||
|
lastName: 'User',
|
||||||
|
role: UserRole.DCB_PARTNER_ADMIN
|
||||||
|
};
|
||||||
|
|
||||||
|
const userResult = await firstValueFrom(
|
||||||
|
this.merchantCrudService.createMerchantUser(
|
||||||
|
this.testData.merchantConfigId,
|
||||||
|
this.testData.keycloakMerchantId,
|
||||||
|
userData
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.testData.testUserId = userResult.keycloakUser.id;
|
||||||
|
this.testData.testMerchantConfigUserId = String(userResult.merchantConfigUser.userId);
|
||||||
|
|
||||||
|
console.log('✅ Utilisateur créé avec succès!');
|
||||||
|
console.log(' Keycloak User ID:', this.testData.testUserId);
|
||||||
|
console.log(' Merchant Config User ID:', this.testData.testMerchantConfigUserId);
|
||||||
|
console.log(' Email:', userResult.keycloakUser.email);
|
||||||
|
console.log(' Rôle Keycloak:', userResult.keycloakUser.role);
|
||||||
|
console.log(' Rôle Merchant Config:', userResult.merchantConfigUser.role);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR lors de la création:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TEST 2: Opérations de lecture
|
||||||
|
*/
|
||||||
|
private async testReadOperations(): Promise<void> {
|
||||||
|
console.log('\n🔍 TEST 2: Opérations READ');
|
||||||
|
console.log('-'.repeat(40));
|
||||||
|
|
||||||
|
if (!this.testData.merchantConfigId) {
|
||||||
|
console.log('⚠️ Aucun merchant créé, passage au test suivant');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 2.1 Lire le merchant par ID
|
||||||
|
console.log('2.1 Lecture du merchant par ID...');
|
||||||
|
|
||||||
|
const merchant = await firstValueFrom(
|
||||||
|
this.merchantCrudService.getMerchant(
|
||||||
|
this.testData.merchantConfigId,
|
||||||
|
this.testData.keycloakMerchantId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✅ Merchant récupéré:');
|
||||||
|
console.log(' ID:', merchant.merchantConfig.id);
|
||||||
|
console.log(' Nom:', merchant.merchantConfig.name);
|
||||||
|
console.log(' Adresse:', merchant.merchantConfig.adresse);
|
||||||
|
console.log(' Configurations:', merchant.merchantConfig.configs?.length || 0);
|
||||||
|
console.log(' Contacts techniques:', merchant.merchantConfig.technicalContacts?.length || 0);
|
||||||
|
|
||||||
|
// 2.2 Lire tous les merchants
|
||||||
|
console.log('\n2.2 Lecture de tous les merchants...');
|
||||||
|
|
||||||
|
const allMerchants = await firstValueFrom(
|
||||||
|
this.merchantCrudService.getAllMerchants()
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`✅ ${allMerchants.length} merchants trouvés au total`);
|
||||||
|
|
||||||
|
if (allMerchants.length > 0) {
|
||||||
|
console.log(' Dernier merchant:', allMerchants[allMerchants.length - 1].merchantConfig.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.3 Lire les utilisateurs du merchant
|
||||||
|
console.log('\n2.3 Lecture des utilisateurs du merchant...');
|
||||||
|
|
||||||
|
const merchantUsers = await firstValueFrom(
|
||||||
|
this.merchantCrudService.getMerchantUsers(
|
||||||
|
this.testData.merchantConfigId,
|
||||||
|
this.testData.keycloakMerchantId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`✅ ${merchantUsers.users.length} utilisateurs dans ce merchant`);
|
||||||
|
|
||||||
|
merchantUsers.users.forEach((user: any, index: number) => {
|
||||||
|
console.log(` ${index + 1}. ${user.keycloakUser?.email || 'Inconnu'}`);
|
||||||
|
console.log(` Synchronisé: ${user.synced ? '✅' : '❌'}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2.4 Vérifier le statut de synchronisation
|
||||||
|
console.log('\n2.4 Vérification statut synchronisation...');
|
||||||
|
|
||||||
|
const syncStatus = await firstValueFrom(
|
||||||
|
this.merchantCrudService.checkSyncStatus(
|
||||||
|
String(this.testData.merchantConfigId),
|
||||||
|
this.testData.keycloakMerchantId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✅ Statut de synchronisation:');
|
||||||
|
console.log(' Existe dans Keycloak:', syncStatus.existsInKeycloak ? '✅' : '❌');
|
||||||
|
console.log(' Existe dans Merchant Config:', syncStatus.existsInMerchantConfig ? '✅' : '❌');
|
||||||
|
console.log(' Utilisateurs synchronisés:', syncStatus.usersSynced ? '✅' : '❌');
|
||||||
|
console.log(` ${syncStatus.syncedUserCount}/${syncStatus.totalUserCount} utilisateurs synchronisés`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR lors de la lecture:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TEST 3: Opérations de mise à jour
|
||||||
|
*/
|
||||||
|
private async testUpdateOperations(): Promise<void> {
|
||||||
|
console.log('\n✏️ TEST 3: Opérations UPDATE');
|
||||||
|
console.log('-'.repeat(40));
|
||||||
|
|
||||||
|
if (!this.testData.merchantConfigId) {
|
||||||
|
console.log('⚠️ Aucun merchant créé, passage au test suivant');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 3.1 Mettre à jour le merchant
|
||||||
|
console.log('3.1 Mise à jour du merchant...');
|
||||||
|
|
||||||
|
const newName = `Updated Merchant ${Date.now()}`;
|
||||||
|
const newEmail = `updated.${Date.now()}@test-merchant.com`;
|
||||||
|
|
||||||
|
const updateResult = await firstValueFrom(
|
||||||
|
this.merchantCrudService.updateMerchant(
|
||||||
|
this.testData.merchantConfigId,
|
||||||
|
this.testData.keycloakMerchantId,
|
||||||
|
{
|
||||||
|
merchantConfig: {
|
||||||
|
name: newName,
|
||||||
|
description: 'Mis à jour par les tests'
|
||||||
|
},
|
||||||
|
dcbPartner: {
|
||||||
|
email: newEmail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✅ Merchant mis à jour:');
|
||||||
|
console.log(' Nouveau nom:', newName);
|
||||||
|
console.log(' Nouvel email DCB Partner:', newEmail);
|
||||||
|
|
||||||
|
// 3.2 Mettre à jour l'utilisateur
|
||||||
|
console.log('\n3.2 Mise à jour de l\'utilisateur...');
|
||||||
|
|
||||||
|
if (this.testData.testUserId) {
|
||||||
|
const userUpdateResult = await firstValueFrom(
|
||||||
|
this.merchantCrudService.updateMerchantUser(
|
||||||
|
this.testData.testUserId,
|
||||||
|
Number(this.testData.testMerchantConfigUserId),
|
||||||
|
this.testData.merchantConfigId,
|
||||||
|
{
|
||||||
|
keycloak: {
|
||||||
|
firstName: 'Updated',
|
||||||
|
lastName: 'User'
|
||||||
|
},
|
||||||
|
merchantConfig: {
|
||||||
|
role: UserRole.MERCHANT_CONFIG_MANAGER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✅ Utilisateur mis à jour:');
|
||||||
|
console.log(' Nouveau nom:', 'Updated User');
|
||||||
|
console.log(' Nouveau rôle:', UserRole.MERCHANT_CONFIG_MANAGER);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ Aucun utilisateur à mettre à jour');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.3 Activer/désactiver un utilisateur
|
||||||
|
console.log('\n3.3 Changement statut utilisateur...');
|
||||||
|
|
||||||
|
if (this.testData.testUserId) {
|
||||||
|
const toggleResult = await firstValueFrom(
|
||||||
|
this.merchantCrudService.toggleUserStatus(
|
||||||
|
this.testData.testUserId,
|
||||||
|
false // Désactiver
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✅ Statut utilisateur changé:');
|
||||||
|
console.log(' Activé:', toggleResult.enabled ? '✅' : '❌');
|
||||||
|
|
||||||
|
// Réactiver pour les tests suivants
|
||||||
|
await firstValueFrom(
|
||||||
|
this.merchantCrudService.toggleUserStatus(
|
||||||
|
this.testData.testUserId,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
console.log(' Réactivé pour les tests suivants');
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR lors de la mise à jour:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TEST 4: Opérations utilisateurs avancées
|
||||||
|
*/
|
||||||
|
private async testUserOperations(): Promise<void> {
|
||||||
|
console.log('\n👥 TEST 4: Opérations utilisateurs avancées');
|
||||||
|
console.log('-'.repeat(40));
|
||||||
|
|
||||||
|
if (!this.testData.testUserId) {
|
||||||
|
console.log('⚠️ Aucun utilisateur créé, passage au test suivant');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 4.1 Réinitialiser le mot de passe
|
||||||
|
console.log('4.1 Réinitialisation mot de passe...');
|
||||||
|
|
||||||
|
const resetResult = await firstValueFrom(
|
||||||
|
this.merchantCrudService.resetUserPassword(
|
||||||
|
this.testData.testUserId,
|
||||||
|
'NewPassword123!',
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✅ Mot de passe réinitialisé:');
|
||||||
|
console.log(' Succès:', resetResult.success);
|
||||||
|
console.log(' Message:', resetResult.message);
|
||||||
|
|
||||||
|
// 4.2 Rechercher des merchants
|
||||||
|
console.log('\n4.2 Recherche de merchants...');
|
||||||
|
|
||||||
|
const searchResults = await firstValueFrom(
|
||||||
|
this.merchantCrudService.searchMerchants('Test')
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(`✅ ${searchResults.length} merchants trouvés avec "Test"`);
|
||||||
|
|
||||||
|
if (searchResults.length > 0) {
|
||||||
|
console.log(' Premier résultat:', searchResults[0].merchantConfig.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR opérations utilisateurs:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TEST 5: Opérations de suppression
|
||||||
|
*/
|
||||||
|
private async testDeleteOperations(): Promise<void> {
|
||||||
|
console.log('\n🗑️ TEST 5: Opérations DELETE');
|
||||||
|
console.log('-'.repeat(40));
|
||||||
|
|
||||||
|
if (!this.testData.merchantConfigId) {
|
||||||
|
console.log('⚠️ Aucun merchant créé, passage au test suivant');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 5.1 Supprimer l'utilisateur test
|
||||||
|
console.log('5.1 Suppression de l\'utilisateur test...');
|
||||||
|
|
||||||
|
if (this.testData.testUserId) {
|
||||||
|
const deleteUserResult = await firstValueFrom(
|
||||||
|
this.merchantCrudService.deleteMerchantUser(
|
||||||
|
this.testData.testUserId,
|
||||||
|
Number(this.testData.testMerchantConfigUserId),
|
||||||
|
this.testData.merchantConfigId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✅ Utilisateur supprimé:');
|
||||||
|
console.log(' Succès:', deleteUserResult.success);
|
||||||
|
console.log(' Message:', deleteUserResult.message);
|
||||||
|
|
||||||
|
// Réinitialiser les IDs utilisateur
|
||||||
|
this.testData.testUserId = '';
|
||||||
|
this.testData.testMerchantConfigUserId = '';
|
||||||
|
} else {
|
||||||
|
console.log('⚠️ Aucun utilisateur à supprimer');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.2 Supprimer le merchant test
|
||||||
|
console.log('\n5.2 Suppression du merchant test...');
|
||||||
|
|
||||||
|
const deleteMerchantResult = await firstValueFrom(
|
||||||
|
this.merchantCrudService.deleteMerchant(
|
||||||
|
this.testData.merchantConfigId,
|
||||||
|
this.testData.keycloakMerchantId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('✅ Merchant supprimé:');
|
||||||
|
console.log(' Succès:', deleteMerchantResult.success);
|
||||||
|
console.log(' Message:', deleteMerchantResult.message);
|
||||||
|
|
||||||
|
// 5.3 Vérifier la suppression
|
||||||
|
console.log('\n5.3 Vérification de la suppression...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await firstValueFrom(
|
||||||
|
this.merchantCrudService.getMerchant(
|
||||||
|
this.testData.merchantConfigId,
|
||||||
|
this.testData.keycloakMerchantId
|
||||||
|
)
|
||||||
|
);
|
||||||
|
console.log('❌ Le merchant existe toujours - PROBLÈME!');
|
||||||
|
} catch (error) {
|
||||||
|
console.log('✅ Le merchant a bien été supprimé');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Réinitialiser toutes les données
|
||||||
|
this.testData = {
|
||||||
|
merchantConfigId: '',
|
||||||
|
keycloakMerchantId: '',
|
||||||
|
testUserId: '',
|
||||||
|
testMerchantConfigUserId: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('🧹 Données de test réinitialisées');
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ ERREUR lors de la suppression:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Méthodes pour exécuter des tests individuels
|
||||||
|
*/
|
||||||
|
async testCreateOnly(): Promise<void> {
|
||||||
|
console.log('🧪 Test CREATE uniquement');
|
||||||
|
await this.testCreateOperations();
|
||||||
|
}
|
||||||
|
|
||||||
|
async testReadOnly(): Promise<void> {
|
||||||
|
console.log('🧪 Test READ uniquement');
|
||||||
|
await this.testReadOperations();
|
||||||
|
}
|
||||||
|
|
||||||
|
async testUpdateOnly(): Promise<void> {
|
||||||
|
console.log('🧪 Test UPDATE uniquement');
|
||||||
|
await this.testUpdateOperations();
|
||||||
|
}
|
||||||
|
|
||||||
|
async testDeleteOnly(): Promise<void> {
|
||||||
|
console.log('🧪 Test DELETE uniquement');
|
||||||
|
await this.testDeleteOperations();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Afficher l'état actuel des tests
|
||||||
|
*/
|
||||||
|
showTestStatus(): void {
|
||||||
|
console.log('\n📊 ÉTAT ACTUEL DES TESTS');
|
||||||
|
console.log('-'.repeat(30));
|
||||||
|
console.log('Merchant Config ID:', this.testData.merchantConfigId || 'Non créé');
|
||||||
|
console.log('Keycloak Merchant ID:', this.testData.keycloakMerchantId || 'Non créé');
|
||||||
|
console.log('Test User ID:', this.testData.testUserId || 'Non créé');
|
||||||
|
console.log('Test Merchant Config User ID:', this.testData.testMerchantConfigUserId || 'Non créé');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Réinitialiser les données de test
|
||||||
|
*/
|
||||||
|
resetTestData(): void {
|
||||||
|
this.testData = {
|
||||||
|
merchantConfigId: '',
|
||||||
|
keycloakMerchantId: '',
|
||||||
|
testUserId: '',
|
||||||
|
testMerchantConfigUserId: ''
|
||||||
|
};
|
||||||
|
console.log('🧹 Données de test réinitialisées');
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user