feat: Manage Images using Minio Service
This commit is contained in:
parent
a45f4b151c
commit
d26feb396f
@ -150,7 +150,7 @@ export class DashboardAccessService {
|
|||||||
if (access.isHubUser) {
|
if (access.isHubUser) {
|
||||||
return this.merchantService.getAllMerchants().pipe(
|
return this.merchantService.getAllMerchants().pipe(
|
||||||
map(merchants => {
|
map(merchants => {
|
||||||
const available: AllowedMerchant[] = merchants.map(m => ({
|
const available: AllowedMerchant[] = merchants.items.map(m => ({
|
||||||
id: m.id,
|
id: m.id,
|
||||||
name: m.name
|
name: m.name
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -8,7 +8,6 @@ import { Subject, takeUntil } from 'rxjs';
|
|||||||
import { HubUsersService } from './hub-users.service';
|
import { HubUsersService } from './hub-users.service';
|
||||||
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
||||||
import { AuthService } from '@core/services/auth.service';
|
import { AuthService } from '@core/services/auth.service';
|
||||||
import { MerchantSyncService } from './merchant-sync-orchestrator.service';
|
|
||||||
import { PageTitle } from '@app/components/page-title/page-title';
|
import { PageTitle } from '@app/components/page-title/page-title';
|
||||||
import { HubUsersList } from './hub-users-list/hub-users-list';
|
import { HubUsersList } from './hub-users-list/hub-users-list';
|
||||||
import { HubUserProfile } from './hub-users-profile/hub-users-profile';
|
import { HubUserProfile } from './hub-users-profile/hub-users-profile';
|
||||||
@ -39,7 +38,6 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
private modalService = inject(NgbModal);
|
private modalService = inject(NgbModal);
|
||||||
private authService = inject(AuthService);
|
private authService = inject(AuthService);
|
||||||
private hubUsersService = inject(HubUsersService);
|
private hubUsersService = inject(HubUsersService);
|
||||||
private merchantSyncService = inject(MerchantSyncService);
|
|
||||||
protected roleService = inject(RoleManagementService);
|
protected roleService = inject(RoleManagementService);
|
||||||
private cdRef = inject(ChangeDetectorRef);
|
private cdRef = inject(ChangeDetectorRef);
|
||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
@ -476,45 +474,6 @@ export class HubUsersManagement implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Crée un marchand dans MerchantConfig (séparément de l'utilisateur)
|
|
||||||
*/
|
|
||||||
createMerchant() {
|
|
||||||
// Validation du formulaire marchand
|
|
||||||
const merchantValidation = this.validateMerchantForm();
|
|
||||||
if (!merchantValidation.isValid) {
|
|
||||||
this.createUserError = merchantValidation.error!;
|
|
||||||
console.error('❌ Merchant form validation failed:', merchantValidation.error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.creatingMerchant = true;
|
|
||||||
|
|
||||||
console.log('📤 Creating merchant in MerchantConfig...');
|
|
||||||
|
|
||||||
this.merchantSyncService.createMerchantInConfigOnly(this.newMerchant)
|
|
||||||
.pipe(takeUntil(this.destroy$))
|
|
||||||
.subscribe({
|
|
||||||
next: (merchantConfig) => {
|
|
||||||
console.log('✅ Merchant created in MerchantConfig:', merchantConfig);
|
|
||||||
this.creatingMerchant = false;
|
|
||||||
|
|
||||||
// Optionnel: proposer d'associer un utilisateur au marchand créé
|
|
||||||
console.log(`✅ Merchant ID: ${merchantConfig.id} - Name: ${merchantConfig.name}`);
|
|
||||||
|
|
||||||
this.modalService.dismissAll();
|
|
||||||
this.resetMerchantForm();
|
|
||||||
this.cdRef.detectChanges();
|
|
||||||
},
|
|
||||||
error: (error) => {
|
|
||||||
console.error('❌ Error creating merchant in MerchantConfig:', error);
|
|
||||||
this.creatingMerchant = false;
|
|
||||||
this.createUserError = this.getMerchantErrorMessage(error);
|
|
||||||
this.cdRef.detectChanges();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vérifie si le rôle est un rôle partenaire
|
* Vérifie si le rôle est un rôle partenaire
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,507 +0,0 @@
|
|||||||
import { Injectable, inject } from '@angular/core';
|
|
||||||
import { Observable, forkJoin, map, switchMap, catchError, of, throwError } from 'rxjs';
|
|
||||||
import {
|
|
||||||
CreateUserDto,
|
|
||||||
User,
|
|
||||||
UserType,
|
|
||||||
UserRole,
|
|
||||||
} from '@core/models/dcb-bo-hub-user.model';
|
|
||||||
|
|
||||||
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;
|
|
||||||
keycloakUser?: User; // Utilisateur associé (optionnel)
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MerchantUserSyncResult {
|
|
||||||
keycloakUser: User;
|
|
||||||
merchantConfigUser?: MerchantUser;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MerchantSyncStatus {
|
|
||||||
existsInKeycloak: boolean;
|
|
||||||
existsInMerchantConfig: boolean;
|
|
||||||
usersSynced: boolean;
|
|
||||||
syncedUserCount: number;
|
|
||||||
totalUserCount: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UserMerchantAssociation {
|
|
||||||
userId: string;
|
|
||||||
merchantConfigId: string;
|
|
||||||
role: UserRole; // Rôle dans MerchantConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class MerchantSyncService {
|
|
||||||
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]
|
|
||||||
]);
|
|
||||||
|
|
||||||
// ==================== MÉTHODES DE BASE ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CREATE - Créer un merchant uniquement dans MerchantConfig
|
|
||||||
*/
|
|
||||||
createMerchantInConfigOnly(merchantData: CreateMerchantDto): Observable<Merchant> {
|
|
||||||
console.log('📝 CREATE Merchant dans MerchantConfig seulement...');
|
|
||||||
|
|
||||||
return this.merchantConfigService.createMerchant(merchantData).pipe(
|
|
||||||
map(merchant => {
|
|
||||||
console.log('✅ Merchant créé dans MerchantConfig:', merchant);
|
|
||||||
return merchant;
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Échec création merchant dans MerchantConfig:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CREATE - Créer un utilisateur dans Keycloak (sans association)
|
|
||||||
*/
|
|
||||||
createKeycloakUser(
|
|
||||||
userData: {
|
|
||||||
username: string;
|
|
||||||
email: string;
|
|
||||||
password: string;
|
|
||||||
firstName: string;
|
|
||||||
lastName: string;
|
|
||||||
role: UserRole;
|
|
||||||
enabled?: boolean;
|
|
||||||
emailVerified?: boolean;
|
|
||||||
}
|
|
||||||
): Observable<User> {
|
|
||||||
console.log('📝 CREATE User dans Keycloak...');
|
|
||||||
|
|
||||||
// Déterminer le type d'utilisateur selon le rôle
|
|
||||||
const userType = this.isMerchantRole(userData.role)
|
|
||||||
? UserType.MERCHANT_PARTNER
|
|
||||||
: UserType.HUB;
|
|
||||||
|
|
||||||
const keycloakUserDto: CreateUserDto = {
|
|
||||||
username: userData.username,
|
|
||||||
email: userData.email,
|
|
||||||
firstName: userData.firstName,
|
|
||||||
lastName: userData.lastName,
|
|
||||||
password: userData.password,
|
|
||||||
userType: userType,
|
|
||||||
role: userData.role,
|
|
||||||
enabled: userData.enabled ?? true,
|
|
||||||
emailVerified: userData.emailVerified ?? false
|
|
||||||
};
|
|
||||||
|
|
||||||
// Sélectionner le service approprié selon le type d'utilisateur
|
|
||||||
const createService = this.merchantUsersService.createMerchantUser(keycloakUserDto);
|
|
||||||
|
|
||||||
return createService.pipe(
|
|
||||||
map(user => {
|
|
||||||
console.log('✅ Utilisateur créé dans Keycloak:', user);
|
|
||||||
return user;
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Échec création utilisateur dans Keycloak:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* READ - Récupérer un merchant depuis MerchantConfig seulement
|
|
||||||
*/
|
|
||||||
getMerchantFromConfigOnly(merchantConfigId: string): Observable<Merchant> {
|
|
||||||
console.log('🔍 READ Merchant depuis MerchantConfig seulement...');
|
|
||||||
|
|
||||||
return this.merchantConfigService.getMerchantById(Number(merchantConfigId)).pipe(
|
|
||||||
map(merchant => {
|
|
||||||
console.log('✅ Merchant récupéré depuis MerchantConfig:', merchant);
|
|
||||||
return merchant;
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Erreur récupération merchant depuis MerchantConfig:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* READ - Récupérer tous les merchants depuis MerchantConfig
|
|
||||||
*/
|
|
||||||
getAllMerchantsFromConfig(): Observable<Merchant[]> {
|
|
||||||
console.log('🔍 READ All Merchants depuis MerchantConfig...');
|
|
||||||
|
|
||||||
return this.merchantConfigService.getAllMerchants().pipe(
|
|
||||||
map(merchants => {
|
|
||||||
console.log(`✅ ${merchants.length} merchants récupérés depuis MerchantConfig`);
|
|
||||||
return merchants;
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Erreur récupération merchants depuis MerchantConfig:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== ASSOCIATION UTILISATEUR-MERCHANT ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Associer un utilisateur existant à un marchand
|
|
||||||
*/
|
|
||||||
associateUserToMerchant(
|
|
||||||
association: UserMerchantAssociation
|
|
||||||
): Observable<MerchantUserSyncResult> {
|
|
||||||
console.log('🔗 ASSOCIATE User to Merchant...');
|
|
||||||
|
|
||||||
return forkJoin({
|
|
||||||
user: this.getUserById(association.userId),
|
|
||||||
merchant: this.merchantConfigService.getMerchantById(Number(association.merchantConfigId))
|
|
||||||
}).pipe(
|
|
||||||
switchMap(({ user, merchant }) => {
|
|
||||||
console.log(`🔗 Associating user ${user.username} to merchant ${merchant.name}`);
|
|
||||||
|
|
||||||
// 2. Ajouter l'utilisateur à MerchantConfig
|
|
||||||
const merchantConfigUserDto: AddUserToMerchantDto = {
|
|
||||||
userId: user.id,
|
|
||||||
role: association.role,
|
|
||||||
merchantPartnerId: Number(association.merchantConfigId),
|
|
||||||
};
|
|
||||||
|
|
||||||
return this.merchantConfigService.addUserToMerchant(merchantConfigUserDto).pipe(
|
|
||||||
map(merchantConfigUser => {
|
|
||||||
console.log('✅ User added to Merchant Config:', merchantConfigUser);
|
|
||||||
return {
|
|
||||||
keycloakUser: user,
|
|
||||||
merchantConfigUser
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Error adding user to MerchantConfig:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Error in association process:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dissocier un utilisateur d'un marchand
|
|
||||||
*/
|
|
||||||
dissociateUserFromMerchant(
|
|
||||||
userId: string,
|
|
||||||
merchantConfigId: string
|
|
||||||
): Observable<{ success: boolean; message: string }> {
|
|
||||||
console.log('🔗 DISSOCIATE User from Merchant...');
|
|
||||||
|
|
||||||
return forkJoin({
|
|
||||||
// Retirer l'utilisateur de MerchantConfig
|
|
||||||
merchantConfigRemoval: this.merchantConfigService.removeUserFromMerchant(
|
|
||||||
Number(merchantConfigId),
|
|
||||||
userId
|
|
||||||
).pipe(catchError(() => of({ ignored: true })))
|
|
||||||
}).pipe(
|
|
||||||
map(() => ({
|
|
||||||
success: true,
|
|
||||||
message: 'Utilisateur dissocié du marchand avec succès'
|
|
||||||
})),
|
|
||||||
catchError(async (error) => ({
|
|
||||||
success: false,
|
|
||||||
message: `Erreur lors de la dissociation: ${error.message}`
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupérer les utilisateurs associés à un marchand
|
|
||||||
*/
|
|
||||||
getUsersByMerchant(merchantConfigId: string): Observable<User[]> {
|
|
||||||
console.log('🔍 READ Users by Merchant...');
|
|
||||||
|
|
||||||
// Récupérer les utilisateurs de MerchantConfig
|
|
||||||
return this.merchantConfigService.getMerchantUsers(Number(merchantConfigId)).pipe(
|
|
||||||
switchMap(merchantConfigUsers => {
|
|
||||||
if (merchantConfigUsers.total === 0) {
|
|
||||||
return of([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Récupérer les détails de chaque utilisateur depuis Keycloak
|
|
||||||
const userObservables = merchantConfigUsers.items.map((mcUser: { userId: string; }) =>
|
|
||||||
this.getUserById(mcUser.userId).pipe(
|
|
||||||
catchError(() => of(null)) // Ignorer les utilisateurs non trouvés
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return forkJoin(userObservables).pipe(
|
|
||||||
map(users => users.filter((user): user is User => user !== null))
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
map(users => {
|
|
||||||
console.log(`✅ ${users.length} utilisateurs trouvés pour merchant ${merchantConfigId}`);
|
|
||||||
return users;
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Erreur récupération utilisateurs:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== MÉTHODES DE GESTION ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UPDATE - Mettre à jour un merchant dans MerchantConfig seulement
|
|
||||||
*/
|
|
||||||
updateMerchantInConfigOnly(
|
|
||||||
merchantConfigId: string,
|
|
||||||
updates: UpdateMerchantDto
|
|
||||||
): Observable<Merchant> {
|
|
||||||
console.log('✏️ UPDATE Merchant dans MerchantConfig seulement...');
|
|
||||||
|
|
||||||
return this.merchantConfigService.updateMerchant(
|
|
||||||
Number(merchantConfigId),
|
|
||||||
updates
|
|
||||||
).pipe(
|
|
||||||
map(merchant => {
|
|
||||||
console.log('✅ Merchant mis à jour dans MerchantConfig:', merchant);
|
|
||||||
return merchant;
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Erreur mise à jour merchant dans MerchantConfig:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UPDATE - Mettre à jour un utilisateur dans Keycloak
|
|
||||||
*/
|
|
||||||
updateKeycloakUserRole(
|
|
||||||
keycloakUserId: string,
|
|
||||||
newRole: UserRole
|
|
||||||
): Observable<UpdateUserRoleDto> {
|
|
||||||
console.log('✏️ UPDATE User dans Keycloak...');
|
|
||||||
|
|
||||||
return this.getUserById(keycloakUserId).pipe(
|
|
||||||
switchMap(user => {
|
|
||||||
const updateService = this.merchantUsersService.updateMerchantUserRole(keycloakUserId, newRole);
|
|
||||||
|
|
||||||
return updateService.pipe(
|
|
||||||
map(updatedUser => {
|
|
||||||
console.log('✅ Utilisateur mis à jour dans Keycloak:', updatedUser);
|
|
||||||
return updatedUser;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Erreur mise à jour utilisateur dans Keycloak:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* UPDATE - Changer le rôle d'un utilisateur dans MerchantConfig
|
|
||||||
*/
|
|
||||||
updateUserRoleInMerchantConfig(
|
|
||||||
merchantConfigId: string,
|
|
||||||
userId: string,
|
|
||||||
newRole: UserRole
|
|
||||||
): Observable<MerchantUser> {
|
|
||||||
console.log('✏️ UPDATE User Role dans MerchantConfig...');
|
|
||||||
|
|
||||||
const updateRoleDto: UpdateUserRoleDto = {
|
|
||||||
role: newRole
|
|
||||||
};
|
|
||||||
|
|
||||||
this.updateKeycloakUserRole(userId, newRole)
|
|
||||||
|
|
||||||
return this.merchantConfigService.updateUserRole(
|
|
||||||
Number(merchantConfigId),
|
|
||||||
userId,
|
|
||||||
updateRoleDto
|
|
||||||
).pipe(
|
|
||||||
map(merchantConfigUser => {
|
|
||||||
console.log('✅ Rôle utilisateur mis à jour dans MerchantConfig:', merchantConfigUser);
|
|
||||||
return merchantConfigUser;
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Erreur changement rôle utilisateur dans MerchantConfig:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DELETE - Supprimer un merchant de MerchantConfig seulement
|
|
||||||
*/
|
|
||||||
deleteMerchantFromConfigOnly(
|
|
||||||
merchantConfigId: string
|
|
||||||
): Observable<{ success: boolean; message: string }> {
|
|
||||||
console.log('🗑️ DELETE Merchant de MerchantConfig seulement...');
|
|
||||||
|
|
||||||
return this.merchantConfigService.deleteMerchant(Number(merchantConfigId)).pipe(
|
|
||||||
map(() => ({
|
|
||||||
success: true,
|
|
||||||
message: 'Merchant supprimé de MerchantConfig avec succès'
|
|
||||||
})),
|
|
||||||
catchError(async (error) => ({
|
|
||||||
success: false,
|
|
||||||
message: `Erreur suppression merchant: ${error.message}`
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DELETE - Supprimer un utilisateur de Keycloak
|
|
||||||
*/
|
|
||||||
deleteKeycloakUser(
|
|
||||||
userId: string
|
|
||||||
): Observable<{ success: boolean; message: string }> {
|
|
||||||
console.log('🗑️ DELETE User de Keycloak...');
|
|
||||||
|
|
||||||
return this.getUserById(userId).pipe(
|
|
||||||
switchMap(user => {
|
|
||||||
const deleteService = this.merchantUsersService.deleteMerchantUser(userId);
|
|
||||||
|
|
||||||
return deleteService.pipe(
|
|
||||||
map(() => ({
|
|
||||||
success: true,
|
|
||||||
message: 'Utilisateur supprimé de Keycloak avec succès'
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
catchError(async (error) => ({
|
|
||||||
success: false,
|
|
||||||
message: `Erreur suppression utilisateur: ${error.message}`
|
|
||||||
}))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== MÉTHODES DE RECHERCHE ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rechercher des merchants dans MerchantConfig
|
|
||||||
*/
|
|
||||||
searchMerchantsInConfig(query: string): Observable<Merchant[]> {
|
|
||||||
console.log('🔍 SEARCH Merchants dans MerchantConfig...');
|
|
||||||
|
|
||||||
return this.merchantConfigService.getAllMerchants({ query }).pipe(
|
|
||||||
map(merchants => {
|
|
||||||
console.log(`✅ ${merchants.length} merchants trouvés avec "${query}"`);
|
|
||||||
return merchants;
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Erreur recherche merchants dans MerchantConfig:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rechercher des utilisateurs dans Keycloak
|
|
||||||
*/
|
|
||||||
searchKeycloakUsers(query: string): Observable<User[]> {
|
|
||||||
console.log('🔍 SEARCH Users dans Keycloak...');
|
|
||||||
|
|
||||||
// Rechercher dans les deux types d'utilisateurs
|
|
||||||
return forkJoin({
|
|
||||||
merchantUsers: this.merchantUsersService.searchMerchantUsers({ query }).pipe(
|
|
||||||
catchError(() => of([]))
|
|
||||||
)
|
|
||||||
}).pipe(
|
|
||||||
map(({ merchantUsers }) => {
|
|
||||||
const allUsers = [
|
|
||||||
...merchantUsers
|
|
||||||
];
|
|
||||||
console.log(`✅ ${allUsers.length} utilisateurs trouvés avec "${query}"`);
|
|
||||||
return allUsers;
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error('❌ Erreur recherche utilisateurs:', error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== MÉTHODES PRIVÉES ====================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Récupérer un utilisateur par ID (gère les deux types)
|
|
||||||
*/
|
|
||||||
private getUserById(userId: string): Observable<User> {
|
|
||||||
return forkJoin({
|
|
||||||
merchantUser: this.merchantUsersService.getMerchantUserById(userId).pipe(
|
|
||||||
catchError(() => of(null))
|
|
||||||
)
|
|
||||||
}).pipe(
|
|
||||||
map(({ merchantUser }) => {
|
|
||||||
if (merchantUser) return merchantUser;
|
|
||||||
throw new Error(`Utilisateur ${userId} non trouvé`);
|
|
||||||
}),
|
|
||||||
catchError(error => {
|
|
||||||
console.error(`❌ Erreur récupération utilisateur ${userId}:`, error);
|
|
||||||
return throwError(() => error);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si le rôle est un rôle partenaire
|
|
||||||
*/
|
|
||||||
private isMerchantRole(role: UserRole): boolean {
|
|
||||||
return [
|
|
||||||
UserRole.DCB_PARTNER_ADMIN,
|
|
||||||
UserRole.DCB_PARTNER_MANAGER,
|
|
||||||
UserRole.DCB_PARTNER_SUPPORT
|
|
||||||
].includes(role);
|
|
||||||
}
|
|
||||||
|
|
||||||
private mapToMerchantConfigRole(keycloakRole: UserRole): UserRole {
|
|
||||||
const mappedRole = this.ROLE_MAPPING.get(keycloakRole);
|
|
||||||
return mappedRole || UserRole.MERCHANT_CONFIG_VIEWER;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common';
|
|||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { NgIcon } from '@ng-icons/core';
|
import { NgIcon } from '@ng-icons/core';
|
||||||
import { NgbNavModule, NgbModal, NgbModalModule } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbNavModule, NgbModal, NgbModalModule } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import { catchError, map, of, Subject, switchMap, takeUntil } from 'rxjs';
|
import { catchError, map, of, Subject, switchMap, takeUntil, throwError } from 'rxjs';
|
||||||
|
|
||||||
import { MerchantUsersService } from './merchant-users.service';
|
import { MerchantUsersService } from './merchant-users.service';
|
||||||
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
||||||
@ -346,23 +346,49 @@ export class MerchantUsersManagement implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private loadAllMerchants(): void {
|
private loadAllMerchants(): void {
|
||||||
|
if (this.isMerchantUser) {
|
||||||
|
console.log('⚠️ User is not a Hub user, merchant list not displayed');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.loadingMerchantPartners = true;
|
this.loadingMerchantPartners = true;
|
||||||
this.merchantPartnersError = '';
|
this.merchantPartnersError = '';
|
||||||
|
this.merchantPartners = [];
|
||||||
|
|
||||||
this.merchantConfigService.fetchAllMerchants()
|
const pageSize = 10; // batch size identique au backend
|
||||||
|
let currentPage = 1;
|
||||||
|
|
||||||
|
const loadNextPage = () => {
|
||||||
|
this.merchantConfigService.getAllMerchants(currentPage, pageSize)
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: (merchants) => {
|
next: response => {
|
||||||
this.merchantPartners = merchants;
|
const items = Array.isArray(response.items) ? response.items : [];
|
||||||
|
this.merchantPartners.push(...items);
|
||||||
|
|
||||||
|
const loadedCount = this.merchantPartners.length;
|
||||||
|
const totalItems = response.total;
|
||||||
|
|
||||||
|
console.log(`📥 Page ${currentPage} chargée (${loadedCount}/${totalItems} merchants)`);
|
||||||
|
|
||||||
|
if (loadedCount < totalItems) {
|
||||||
|
currentPage++;
|
||||||
|
loadNextPage();
|
||||||
|
} else {
|
||||||
this.loadingMerchantPartners = false;
|
this.loadingMerchantPartners = false;
|
||||||
console.log('✅ All merchants loaded for Hub Admin:', merchants.length);
|
console.log(`✅ Tous les merchants chargés: ${loadedCount}`);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
error: (error) => {
|
error: err => {
|
||||||
console.error('❌ Error loading all merchants:', error);
|
console.error('❌ Error loading merchants:', err);
|
||||||
this.merchantPartnersError = 'Erreur lors du chargement des merchants';
|
this.merchantPartnersError = 'Erreur lors du chargement des merchants';
|
||||||
this.loadingMerchantPartners = false;
|
this.loadingMerchantPartners = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
loadNextPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
getCurrentUserMerchantName(): string {
|
getCurrentUserMerchantName(): string {
|
||||||
@ -593,7 +619,6 @@ export class MerchantUsersManagement implements OnInit, OnDestroy {
|
|||||||
merchantPartnerId: merchantPartnerId
|
merchantPartnerId: merchantPartnerId
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('📤 Adding user to merchant config:', addUserDto);
|
|
||||||
return this.merchantConfigService.addUserToMerchant(addUserDto).pipe(
|
return this.merchantConfigService.addUserToMerchant(addUserDto).pipe(
|
||||||
map((merchantConfigUser) => {
|
map((merchantConfigUser) => {
|
||||||
return {
|
return {
|
||||||
@ -608,15 +633,16 @@ export class MerchantUsersManagement implements OnInit, OnDestroy {
|
|||||||
// ROLLBACK: Supprimer l'utilisateur Keycloak créé
|
// ROLLBACK: Supprimer l'utilisateur Keycloak créé
|
||||||
if (createdUserId) {
|
if (createdUserId) {
|
||||||
console.log(`🔄 Rollback: Deleting Keycloak user ${createdUserId} because merchant association failed`);
|
console.log(`🔄 Rollback: Deleting Keycloak user ${createdUserId} because merchant association failed`);
|
||||||
|
|
||||||
return this.merchantUsersService.deleteMerchantUser(createdUserId).pipe(
|
return this.merchantUsersService.deleteMerchantUser(createdUserId).pipe(
|
||||||
map(() => {
|
switchMap(() => {
|
||||||
console.log(`✅ Keycloak user ${createdUserId} deleted as part of rollback`);
|
console.log(`✅ Keycloak user ${createdUserId} deleted as part of rollback`);
|
||||||
throw new Error(`Failed to associate user with merchant: ${merchantError.message}. User creation rolled back.`);
|
// On propage l'erreur originale en créant un Observable qui échoue
|
||||||
|
return throwError(() => new Error(`Failed to associate user with merchant: ${merchantError.message}. User creation rolled back.`));
|
||||||
}),
|
}),
|
||||||
catchError((deleteError) => {
|
catchError((deleteError) => {
|
||||||
console.error(`❌ Failed to delete Keycloak user during rollback:`, deleteError);
|
console.error(`❌ Failed to delete Keycloak user during rollback:`, deleteError);
|
||||||
// Même si le rollback échoue, on propage l'erreur originale avec info supplémentaire
|
return throwError(() => new Error(`Failed to associate user with merchant: ${merchantError.message}. AND failed to rollback user creation: ${deleteError.message}`));
|
||||||
throw new Error(`Failed to associate user with merchant: ${merchantError.message}. AND failed to rollback user creation: ${deleteError.message}`);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -334,7 +334,7 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
@if (totalPages > 1) {
|
@if (totalPages >= 1) {
|
||||||
<div class="d-flex justify-content-between align-items-center mt-3">
|
<div class="d-flex justify-content-between align-items-center mt-3">
|
||||||
<div class="text-muted">
|
<div class="text-muted">
|
||||||
Affichage de {{ getStartIndex() }}
|
Affichage de {{ getStartIndex() }}
|
||||||
|
|||||||
@ -154,7 +154,7 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
loadMerchants() {
|
loadMerchants(): void {
|
||||||
if (!this.isHubUser) {
|
if (!this.isHubUser) {
|
||||||
console.log('⚠️ User is not a Hub user, merchant list not displayed');
|
console.log('⚠️ User is not a Hub user, merchant list not displayed');
|
||||||
return;
|
return;
|
||||||
@ -163,15 +163,14 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.error = '';
|
this.error = '';
|
||||||
|
|
||||||
this.merchantConfigService.getMerchants(
|
const params = this.buildSearchParams();
|
||||||
this.currentPage,
|
const skip = (this.currentPage - 1) * this.itemsPerPage;
|
||||||
this.itemsPerPage,
|
|
||||||
this.buildSearchParams()
|
this.merchantConfigService.getAllMerchants(this.currentPage, this.itemsPerPage, params)
|
||||||
)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
catchError(error => {
|
catchError(error => {
|
||||||
console.error('Error loading merchants:', error);
|
console.error('❌ Error loading merchants:', error);
|
||||||
this.error = 'Erreur lors du chargement des marchands';
|
this.error = 'Erreur lors du chargement des marchands';
|
||||||
return of({
|
return of({
|
||||||
items: [],
|
items: [],
|
||||||
@ -182,37 +181,26 @@ export class MerchantConfigsList implements OnInit, OnDestroy {
|
|||||||
} as PaginatedResponse<Merchant>);
|
} as PaginatedResponse<Merchant>);
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.subscribe({
|
.subscribe(response => {
|
||||||
next: (response) => {
|
|
||||||
console.log('📊 Pagination response:', {
|
|
||||||
page: response.page,
|
|
||||||
total: response.total,
|
|
||||||
totalPages: response.totalPages,
|
|
||||||
itemsCount: response.items?.length,
|
|
||||||
limit: response.limit
|
|
||||||
});
|
|
||||||
|
|
||||||
this.allMerchants = response.items || [];
|
this.allMerchants = response.items || [];
|
||||||
this.displayedMerchants = response.items || [];
|
this.displayedMerchants = response.items || [];
|
||||||
this.totalItems = response.total || 0;
|
this.totalItems = response.total || 0;
|
||||||
this.totalPages = response.totalPages || 0;
|
this.totalPages = response.totalPages || Math.ceil((response.total || 0) / this.itemsPerPage);
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.cdRef.detectChanges();
|
this.cdRef.detectChanges();
|
||||||
},
|
|
||||||
error: () => {
|
console.log('📊 Pagination response:', {
|
||||||
this.error = 'Erreur lors du chargement des marchands';
|
page: response.page,
|
||||||
this.loading = false;
|
total: response.total,
|
||||||
this.allMerchants = [];
|
totalPages: this.totalPages,
|
||||||
this.displayedMerchants = [];
|
itemsCount: response.items?.length,
|
||||||
this.totalItems = 0;
|
limit: response.limit
|
||||||
this.totalPages = 0;
|
});
|
||||||
this.cdRef.detectChanges();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== AFFICHAGE DU LOGO ====================
|
// ==================== AFFICHAGE DU LOGO ====================
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -61,198 +61,42 @@ export class MerchantConfigService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getMerchants(page: number = 1, limit: number = 10, params?: SearchMerchantsParams): Observable<PaginatedResponse<Merchant>> {
|
/**
|
||||||
// Vérifier si le cache est valide
|
* Récupère tous les merchants (optionnel: avec recherche)
|
||||||
const paramsChanged = !this.areParamsEqual(params, this.cacheParams);
|
*/
|
||||||
|
getAllMerchants(
|
||||||
|
page: number = 1,
|
||||||
|
limit: number = 10,
|
||||||
|
params?: SearchMerchantsParams
|
||||||
|
): Observable<PaginatedResponse<Merchant>> {
|
||||||
|
const skip = (page - 1) * limit;
|
||||||
|
|
||||||
if (paramsChanged || this.merchantsCache.length === 0) {
|
|
||||||
// Nouvelle requête nécessaire
|
|
||||||
return this.fetchAllMerchants(params).pipe(
|
|
||||||
map(allMerchants => {
|
|
||||||
// Mettre en cache
|
|
||||||
this.merchantsCache = allMerchants;
|
|
||||||
this.cacheParams = params;
|
|
||||||
|
|
||||||
// Appliquer pagination
|
|
||||||
return this.applyPagination(allMerchants, page, limit);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Vérifier si le cache contient assez d'éléments pour la page demandée
|
|
||||||
const neededItems = page * limit;
|
|
||||||
|
|
||||||
if (neededItems <= this.merchantsCache.length) {
|
|
||||||
// Cache suffisant
|
|
||||||
return of(this.applyPagination(this.merchantsCache, page, limit));
|
|
||||||
} else {
|
|
||||||
// Besoin de plus d'éléments
|
|
||||||
const additionalNeeded = neededItems - this.merchantsCache.length;
|
|
||||||
const newTake = this.calculateOptimalTake(additionalNeeded, page, limit);
|
|
||||||
|
|
||||||
console.log(`🔄 Cache insufficient (${this.merchantsCache.length}), fetching ${newTake} more items`);
|
|
||||||
|
|
||||||
return this.fetchAdditionalMerchants(newTake, params).pipe(
|
|
||||||
map(additionalMerchants => {
|
|
||||||
// Fusionner avec le cache (éviter les doublons)
|
|
||||||
const merged = this.mergeMerchants(this.merchantsCache, additionalMerchants);
|
|
||||||
this.merchantsCache = merged;
|
|
||||||
|
|
||||||
return this.applyPagination(merged, page, limit);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculer le take optimal en fonction du total
|
|
||||||
private calculateOptimalTake(totalCount: number, page: number, limit: number): number {
|
|
||||||
// Calculer combien d'éléments nous avons besoin pour la pagination
|
|
||||||
const neededItems = page * limit;
|
|
||||||
|
|
||||||
// Ajouter un buffer pour éviter les appels fréquents
|
|
||||||
const buffer = Math.max(limit * 2, 100);
|
|
||||||
|
|
||||||
// Calculer le take nécessaire
|
|
||||||
let optimalTake = neededItems + buffer;
|
|
||||||
|
|
||||||
// Si le total est connu, adapter le take
|
|
||||||
if (totalCount > 0) {
|
|
||||||
// Prendre soit ce dont on a besoin, soit le total (le plus petit)
|
|
||||||
optimalTake = Math.min(optimalTake, totalCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arrondir aux paliers optimaux
|
|
||||||
return this.roundToOptimalValue(optimalTake);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Arrondir à des valeurs optimales (100, 500, 1000, etc.)
|
|
||||||
private roundToOptimalValue(value: number): number {
|
|
||||||
if (value <= 100) return 100;
|
|
||||||
if (value <= 500) return 500;
|
|
||||||
if (value <= 1000) return 1000;
|
|
||||||
if (value <= 2000) return 2000;
|
|
||||||
if (value <= 5000) return 5000;
|
|
||||||
if (value <= 10000) return 10000;
|
|
||||||
|
|
||||||
// Pour les très grands nombres, arrondir au multiple de 10000 supérieur
|
|
||||||
return Math.ceil(value / 10000) * 10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetchAllMerchants(params?: SearchMerchantsParams): Observable<Merchant[]> {
|
|
||||||
// Commencer avec un take raisonnable
|
|
||||||
const initialTake = 500;
|
|
||||||
|
|
||||||
return this.fetchMerchantsWithParams(initialTake, params).pipe(
|
|
||||||
switchMap(initialBatch => {
|
|
||||||
// Si nous avons récupéré moins que demandé, c'est probablement tout
|
|
||||||
if (initialBatch.length < initialTake) {
|
|
||||||
console.log(`✅ Retrieved all ${initialBatch.length} merchants`);
|
|
||||||
return of(initialBatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sinon, peut-être qu'il y a plus, essayer un take plus grand
|
|
||||||
console.log(`⚠️ Initial batch size (${initialBatch.length}) equals take, might be more`);
|
|
||||||
|
|
||||||
const largerTake = 2000;
|
|
||||||
return this.fetchMerchantsWithParams(largerTake, params).pipe(
|
|
||||||
map(largerBatch => {
|
|
||||||
console.log(`✅ Retrieved ${largerBatch.length} merchants with larger take`);
|
|
||||||
return largerBatch;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private fetchMerchantsWithParams(take: number, params?: SearchMerchantsParams): Observable<Merchant[]> {
|
|
||||||
let httpParams = new HttpParams().set('take', take.toString());
|
|
||||||
|
|
||||||
if (params?.query) {
|
|
||||||
httpParams = httpParams.set('query', params.query.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log(`📥 Fetching ${take} merchants`);
|
|
||||||
|
|
||||||
return this.http.get<ApiMerchant[]>(this.baseApiUrl, {
|
|
||||||
params: httpParams
|
|
||||||
}).pipe(
|
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
|
||||||
map(apiMerchants =>
|
|
||||||
apiMerchants.map(merchant =>
|
|
||||||
this.dataAdapter.convertApiMerchantToFrontend(merchant)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private fetchAdditionalMerchants(take: number, params?: SearchMerchantsParams): Observable<Merchant[]> {
|
|
||||||
// Prendre à partir de la fin du cache
|
|
||||||
const skip = this.merchantsCache.length;
|
|
||||||
let httpParams = new HttpParams()
|
let httpParams = new HttpParams()
|
||||||
.set('take', take.toString())
|
.set('skip', skip.toString())
|
||||||
.set('skip', skip.toString());
|
.set('take', limit.toString());
|
||||||
|
|
||||||
if (params?.query) {
|
if (params?.query) {
|
||||||
httpParams = httpParams.set('query', params.query.trim());
|
httpParams = httpParams.set('query', params.query.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`📥 Fetching additional ${take} merchants (skip: ${skip})`);
|
return this.http.get<{ items: ApiMerchant[], total: number }>(this.baseApiUrl, { params: httpParams })
|
||||||
|
.pipe(
|
||||||
return this.http.get<ApiMerchant[]>(this.baseApiUrl, {
|
|
||||||
params: httpParams
|
|
||||||
}).pipe(
|
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
timeout(this.REQUEST_TIMEOUT),
|
||||||
map(apiMerchants =>
|
map(response => {
|
||||||
apiMerchants.map(merchant =>
|
// Sécuriser le mapping, même si items est undefined
|
||||||
this.dataAdapter.convertApiMerchantToFrontend(merchant)
|
const itemsArray: ApiMerchant[] = Array.isArray(response?.items) ? response.items : [];
|
||||||
)
|
const merchants: Merchant[] = itemsArray.map(m => this.dataAdapter.convertApiMerchantToFrontend(m));
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private mergeMerchants(existing: Merchant[], newOnes: Merchant[]): Merchant[] {
|
const total: number = typeof response?.total === 'number' ? response.total : merchants.length;
|
||||||
const existingIds = new Set(existing.map(m => m.id));
|
|
||||||
const uniqueNewOnes = newOnes.filter(m => !existingIds.has(m.id));
|
|
||||||
return [...existing, ...uniqueNewOnes];
|
|
||||||
}
|
|
||||||
|
|
||||||
private applyPagination(merchants: Merchant[], page: number, limit: number): PaginatedResponse<Merchant> {
|
|
||||||
const total = merchants.length;
|
|
||||||
const totalPages = Math.ceil(total / limit);
|
|
||||||
|
|
||||||
const startIndex = (page - 1) * limit;
|
|
||||||
const endIndex = Math.min(startIndex + limit, total);
|
|
||||||
const paginatedItems = merchants.slice(startIndex, endIndex);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
items: paginatedItems,
|
items: merchants,
|
||||||
total: total,
|
total,
|
||||||
page: page,
|
page,
|
||||||
limit: limit,
|
limit,
|
||||||
totalPages: totalPages
|
totalPages: Math.ceil(total / limit)
|
||||||
};
|
};
|
||||||
}
|
}),
|
||||||
|
|
||||||
private areParamsEqual(a?: SearchMerchantsParams, b?: SearchMerchantsParams): boolean {
|
|
||||||
if (!a && !b) return true;
|
|
||||||
if (!a || !b) return false;
|
|
||||||
return a.query === b.query;
|
|
||||||
}
|
|
||||||
|
|
||||||
getAllMerchants(params?: SearchMerchantsParams): Observable<Merchant[]> {
|
|
||||||
let httpParams = new HttpParams();
|
|
||||||
|
|
||||||
if (params?.query) {
|
|
||||||
httpParams = httpParams.set('query', params.query.trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.http.get<ApiMerchant[]>(this.baseApiUrl, { params: httpParams }).pipe(
|
|
||||||
timeout(this.REQUEST_TIMEOUT),
|
|
||||||
map(apiMerchants =>
|
|
||||||
apiMerchants.map(merchant =>
|
|
||||||
this.dataAdapter.convertApiMerchantToFrontend(merchant)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
catchError(error => this.handleError('getAllMerchants', error))
|
catchError(error => this.handleError('getAllMerchants', error))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,6 @@ import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
|
|||||||
import { MerchantConfigService } from './merchant-config.service';
|
import { MerchantConfigService } from './merchant-config.service';
|
||||||
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
import { RoleManagementService } from '@core/services/hub-users-roles-management.service';
|
||||||
import { AuthService } from '@core/services/auth.service';
|
import { AuthService } from '@core/services/auth.service';
|
||||||
import { MerchantSyncService } from '../hub-users-management/merchant-sync-orchestrator.service';
|
|
||||||
import { PageTitle } from '@app/components/page-title/page-title';
|
import { PageTitle } from '@app/components/page-title/page-title';
|
||||||
import { MerchantConfigsList } from './merchant-config-list/merchant-config-list';
|
import { MerchantConfigsList } from './merchant-config-list/merchant-config-list';
|
||||||
import { MerchantConfigView } from './merchant-config-view/merchant-config-view';
|
import { MerchantConfigView } from './merchant-config-view/merchant-config-view';
|
||||||
@ -26,7 +25,7 @@ import {
|
|||||||
MerchantConfig,
|
MerchantConfig,
|
||||||
TechnicalContact
|
TechnicalContact
|
||||||
} from '@core/models/merchant-config.model';
|
} from '@core/models/merchant-config.model';
|
||||||
import { UserRole, UserType, PaginatedUserResponse, User } from '@core/models/dcb-bo-hub-user.model';
|
import { UserRole, UserType, User } from '@core/models/dcb-bo-hub-user.model';
|
||||||
|
|
||||||
import { MerchantDataAdapter } from './merchant-data-adapter.service';
|
import { MerchantDataAdapter } from './merchant-data-adapter.service';
|
||||||
|
|
||||||
|
|||||||
@ -16,7 +16,6 @@ import { WebhooksStatus } from '@modules/webhooks/status/status';
|
|||||||
import { WebhooksRetry } from '@modules/webhooks/retry/retry';
|
import { WebhooksRetry } from '@modules/webhooks/retry/retry';
|
||||||
import { Settings } from '@modules/settings/settings';
|
import { Settings } from '@modules/settings/settings';
|
||||||
import { Integrations } from '@modules/integrations/integrations';
|
import { Integrations } from '@modules/integrations/integrations';
|
||||||
import { Support } from '@modules/support/support';
|
|
||||||
import { MyProfile } from '@modules/profile/profile';
|
import { MyProfile } from '@modules/profile/profile';
|
||||||
import { Documentation } from '@modules/documentation/documentation';
|
import { Documentation } from '@modules/documentation/documentation';
|
||||||
import { Help } from '@modules/help/help';
|
import { Help } from '@modules/help/help';
|
||||||
@ -233,17 +232,8 @@ const routes: Routes = [
|
|||||||
},
|
},
|
||||||
|
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
// Support & Profile (Tous les utilisateurs authentifiés)
|
// Profile (Tous les utilisateurs authentifiés)
|
||||||
// ---------------------------
|
// ---------------------------
|
||||||
{
|
|
||||||
path: 'support',
|
|
||||||
component: Support,
|
|
||||||
canActivate: [authGuard, roleGuard],
|
|
||||||
data: {
|
|
||||||
title: 'Support',
|
|
||||||
module: 'support'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'profile',
|
path: 'profile',
|
||||||
component: MyProfile,
|
component: MyProfile,
|
||||||
|
|||||||
@ -1,628 +0,0 @@
|
|||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { MerchantSyncService } from '../hub-users-management/merchant-sync-orchestrator.service';
|
|
||||||
import { UserRole } from '@core/models/dcb-bo-hub-user.model';
|
|
||||||
import { firstValueFrom } from 'rxjs';
|
|
||||||
import { MerchantUsersService } from '@modules/hub-users-management/merchant-users.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-support',
|
|
||||||
templateUrl: './support.html'
|
|
||||||
})
|
|
||||||
export class Support implements OnInit {
|
|
||||||
|
|
||||||
private testData = {
|
|
||||||
merchantConfigId: '',
|
|
||||||
keycloakUserId: '',
|
|
||||||
testUserId: '',
|
|
||||||
testMerchantConfigUserId: '',
|
|
||||||
associatedUserId: '' // ID d'un utilisateur associé au marchand
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private merchantCrudService: MerchantSyncService,
|
|
||||||
private merchantUsersService: MerchantUsersService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnInit() {
|
|
||||||
console.log('🚀 Support Component - Tests CRUD Merchants');
|
|
||||||
console.log('='.repeat(60));
|
|
||||||
console.log('📌 NOUVELLE LOGIQUE: Merchant et User indépendants, association séparée');
|
|
||||||
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('📌 NOUVELLE LOGIQUE: Association séparée');
|
|
||||||
console.log('='.repeat(50));
|
|
||||||
|
|
||||||
// Test 1: Création indépendante
|
|
||||||
await this.testCreateOperations();
|
|
||||||
|
|
||||||
// Test 2: Association
|
|
||||||
await this.testAssociationOperations();
|
|
||||||
|
|
||||||
// Test 3: Lecture
|
|
||||||
await this.testReadOperations();
|
|
||||||
|
|
||||||
// Test 4: Mise à jour
|
|
||||||
await this.testUpdateOperations();
|
|
||||||
|
|
||||||
// Test 5: Gestion utilisateurs
|
|
||||||
await this.testUserOperations();
|
|
||||||
|
|
||||||
// Test 6: 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 indépendante
|
|
||||||
*/
|
|
||||||
private async testCreateOperations(): Promise<void> {
|
|
||||||
console.log('\n📝 TEST 1: Opérations CREATE INDÉPENDANTES');
|
|
||||||
console.log('-'.repeat(40));
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 1.1 Créer un merchant uniquement dans MerchantConfig
|
|
||||||
console.log('1.1 Création d\'un merchant dans MerchantConfig seulement...');
|
|
||||||
|
|
||||||
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`
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('📤 Données merchant pour MerchantConfig:', merchantData);
|
|
||||||
|
|
||||||
const merchantConfigResult = await firstValueFrom(
|
|
||||||
this.merchantCrudService.createMerchantInConfigOnly(merchantData)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.testData.merchantConfigId = String(merchantConfigResult.id!);
|
|
||||||
|
|
||||||
console.log('✅ Merchant créé dans MerchantConfig uniquement!');
|
|
||||||
console.log(' Merchant Config ID:', this.testData.merchantConfigId);
|
|
||||||
console.log(' Merchant name:', merchantConfigResult.name);
|
|
||||||
|
|
||||||
// 1.2 Créer un utilisateur Hub dans Keycloak
|
|
||||||
console.log('\n1.2 Création d\'un utilisateur Hub dans Keycloak...');
|
|
||||||
|
|
||||||
const hubUserData = {
|
|
||||||
username: `testhubuser.${Date.now()}`,
|
|
||||||
email: `hubuser.${Date.now()}@example.com`,
|
|
||||||
password: 'HubPassword123!',
|
|
||||||
firstName: 'Test',
|
|
||||||
lastName: 'HubUser',
|
|
||||||
role: UserRole.DCB_SUPPORT
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('👤 Données Hub User pour Keycloak:', { ...hubUserData, password: '***' });
|
|
||||||
|
|
||||||
const hubUserResult = await firstValueFrom(
|
|
||||||
this.merchantCrudService.createKeycloakUser(hubUserData)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('✅ Utilisateur Hub créé dans Keycloak:');
|
|
||||||
console.log(' Keycloak User ID:', hubUserResult.id);
|
|
||||||
console.log(' Email:', hubUserResult.email);
|
|
||||||
console.log(' Rôle:', hubUserResult.role);
|
|
||||||
console.log(' User Type:', hubUserResult.userType);
|
|
||||||
|
|
||||||
// 1.3 Créer un utilisateur Partenaire dans Keycloak
|
|
||||||
console.log('\n1.3 Création d\'un utilisateur Partenaire dans Keycloak...');
|
|
||||||
|
|
||||||
const partnerUserData = {
|
|
||||||
username: `testpartner.${Date.now()}`,
|
|
||||||
email: `partner.${Date.now()}@example.com`,
|
|
||||||
password: 'PartnerPassword123!',
|
|
||||||
firstName: 'Test',
|
|
||||||
lastName: 'Partner',
|
|
||||||
role: UserRole.DCB_PARTNER_ADMIN
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('👤 Données Partner User pour Keycloak:', { ...partnerUserData, password: '***' });
|
|
||||||
|
|
||||||
const partnerUserResult = await firstValueFrom(
|
|
||||||
this.merchantCrudService.createKeycloakUser(partnerUserData)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.testData.testUserId = partnerUserResult.id;
|
|
||||||
|
|
||||||
console.log('✅ Utilisateur Partenaire créé dans Keycloak:');
|
|
||||||
console.log(' Keycloak User ID:', this.testData.testUserId);
|
|
||||||
console.log(' Email:', partnerUserResult.email);
|
|
||||||
console.log(' Rôle:', partnerUserResult.role);
|
|
||||||
console.log(' User Type:', partnerUserResult.userType);
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ ERREUR lors de la création:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 2: Opérations d'association
|
|
||||||
*/
|
|
||||||
private async testAssociationOperations(): Promise<void> {
|
|
||||||
console.log('\n🔗 TEST 2: Opérations d\'ASSOCIATION');
|
|
||||||
console.log('-'.repeat(40));
|
|
||||||
|
|
||||||
if (!this.testData.merchantConfigId || !this.testData.testUserId) {
|
|
||||||
console.log('⚠️ Merchant ou utilisateur non créé, passage au test suivant');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 2.1 Associer l'utilisateur au marchand
|
|
||||||
console.log('2.1 Association de l\'utilisateur au marchand...');
|
|
||||||
|
|
||||||
const associationData = {
|
|
||||||
userId: this.testData.testUserId,
|
|
||||||
merchantConfigId: this.testData.merchantConfigId,
|
|
||||||
role: UserRole.MERCHANT_CONFIG_ADMIN
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('🔗 Données d\'association:', associationData);
|
|
||||||
|
|
||||||
const associationResult = await firstValueFrom(
|
|
||||||
this.merchantCrudService.associateUserToMerchant(associationData)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.testData.associatedUserId = associationResult.keycloakUser.id;
|
|
||||||
this.testData.testMerchantConfigUserId = String(associationResult.merchantConfigUser?.userId || '');
|
|
||||||
|
|
||||||
console.log('✅ Utilisateur associé au marchand:');
|
|
||||||
console.log(' Keycloak User ID:', associationResult.keycloakUser.id);
|
|
||||||
console.log(' Merchant Config User ID:', this.testData.testMerchantConfigUserId);
|
|
||||||
console.log(' Rôle dans MerchantConfig:', associationResult.merchantConfigUser?.role);
|
|
||||||
console.log(' Associé au merchant:', this.testData.merchantConfigId);
|
|
||||||
|
|
||||||
// 2.2 Récupérer les utilisateurs associés au marchand
|
|
||||||
console.log('\n2.2 Lecture des utilisateurs associés au marchand...');
|
|
||||||
|
|
||||||
const merchantUsers = await firstValueFrom(
|
|
||||||
this.merchantCrudService.getUsersByMerchant(this.testData.merchantConfigId)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`✅ ${merchantUsers.length} utilisateurs associés à ce merchant`);
|
|
||||||
|
|
||||||
merchantUsers.forEach((user: any, index: number) => {
|
|
||||||
console.log(` ${index + 1}. ${user.email || 'Inconnu'}`);
|
|
||||||
console.log(` ID: ${user.id}`);
|
|
||||||
console.log(` Rôle: ${user.role}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ ERREUR lors de l\'association:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 3: Opérations de lecture
|
|
||||||
*/
|
|
||||||
private async testReadOperations(): Promise<void> {
|
|
||||||
console.log('\n🔍 TEST 3: Opérations READ');
|
|
||||||
console.log('-'.repeat(40));
|
|
||||||
|
|
||||||
if (!this.testData.merchantConfigId) {
|
|
||||||
console.log('⚠️ Aucun merchant créé, passage au test suivant');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 3.1 Lire le merchant depuis MerchantConfig
|
|
||||||
console.log('3.1 Lecture du merchant depuis MerchantConfig...');
|
|
||||||
|
|
||||||
const merchant = await firstValueFrom(
|
|
||||||
this.merchantCrudService.getMerchantFromConfigOnly(
|
|
||||||
this.testData.merchantConfigId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('✅ Merchant récupéré depuis MerchantConfig:');
|
|
||||||
console.log(' ID:', merchant.id);
|
|
||||||
console.log(' Nom:', merchant.name);
|
|
||||||
console.log(' Adresse:', merchant.adresse);
|
|
||||||
console.log(' Configurations:', merchant.configs?.length || 0);
|
|
||||||
|
|
||||||
// 3.2 Lire tous les merchants depuis MerchantConfig
|
|
||||||
console.log('\n3.2 Lecture de tous les merchants depuis MerchantConfig...');
|
|
||||||
|
|
||||||
const allMerchants = await firstValueFrom(
|
|
||||||
this.merchantCrudService.getAllMerchantsFromConfig()
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`✅ ${allMerchants.length} merchants trouvés au total`);
|
|
||||||
|
|
||||||
if (allMerchants.length > 0) {
|
|
||||||
const lastMerchant = allMerchants[allMerchants.length - 1];
|
|
||||||
console.log(' Dernier merchant:', lastMerchant.name, '(ID:', lastMerchant.id, ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.3 Rechercher des merchants
|
|
||||||
console.log('\n3.3 Recherche de merchants dans MerchantConfig...');
|
|
||||||
|
|
||||||
const searchResults = await firstValueFrom(
|
|
||||||
this.merchantCrudService.searchMerchantsInConfig('Test')
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`✅ ${searchResults.length} merchants trouvés avec "Test"`);
|
|
||||||
|
|
||||||
if (searchResults.length > 0) {
|
|
||||||
console.log(' Premier résultat:', searchResults[0].name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.4 Rechercher des utilisateurs
|
|
||||||
console.log('\n3.4 Recherche d\'utilisateurs dans Keycloak...');
|
|
||||||
|
|
||||||
const userSearchResults = await firstValueFrom(
|
|
||||||
this.merchantCrudService.searchKeycloakUsers('test')
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`✅ ${userSearchResults.length} utilisateurs trouvés avec "test"`);
|
|
||||||
|
|
||||||
if (userSearchResults.length > 0) {
|
|
||||||
console.log(' Premier résultat:', userSearchResults[0].email);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ ERREUR lors de la lecture:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 4: Opérations de mise à jour
|
|
||||||
*/
|
|
||||||
private async testUpdateOperations(): Promise<void> {
|
|
||||||
console.log('\n✏️ TEST 4: Opérations UPDATE');
|
|
||||||
console.log('-'.repeat(40));
|
|
||||||
|
|
||||||
if (!this.testData.merchantConfigId) {
|
|
||||||
console.log('⚠️ Aucun merchant créé, passage au test suivant');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 4.1 Mettre à jour le merchant dans MerchantConfig
|
|
||||||
console.log('4.1 Mise à jour du merchant dans MerchantConfig...');
|
|
||||||
|
|
||||||
const newName = `Updated Merchant ${Date.now()}`;
|
|
||||||
|
|
||||||
const updateResult = await firstValueFrom(
|
|
||||||
this.merchantCrudService.updateMerchantInConfigOnly(
|
|
||||||
this.testData.merchantConfigId,
|
|
||||||
{
|
|
||||||
name: newName,
|
|
||||||
description: 'Mis à jour par les tests',
|
|
||||||
adresse: '456 Updated Street'
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('✅ Merchant mis à jour dans MerchantConfig:');
|
|
||||||
console.log(' Nouveau nom:', newName);
|
|
||||||
console.log(' Description:', updateResult.description);
|
|
||||||
|
|
||||||
// 4.3 Changer le rôle de l'utilisateur dans MerchantConfig
|
|
||||||
console.log('\n4.3 Changement de rôle utilisateur dans MerchantConfig...');
|
|
||||||
|
|
||||||
if (this.testData.testUserId && this.testData.merchantConfigId && this.testData.testMerchantConfigUserId) {
|
|
||||||
const roleUpdateResult = await firstValueFrom(
|
|
||||||
this.merchantCrudService.updateUserRoleInMerchantConfig(
|
|
||||||
this.testData.merchantConfigId,
|
|
||||||
this.testData.testUserId,
|
|
||||||
UserRole.MERCHANT_CONFIG_MANAGER
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('✅ Rôle utilisateur mis à jour dans MerchantConfig:');
|
|
||||||
console.log(' Nouveau rôle:', UserRole.MERCHANT_CONFIG_MANAGER);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ ERREUR lors de la mise à jour:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 5: Opérations utilisateurs avancées
|
|
||||||
*/
|
|
||||||
private async testUserOperations(): Promise<void> {
|
|
||||||
console.log('\n👥 TEST 5: Opérations utilisateurs avancées');
|
|
||||||
console.log('-'.repeat(40));
|
|
||||||
|
|
||||||
if (!this.testData.testUserId) {
|
|
||||||
console.log('⚠️ Aucun utilisateur créé, passage au test suivant');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 5.1 Réinitialiser le mot de passe
|
|
||||||
console.log('5.1 Réinitialisation mot de passe...');
|
|
||||||
|
|
||||||
const resetPasswordDto = {
|
|
||||||
newPassword: 'NewPassword123!',
|
|
||||||
temporary: false
|
|
||||||
};
|
|
||||||
|
|
||||||
const resetResult = await firstValueFrom(
|
|
||||||
this.merchantUsersService.resetMerchantUserPassword(
|
|
||||||
this.testData.testUserId,
|
|
||||||
resetPasswordDto
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('✅ Mot de passe réinitialisé:');
|
|
||||||
console.log(' Message:', resetResult.message);
|
|
||||||
|
|
||||||
// 5.2 Dissocier l'utilisateur du marchand
|
|
||||||
console.log('\n5.2 Dissociation de l\'utilisateur du marchand...');
|
|
||||||
|
|
||||||
if (this.testData.testUserId && this.testData.merchantConfigId) {
|
|
||||||
const dissociateResult = await firstValueFrom(
|
|
||||||
this.merchantCrudService.dissociateUserFromMerchant(
|
|
||||||
this.testData.testUserId,
|
|
||||||
this.testData.merchantConfigId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('✅ Utilisateur dissocié du marchand:');
|
|
||||||
console.log(' Succès:', dissociateResult.success);
|
|
||||||
console.log(' Message:', dissociateResult.message);
|
|
||||||
|
|
||||||
// Vérifier que l'utilisateur n'est plus associé
|
|
||||||
const userMerchants = await firstValueFrom(
|
|
||||||
this.merchantCrudService.getUsersByMerchant(this.testData.merchantConfigId)
|
|
||||||
);
|
|
||||||
|
|
||||||
const userStillLinked = userMerchants.some(
|
|
||||||
user => user.id === this.testData.testUserId
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(` Marchands associés après dissociation: ${userMerchants.length}`);
|
|
||||||
|
|
||||||
if (userStillLinked) {
|
|
||||||
console.error('❌ L’utilisateur est encore associé au marchand ! ❌');
|
|
||||||
} else {
|
|
||||||
console.log('✅ L’utilisateur a été correctement dissocié du marchand.');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5.3 Réassocier l'utilisateur (pour les tests suivants)
|
|
||||||
console.log('\n5.3 Réassociation de l\'utilisateur (pour les tests)...');
|
|
||||||
|
|
||||||
if (this.testData.testUserId && this.testData.merchantConfigId) {
|
|
||||||
const reassociationData = {
|
|
||||||
userId: this.testData.testUserId,
|
|
||||||
merchantConfigId: this.testData.merchantConfigId,
|
|
||||||
role: UserRole.MERCHANT_CONFIG_ADMIN
|
|
||||||
};
|
|
||||||
|
|
||||||
await firstValueFrom(
|
|
||||||
this.merchantCrudService.associateUserToMerchant(reassociationData)
|
|
||||||
);
|
|
||||||
console.log('✅ Utilisateur réassocié pour les tests suivants');
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ ERREUR opérations utilisateurs:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TEST 6: Opérations de suppression
|
|
||||||
*/
|
|
||||||
private async testDeleteOperations(): Promise<void> {
|
|
||||||
console.log('\n🗑️ TEST 6: Opérations DELETE');
|
|
||||||
console.log('-'.repeat(40));
|
|
||||||
|
|
||||||
if (!this.testData.merchantConfigId) {
|
|
||||||
console.log('⚠️ Aucun merchant créé, passage au test suivant');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 6.1 Dissocier avant suppression
|
|
||||||
console.log('6.1 Dissociation de l\'utilisateur avant suppression...');
|
|
||||||
|
|
||||||
if (this.testData.testUserId && this.testData.merchantConfigId) {
|
|
||||||
await firstValueFrom(
|
|
||||||
this.merchantCrudService.dissociateUserFromMerchant(
|
|
||||||
this.testData.testUserId,
|
|
||||||
this.testData.merchantConfigId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
console.log('✅ Utilisateur dissocié');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6.2 Supprimer l'utilisateur de Keycloak
|
|
||||||
console.log('\n6.2 Suppression de l\'utilisateur de Keycloak...');
|
|
||||||
|
|
||||||
if (this.testData.testUserId) {
|
|
||||||
const deleteUserResult = await firstValueFrom(
|
|
||||||
this.merchantCrudService.deleteKeycloakUser(
|
|
||||||
this.testData.testUserId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('✅ Utilisateur supprimé de Keycloak:');
|
|
||||||
console.log(' Succès:', deleteUserResult.success);
|
|
||||||
console.log(' Message:', deleteUserResult.message);
|
|
||||||
|
|
||||||
this.testData.testUserId = '';
|
|
||||||
this.testData.testMerchantConfigUserId = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6.3 Supprimer le merchant de MerchantConfig
|
|
||||||
console.log('\n6.3 Suppression du merchant de MerchantConfig...');
|
|
||||||
|
|
||||||
const deleteMerchantResult = await firstValueFrom(
|
|
||||||
this.merchantCrudService.deleteMerchantFromConfigOnly(
|
|
||||||
this.testData.merchantConfigId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('✅ Merchant supprimé de MerchantConfig:');
|
|
||||||
console.log(' Succès:', deleteMerchantResult.success);
|
|
||||||
console.log(' Message:', deleteMerchantResult.message);
|
|
||||||
|
|
||||||
// 6.4 Vérifier la suppression
|
|
||||||
console.log('\n6.4 Vérification de la suppression...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await firstValueFrom(
|
|
||||||
this.merchantCrudService.getMerchantFromConfigOnly(
|
|
||||||
this.testData.merchantConfigId
|
|
||||||
)
|
|
||||||
);
|
|
||||||
console.log('❌ Le merchant existe toujours dans MerchantConfig - PROBLÈME!');
|
|
||||||
} catch (error) {
|
|
||||||
console.log('✅ Le merchant a bien été supprimé de MerchantConfig');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Réinitialiser toutes les données
|
|
||||||
this.testData = {
|
|
||||||
merchantConfigId: '',
|
|
||||||
keycloakUserId: '',
|
|
||||||
testUserId: '',
|
|
||||||
testMerchantConfigUserId: '',
|
|
||||||
associatedUserId: ''
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log('🧹 Données de test réinitialisées');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ ERREUR lors de la suppression:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== MÉTHODES POUR TESTS INDIVIDUELS ====================
|
|
||||||
|
|
||||||
async testCreateOnly(): Promise<void> {
|
|
||||||
console.log('🧪 Test CREATE uniquement');
|
|
||||||
console.log('📌 Création indépendante');
|
|
||||||
await this.testCreateOperations();
|
|
||||||
}
|
|
||||||
|
|
||||||
async testAssociationOnly(): Promise<void> {
|
|
||||||
console.log('🧪 Test ASSOCIATION uniquement');
|
|
||||||
|
|
||||||
// Créer d'abord un merchant et un utilisateur si nécessaire
|
|
||||||
if (!this.testData.merchantConfigId) {
|
|
||||||
await this.createTestMerchant();
|
|
||||||
}
|
|
||||||
if (!this.testData.testUserId) {
|
|
||||||
await this.createTestUser();
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.testAssociationOperations();
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ==================== MÉTHODES UTILITAIRES ====================
|
|
||||||
|
|
||||||
private async createTestMerchant(): Promise<void> {
|
|
||||||
const merchantData = {
|
|
||||||
name: 'Test Merchant ' + Date.now(),
|
|
||||||
adresse: '123 Test Street',
|
|
||||||
phone: '+336' + Math.floor(10000000 + Math.random() * 90000000),
|
|
||||||
configs: [],
|
|
||||||
technicalContacts: []
|
|
||||||
};
|
|
||||||
|
|
||||||
const merchant = await firstValueFrom(
|
|
||||||
this.merchantCrudService.createMerchantInConfigOnly(merchantData)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.testData.merchantConfigId = String(merchant.id!);
|
|
||||||
console.log('✅ Merchant de test créé:', this.testData.merchantConfigId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createTestUser(): Promise<void> {
|
|
||||||
const userData = {
|
|
||||||
username: `testuser.${Date.now()}`,
|
|
||||||
email: `user.${Date.now()}@example.com`,
|
|
||||||
password: 'TestPassword123!',
|
|
||||||
firstName: 'Test',
|
|
||||||
lastName: 'User',
|
|
||||||
role: UserRole.DCB_PARTNER_ADMIN
|
|
||||||
};
|
|
||||||
|
|
||||||
const user = await firstValueFrom(
|
|
||||||
this.merchantCrudService.createKeycloakUser(userData)
|
|
||||||
);
|
|
||||||
|
|
||||||
this.testData.testUserId = user.id;
|
|
||||||
console.log('✅ Utilisateur de test créé:', this.testData.testUserId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Afficher l'état actuel des tests
|
|
||||||
*/
|
|
||||||
showTestStatus(): void {
|
|
||||||
console.log('\n📊 ÉTAT ACTUEL DES TESTS');
|
|
||||||
console.log('📌 NOUVELLE LOGIQUE: Association séparée');
|
|
||||||
console.log('-'.repeat(30));
|
|
||||||
console.log('Merchant Config ID:', this.testData.merchantConfigId || 'Non créé');
|
|
||||||
console.log('Keycloak User ID:', this.testData.testUserId || 'Non créé');
|
|
||||||
console.log('Associated User ID:', this.testData.associatedUserId || 'Non associé');
|
|
||||||
console.log('Merchant Config User ID:', this.testData.testMerchantConfigUserId || 'Non créé');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Réinitialiser les données de test
|
|
||||||
*/
|
|
||||||
resetTestData(): void {
|
|
||||||
this.testData = {
|
|
||||||
merchantConfigId: '',
|
|
||||||
keycloakUserId: '',
|
|
||||||
testUserId: '',
|
|
||||||
testMerchantConfigUserId: '',
|
|
||||||
associatedUserId: ''
|
|
||||||
};
|
|
||||||
console.log('🧹 Données de test réinitialisées');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue
Block a user