From 00f665fef4f6273310616dd984546d512bbba40a Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 11 Jun 2024 13:39:54 +0530 Subject: [PATCH 1/4] feat: added deleted metadata function and table Signed-off-by: KulkarniShashank --- .../src/agent-service.controller.ts | 4 +- .../agent-service/src/agent-service.module.ts | 4 +- .../src/agent-service.service.ts | 106 +++++++++++------- .../repositories/agent-service.repository.ts | 11 +- .../organization/organization.controller.ts | 3 +- .../src/organization/organization.service.ts | 7 +- .../repositories/organization.repository.ts | 12 +- .../src/organization.controller.ts | 6 +- apps/organization/src/organization.module.ts | 2 +- apps/organization/src/organization.service.ts | 45 ++++++-- apps/user/interfaces/user.interface.ts | 12 ++ apps/user/repositories/user.repository.ts | 85 ++++++++------ apps/user/src/user.controller.ts | 7 +- apps/user/src/user.service.ts | 14 ++- libs/common/src/response-messages/index.ts | 2 +- libs/enum/src/enum.ts | 12 +- libs/http-exception.filter.ts | 51 +++++---- .../migration.sql | 8 ++ .../migration.sql | 5 + libs/prisma-service/prisma/schema.prisma | 32 +++--- libs/user-activity/repositories/index.ts | 48 +++++++- 21 files changed, 327 insertions(+), 149 deletions(-) create mode 100644 libs/prisma-service/prisma/migrations/20240610111750_added_email_user_org_delete_activity/migration.sql create mode 100644 libs/prisma-service/prisma/migrations/20240610142316_remove_unique_user_id_user_org_delete_activity/migration.sql diff --git a/apps/agent-service/src/agent-service.controller.ts b/apps/agent-service/src/agent-service.controller.ts index 736696771..2d0becf33 100644 --- a/apps/agent-service/src/agent-service.controller.ts +++ b/apps/agent-service/src/agent-service.controller.ts @@ -242,8 +242,8 @@ export class AgentServiceController { } @MessagePattern({ cmd: 'delete-wallet' }) - async deleteWallet(payload: { orgId }): Promise { - return this.agentServiceService.deleteWallet(payload.orgId); + async deleteWallet(payload: { orgId, user }): Promise { + return this.agentServiceService.deleteWallet(payload.orgId, payload.user); } @MessagePattern({ cmd: 'agent-receive-invitation-url' }) diff --git a/apps/agent-service/src/agent-service.module.ts b/apps/agent-service/src/agent-service.module.ts index 87bcb642a..413b8d1c3 100644 --- a/apps/agent-service/src/agent-service.module.ts +++ b/apps/agent-service/src/agent-service.module.ts @@ -10,6 +10,7 @@ import { ConnectionService } from 'apps/connection/src/connection.service'; import { ConnectionRepository } from 'apps/connection/src/connection.repository'; import { CacheModule } from '@nestjs/cache-manager'; import { getNatsOptions } from '@credebl/common/nats.config'; +import { UserActivityRepository } from 'libs/user-activity/repositories'; @Module({ imports: [ @@ -31,7 +32,8 @@ import { getNatsOptions } from '@credebl/common/nats.config'; PrismaService, Logger, ConnectionService, - ConnectionRepository + ConnectionRepository, + UserActivityRepository ], exports: [AgentServiceService, AgentServiceRepository, AgentServiceModule] }) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 3a72aafd7..57a6c78a4 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -54,9 +54,9 @@ import { IAgentConfigure, OrgDid } from './interface/agent-service.interface'; -import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType } from '@credebl/enum/enum'; +import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType, RecordType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; -import { Prisma, ledgers, org_agents, organisation, platform_config } from '@prisma/client'; +import { Prisma, ledgers, org_agents, organisation, platform_config, user } from '@prisma/client'; import { CommonConstants } from '@credebl/common/common.constant'; import { CommonService } from '@credebl/common'; import { GetSchemaAgentRedirection } from 'apps/ledger/src/schema/schema.interface'; @@ -72,6 +72,7 @@ import { IConnectionDetailsById } from 'apps/api-gateway/src/interfaces/IConnect import { ledgerName } from '@credebl/common/cast.helper'; import { InvitationMessage } from '@credebl/common/interfaces/agent-service.interface'; import * as CryptoJS from 'crypto-js'; +import { UserActivityRepository } from 'libs/user-activity/repositories'; @Injectable() @WebSocketGateway() @@ -83,7 +84,8 @@ export class AgentServiceService { private readonly commonService: CommonService, private readonly connectionService: ConnectionService, @Inject('NATS_CLIENT') private readonly agentServiceProxy: ClientProxy, - @Inject(CACHE_MANAGER) private cacheService: Cache + @Inject(CACHE_MANAGER) private cacheService: Cache, + private readonly userActivityRepository: UserActivityRepository ) {} async ReplaceAt(input, search, replace, start, end): Promise { @@ -953,8 +955,8 @@ export class AgentServiceService { const createdDidDetails = { orgId, - did: didDetails?.['did'] ?? didDetails?.["didState"]?.["did"], - didDocument: didDetails?.['didDocument'] ?? didDetails?.['didDoc'] ?? didDetails?.["didState"]?.["didDocument"], + did: didDetails?.['did'] ?? didDetails?.['didState']?.['did'], + didDocument: didDetails?.['didDocument'] ?? didDetails?.['didDoc'] ?? didDetails?.['didState']?.['didDocument'], isPrimaryDid, orgAgentId: agentDetails.id, userId: user.id @@ -1021,9 +1023,7 @@ export class AgentServiceService { ); } - private async storeDid(createdDidDetails): Promise< - OrgDid - > { + private async storeDid(createdDidDetails): Promise { const storeDidDetails = await this.agentServiceRepository.storeDidDetails(createdDidDetails); if (!storeDidDetails) { @@ -1632,53 +1632,79 @@ export class AgentServiceService { } } - async deleteWallet(orgId: string): Promise { + async deleteWallet(orgId: string, user: user): Promise { try { - // Retrieve the API key and agent information - const [getApiKeyResult, orgAgentResult] = await Promise.allSettled([ - this.getOrgAgentApiKey(orgId), - this.agentServiceRepository.getAgentApiKey(orgId) - ]); - - if (getApiKeyResult.status === 'rejected') { - throw new InternalServerErrorException(`Failed to get API key: ${getApiKeyResult.reason}`); - } + // Retrieve the API key and agent information + const [getApiKeyResult, orgAgentResult] = await Promise.allSettled([ + this.getOrgAgentApiKey(orgId), + this.agentServiceRepository.getAgentApiKey(orgId) + ]); - if (orgAgentResult.status === 'rejected') { - throw new InternalServerErrorException(`Failed to get agent information: ${orgAgentResult.reason}`); - } + if (getApiKeyResult.status === 'rejected') { + throw new InternalServerErrorException(`Failed to get API key: ${getApiKeyResult.reason}`); + } - const getApiKey = getApiKeyResult?.value; - const orgAgent = orgAgentResult?.value; + if (orgAgentResult.status === 'rejected') { + throw new InternalServerErrorException(`Failed to get agent information: ${orgAgentResult.reason}`); + } - const orgAgentTypeResult = await this.agentServiceRepository.getOrgAgentType(orgAgent.orgAgentTypeId); + const getApiKey = getApiKeyResult?.value; + const orgAgent = orgAgentResult?.value; - if (!orgAgentTypeResult) { - throw new NotFoundException(ResponseMessages.agent.error.orgAgentNotFound); - } + const orgAgentTypeResult = await this.agentServiceRepository.getOrgAgentType(orgAgent.orgAgentTypeId); + + if (!orgAgentTypeResult) { + throw new NotFoundException(ResponseMessages.agent.error.orgAgentNotFound); + } - // Determine the URL based on the agent type - const url = orgAgentTypeResult.agent === OrgAgentType.SHARED - ? `${orgAgent.agentEndPoint}${CommonConstants.URL_SHAGENT_DELETE_SUB_WALLET}`.replace('#', orgAgent?.tenantId) - : `${orgAgent.agentEndPoint}${CommonConstants.URL_DELETE_WALLET}`; + // Determine the URL based on the agent type + const url = + orgAgentTypeResult.agent === OrgAgentType.SHARED + ? `${orgAgent.agentEndPoint}${CommonConstants.URL_SHAGENT_DELETE_SUB_WALLET}`.replace('#', orgAgent?.tenantId) + : `${orgAgent.agentEndPoint}${CommonConstants.URL_DELETE_WALLET}`; - // Make the HTTP DELETE request - const deleteWallet = await this.commonService.httpDelete(url, { - headers: { authorization: getApiKey } + // Make the HTTP DELETE request + const deleteWallet = await this.commonService + .httpDelete(url, { + headers: { authorization: getApiKey } }) .then(async (response) => response); - if (deleteWallet.status === 204) { - const deleteOrgAgent = await this.agentServiceRepository.deleteOrgAgentByOrg(orgId); - return deleteOrgAgent; + if (deleteWallet.status === 204) { + const {orgDid, agentInvitation, deleteOrgAgent} = await this.agentServiceRepository.deleteOrgAgentByOrg(orgId); + + this.logger.log(`orgDid :::: ${JSON.stringify(orgDid)}`); + this.logger.log(`agentInvitation :::: ${JSON.stringify(agentInvitation)}`); + this.logger.log(`deleteOrgAgent :::: ${JSON.stringify(deleteOrgAgent)}`); + + const deletions = [ + { records: orgDid.count, tableName: 'org_dids' }, + { records: agentInvitation.count, tableName: 'agent_invitations' }, + { records: deleteOrgAgent ? 1 : 0, tableName: 'org_agents' } + ]; + + const logDeletionActivity = async (records, tableName): Promise => { + if (records) { + const txnMetadata = { + deletedRecordsCount: records, + deletedRecordInTable: tableName + }; + const recordType = RecordType.WALLET; + await this.userActivityRepository._orgDeletedActivity(orgId, user, txnMetadata, recordType); + } + }; + + for (const { records, tableName } of deletions) { + await logDeletionActivity(records, tableName); } + return deleteOrgAgent; + } } catch (error) { - this.logger.error(`Error in delete wallet in agent service: ${JSON.stringify(error.message)}`); - throw new RpcException(error.response ? error.response : error); + this.logger.error(`Error in delete wallet in agent service: ${JSON.stringify(error.message)}`); + throw new RpcException(error.response ? error.response : error); } } - async receiveInvitationUrl(receiveInvitationUrl: IReceiveInvitationUrl, url: string, orgId: string): Promise { try { const getApiKey = await this.getOrgAgentApiKey(orgId); diff --git a/apps/agent-service/src/repositories/agent-service.repository.ts b/apps/agent-service/src/repositories/agent-service.repository.ts index d9b798cd7..4e1a5faf6 100644 --- a/apps/agent-service/src/repositories/agent-service.repository.ts +++ b/apps/agent-service/src/repositories/agent-service.repository.ts @@ -485,12 +485,15 @@ export class AgentServiceRepository { } } - // eslint-disable-next-line camelcase - async deleteOrgAgentByOrg(orgId: string): Promise { + async deleteOrgAgentByOrg(orgId: string): Promise<{orgDid: Prisma.BatchPayload; + agentInvitation: Prisma.BatchPayload; + // eslint-disable-next-line camelcase + deleteOrgAgent: org_agents; + }> { try { return await this.prisma.$transaction(async (prisma) => { // Concurrently delete related records - await Promise.all([ + const [orgDid, agentInvitation] = await Promise.all([ prisma.org_dids.deleteMany({ where: { orgId } }), prisma.agent_invitations.deleteMany({ where: { orgId } }) ]); @@ -498,7 +501,7 @@ export class AgentServiceRepository { // Delete the organization agent const deleteOrgAgent = await prisma.org_agents.delete({ where: { orgId } }); - return deleteOrgAgent; + return {orgDid, agentInvitation, deleteOrgAgent}; }); } catch (error) { this.logger.error(`[deleteOrgAgentByOrg] - Error deleting org agent record: ${error.message}`); diff --git a/apps/api-gateway/src/organization/organization.controller.ts b/apps/api-gateway/src/organization/organization.controller.ts index 4333b8c20..95d64bfe1 100644 --- a/apps/api-gateway/src/organization/organization.controller.ts +++ b/apps/api-gateway/src/organization/organization.controller.ts @@ -526,10 +526,11 @@ export class OrganizationController { @Roles(OrgRoles.OWNER) async deleteOrganization( @Param('orgId') orgId: string, + @User() user: user, @Res() res: Response ): Promise { - await this.organizationService.deleteOrganization(orgId); + await this.organizationService.deleteOrganization(orgId, user); const finalResponse: IResponse = { statusCode: HttpStatus.OK, diff --git a/apps/api-gateway/src/organization/organization.service.ts b/apps/api-gateway/src/organization/organization.service.ts index bbb5b4fe4..c2aa99d41 100644 --- a/apps/api-gateway/src/organization/organization.service.ts +++ b/apps/api-gateway/src/organization/organization.service.ts @@ -6,7 +6,7 @@ import { CreateOrganizationDto } from './dtos/create-organization-dto'; import { BulkSendInvitationDto } from './dtos/send-invitation.dto'; import { UpdateUserRolesDto } from './dtos/update-user-roles.dto'; import { UpdateOrganizationDto } from './dtos/update-organization-dto'; -import { organisation } from '@prisma/client'; +import { organisation, user } from '@prisma/client'; import { IDidList, IGetOrgById, IGetOrganization } from 'apps/organization/interfaces/organization.interface'; import { IOrgUsers } from 'apps/user/interfaces/user.interface'; import { IOrgCredentials, IOrganization, IOrganizationInvitations, IOrganizationDashboard, IDeleteOrganization } from '@credebl/common/interfaces/organization.interface'; @@ -203,9 +203,10 @@ export class OrganizationService extends BaseService { } async deleteOrganization( - orgId: string + orgId: string, + user: user ): Promise { - const payload = { orgId }; + const payload = { orgId, user }; return this.sendNatsMessage(this.serviceProxy, 'delete-organization', payload); } diff --git a/apps/organization/repositories/organization.repository.ts b/apps/organization/repositories/organization.repository.ts index fdd29bd98..efe10d254 100644 --- a/apps/organization/repositories/organization.repository.ts +++ b/apps/organization/repositories/organization.repository.ts @@ -729,7 +729,11 @@ export class OrganizationRepository { } } - async deleteOrg(id: string): Promise { + async deleteOrg(id: string):Promise<{ + deletedUserActivity: Prisma.BatchPayload; + deletedUserOrgRole: Prisma.BatchPayload; + deleteOrg: IDeleteOrganization + }> { const tablesToCheck = [ 'org_invitations', 'org_agents', @@ -772,15 +776,15 @@ export class OrganizationRepository { } // User activity delete by orgId - await prisma.user_activity.deleteMany({ where: { orgId: id } }); + const deletedUserActivity = await prisma.user_activity.deleteMany({ where: { orgId: id } }); // User org role delete by orgId - await prisma.user_org_roles.deleteMany({ where: { orgId: id } }); + const deletedUserOrgRole = await prisma.user_org_roles.deleteMany({ where: { orgId: id } }); // If no references are found, delete the organization const deleteOrg = await prisma.organisation.delete({ where: { id } }); - return deleteOrg; + return {deletedUserActivity, deletedUserOrgRole, deleteOrg}; }); } catch (error) { this.logger.error(`Error in deleteOrg: ${error}`); diff --git a/apps/organization/src/organization.controller.ts b/apps/organization/src/organization.controller.ts index bed1de82f..463ccefff 100644 --- a/apps/organization/src/organization.controller.ts +++ b/apps/organization/src/organization.controller.ts @@ -7,7 +7,7 @@ import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { UpdateInvitationDto } from '../dtos/update-invitation.dt'; import { IDidList, IGetOrgById, IGetOrganization, IUpdateOrganization, Payload } from '../interfaces/organization.interface'; -import { organisation } from '@prisma/client'; +import { organisation, user } from '@prisma/client'; import { IOrgCredentials, IOrganizationInvitations, IOrganization, IOrganizationDashboard, IDeleteOrganization } from '@credebl/common/interfaces/organization.interface'; import { IAccessTokenData } from '@credebl/common/interfaces/interface'; import { IClientRoles } from '@credebl/client-registration/interfaces/client.interface'; @@ -227,8 +227,8 @@ export class OrganizationController { } @MessagePattern({ cmd: 'delete-organization' }) - async deleteOrganization(payload: { orgId: string }): Promise { - return this.organizationService.deleteOrganization(payload.orgId); + async deleteOrganization(payload: { orgId: string, user: user }): Promise { + return this.organizationService.deleteOrganization(payload.orgId, payload.user); } @MessagePattern({ cmd: 'delete-org-client-credentials' }) diff --git a/apps/organization/src/organization.module.ts b/apps/organization/src/organization.module.ts index c2c86edb3..9fe9c7350 100644 --- a/apps/organization/src/organization.module.ts +++ b/apps/organization/src/organization.module.ts @@ -35,7 +35,7 @@ import { AwsService } from '@credebl/aws'; providers: [ OrganizationService, OrganizationRepository, PrismaService, Logger, OrgRolesService, UserOrgRolesService, OrgRolesRepository, UserActivityRepository, - UserOrgRolesRepository, UserRepository, UserActivityService, + UserActivityRepository, UserOrgRolesRepository, UserRepository, UserActivityService, ClientRegistrationService, KeycloakUrlService, AwsService diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index 421116ab4..bdc3ad871 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -26,7 +26,7 @@ import { sendEmail } from '@credebl/common/send-grid-helper-file'; import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { UpdateInvitationDto } from '../dtos/update-invitation.dt'; -import { Invitation, transition } from '@credebl/enum/enum'; +import { Invitation, RecordType, transition } from '@credebl/enum/enum'; import { IGetOrgById, IGetOrganization, IUpdateOrganization, IOrgAgent, IClientCredentials, ICreateConnectionUrl, IOrgRole, IDidList, IPrimaryDidDetails } from '../interfaces/organization.interface'; import { UserActivityService } from '@credebl/user-activity'; import { ClientRegistrationService } from '@credebl/client-registration/client-registration.service'; @@ -46,6 +46,7 @@ import { ClientCredentialTokenPayloadDto } from '@credebl/client-registration/dt import { IAccessTokenData } from '@credebl/common/interfaces/interface'; import { IClientRoles } from '@credebl/client-registration/interfaces/client.interface'; import { toNumber } from '@credebl/common/cast.helper'; +import { UserActivityRepository } from 'libs/user-activity/repositories'; @Injectable() export class OrganizationService { constructor( @@ -59,8 +60,10 @@ export class OrganizationService { private readonly userActivityService: UserActivityService, private readonly logger: Logger, @Inject(CACHE_MANAGER) private cacheService: Cache, - private readonly clientRegistrationService: ClientRegistrationService + private readonly clientRegistrationService: ClientRegistrationService, + private readonly userActivityRepository: UserActivityRepository ) {} + /** * @@ -1298,14 +1301,40 @@ export class OrganizationService { } } - async deleteOrganization(orgId: string): Promise { + async deleteOrganization(orgId: string, user: user): Promise { try { - const deleteOrg = await this.organizationRepository.deleteOrg(orgId); - return deleteOrg; - + const { deletedUserActivity, deletedUserOrgRole, deleteOrg } = await this.organizationRepository.deleteOrg(orgId); + + this.logger.log(`deletedUserActivity ::: ${JSON.stringify(deletedUserActivity)}`); + this.logger.log(`deletedUserOrgRole ::: ${JSON.stringify(deletedUserOrgRole)}`); + this.logger.log(`deleteOrg ::: ${JSON.stringify(deleteOrg)}`); + + const deletions = [ + { records: deletedUserActivity.count, tableName: 'user_activity' }, + { records: deletedUserOrgRole.count, tableName: 'user_org_roles' }, + { records: deleteOrg ? 1 : 0, tableName: 'organization' } + ]; + + const logDeletionActivity = async (records, tableName): Promise => { + if (records) { + const txnMetadata = { + deletedRecordsCount: records, + deletedRecordInTable: tableName + }; + const recordType = RecordType.ORGANIZATION; + await this.userActivityRepository._orgDeletedActivity(orgId, user, txnMetadata, recordType); + } + }; + + for (const { records, tableName } of deletions) { + await logDeletionActivity(records, tableName); + } + + return deleteOrg; + } catch (error) { - this.logger.error(`delete organization: ${JSON.stringify(error)}`); - throw new RpcException(error.response ? error.response : error); + this.logger.error(`delete organization: ${JSON.stringify(error)}`); + throw new RpcException(error.response ? error.response : error); } } diff --git a/apps/user/interfaces/user.interface.ts b/apps/user/interfaces/user.interface.ts index ff4c977da..666af3427 100644 --- a/apps/user/interfaces/user.interface.ts +++ b/apps/user/interfaces/user.interface.ts @@ -1,3 +1,5 @@ +import { Prisma, RecordType } from "@prisma/client"; + export interface IUsersProfile { id: string; username?: string; @@ -200,4 +202,14 @@ export interface IIssueCertificate { export interface IPuppeteerOption{ width: number; height: number; +} + +export interface IUserDeletedActivity { + id: string; + userId: string; + orgId: string; + recordType: RecordType; + txnMetadata: Prisma.JsonValue; + deletedBy: string; + deleteDateTime: Date; } \ No newline at end of file diff --git a/apps/user/repositories/user.repository.ts b/apps/user/repositories/user.repository.ts index 95337baab..199d30416 100644 --- a/apps/user/repositories/user.repository.ts +++ b/apps/user/repositories/user.repository.ts @@ -9,13 +9,15 @@ import { IUserCredentials, ISendVerificationEmail, IUsersProfile, - IUserInformation, - IVerifyUserEmail + IUserInformation, + IVerifyUserEmail, + IUserDeletedActivity } from '../interfaces/user.interface'; import { InternalServerErrorException } from '@nestjs/common'; import { PrismaService } from '@credebl/prisma-service'; // eslint-disable-next-line camelcase import { schema, token, user } from '@prisma/client'; +import { RecordType } from '@credebl/enum/enum'; interface UserQueryOptions { id?: string; // Use the appropriate type based on your data model @@ -36,7 +38,7 @@ export class UserRepository { * @param userEmailVerification * @returns user's email */ - async createUser(userEmailVerification:ISendVerificationEmail, verifyCode: string): Promise { + async createUser(userEmailVerification: ISendVerificationEmail, verifyCode: string): Promise { try { const saveResponse = await this.prisma.user.upsert({ where: { @@ -76,7 +78,7 @@ export class UserRepository { }); } catch (error) { this.logger.error(`checkUserExist: ${JSON.stringify(error)}`); - throw new error; + throw new error(); } } @@ -203,9 +205,9 @@ export class UserRepository { } /** - * - * @param id - * @returns + * + * @param id + * @returns */ async getUserByKeycloakId(id: string): Promise { try { @@ -243,7 +245,6 @@ export class UserRepository { } } - async findUserByEmail(email: string): Promise { const queryOptions: UserQueryOptions = { email @@ -275,13 +276,13 @@ export class UserRepository { keycloakUserId: true, isEmailVerified: true, userOrgRoles: { - select:{ + select: { id: true, - userId:true, - orgRoleId:true, - orgId:true, + userId: true, + orgRoleId: true, + orgId: true, orgRole: { - select:{ + select: { id: true, name: true, description: true @@ -292,7 +293,7 @@ export class UserRepository { id: true, name: true, description: true, - orgSlug:true, + orgSlug: true, logoUrl: true, website: true, publicProfile: true @@ -329,13 +330,13 @@ export class UserRepository { isEmailVerified: true, publicProfile: true, userOrgRoles: { - select:{ + select: { id: true, - userId:true, - orgRoleId:true, - orgId:true, + userId: true, + orgRoleId: true, + orgId: true, orgRole: { - select:{ + select: { id: true, name: true, description: true @@ -346,7 +347,7 @@ export class UserRepository { id: true, name: true, description: true, - orgSlug:true, + orgSlug: true, logoUrl: true, website: true, publicProfile: true @@ -364,10 +365,7 @@ export class UserRepository { * @returns Updates organization details */ // eslint-disable-next-line camelcase - async updateUserDetails( - id: string, - keycloakId: string - ): Promise { + async updateUserDetails(id: string, keycloakId: string): Promise { try { const updateUserDetails = await this.prisma.user.update({ where: { @@ -465,7 +463,7 @@ export class UserRepository { agentSpinUpStatus: true, agentsTypeId: true, createDateTime: true, - orgAgentTypeId:true + orgAgentTypeId: true } } } @@ -620,10 +618,10 @@ export class UserRepository { } /** - * - * @param userId - * @param token - * @param expireTime + * + * @param userId + * @param token + * @param expireTime * @returns token details */ async createTokenForResetPassword(userId: string, token: string, expireTime: Date): Promise { @@ -643,9 +641,9 @@ export class UserRepository { } /** - * - * @param userId - * @param token + * + * @param userId + * @param token * @returns reset password token details */ async getResetPasswordTokenDetails(userId: string, token: string): Promise { @@ -664,8 +662,8 @@ export class UserRepository { } /** - * - * @param id + * + * @param id * @returns token delete records */ async deleteResetPasswordToken(id: string): Promise { @@ -760,4 +758,23 @@ export class UserRepository { throw new InternalServerErrorException(error); } } + + async updateOrgDeletedActivity(orgId: string, userId: string, deletedBy: string, recordType: RecordType, userEmail: string, txnMetadata: object): Promise { + try { + const orgDeletedActivity = await this.prisma.user_org_delete_activity.create({ + data: { + orgId, + userEmail, + deletedBy, + recordType, + txnMetadata, + userId + } + }); + return orgDeletedActivity; + } catch (error) { + this.logger.error(`Error in updateOrgDeletedActivity: ${error} `); + throw error; + } + } } diff --git a/apps/user/src/user.controller.ts b/apps/user/src/user.controller.ts index 76f995980..a34948918 100644 --- a/apps/user/src/user.controller.ts +++ b/apps/user/src/user.controller.ts @@ -1,4 +1,4 @@ -import { IOrgUsers, Payload, ICheckUserDetails, PlatformSettings, IShareUserCertificate, UpdateUserProfile, IUsersProfile, IUserInformation, IUserSignIn, IUserCredentials, IUserResetPassword} from '../interfaces/user.interface'; +import { IOrgUsers, Payload, ICheckUserDetails, PlatformSettings, IShareUserCertificate, UpdateUserProfile, IUsersProfile, IUserInformation, IUserSignIn, IUserCredentials, IUserResetPassword, IUserDeletedActivity} from '../interfaces/user.interface'; import { AcceptRejectInvitationDto } from '../dtos/accept-reject-invitation.dto'; import { Controller } from '@nestjs/common'; import { MessagePattern } from '@nestjs/microservices'; @@ -204,4 +204,9 @@ export class UserController { return this.userService.getPlatformEcosystemSettings(); } + @MessagePattern({ cmd: 'org-deleted-activity' }) + async updateOrgDeletedActivity(payload: { orgId, userId, deletedBy, recordType, userEmail, txnMetadata }): Promise { + return this.userService.updateOrgDeletedActivity(payload.orgId, payload.userId, payload.deletedBy, payload.recordType, payload.userEmail, payload.txnMetadata); + } + } diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index e6c86161a..31e1572b3 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -40,14 +40,15 @@ import { IUsersProfile, IUserResetPassword, IPuppeteerOption, - IShareDegreeCertificateRes + IShareDegreeCertificateRes, + IUserDeletedActivity } from '../interfaces/user.interface'; import { AcceptRejectInvitationDto } from '../dtos/accept-reject-invitation.dto'; import { UserActivityService } from '@credebl/user-activity'; import { SupabaseService } from '@credebl/supabase'; import { UserDevicesRepository } from '../repositories/user-device.repository'; import { v4 as uuidv4 } from 'uuid'; -import { EcosystemConfigSettings, Invitation, UserCertificateId } from '@credebl/enum/enum'; +import { EcosystemConfigSettings, Invitation, RecordType, UserCertificateId } from '@credebl/enum/enum'; import { WinnerTemplate } from '../templates/winner-template'; import { ParticipantTemplate } from '../templates/participant-template'; import { ArbiterTemplate } from '../templates/arbiter-template'; @@ -1143,4 +1144,13 @@ export class UserService { throw new RpcException(error.response ? error.response : error); } } + + async updateOrgDeletedActivity(orgId: string, userId: string, deletedBy: string, recordType: RecordType, userEmail: string, txnMetadata: object): Promise { + try { + return this.userRepository.updateOrgDeletedActivity(orgId, userId, deletedBy, recordType, userEmail, txnMetadata); + } catch (error) { + this.logger.error(`In updateOrgDeletedActivity : ${JSON.stringify(error)}`); + throw error; + } + } } diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index c62809a40..a02aa589e 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -212,7 +212,7 @@ export const ResponseMessages = { webhookUrlRegister:'Webhook Url registered successfully', getWebhookUrl:'Webhook Url fetched successfully', createKeys:'Key-pair created successfully', - walletDelete: 'Wallet is deleted' + walletDelete: 'The wallet has been deleted.' }, error: { exists: 'An agent name is already exist', diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 3ed5d31d7..74ba09bb9 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -138,4 +138,14 @@ export enum IssueCredentialType { export enum TemplateIdentifier { EMAIL_COLUMN = 'email_identifier' -} \ No newline at end of file +} + +export enum RecordType { + VERIFICATION_RECORD = 'VERIFICATION_RECORD', + ISSUANCE_RECORD = 'ISSUANCE_RECORD', + CONNECTION = 'CONNECTION', + ECOSYSTEM_MEMBER = 'ECOSYSTEM_MEMBER', + ORGANIZATION = 'ORGANIZATION', + WALLET = 'WALLET', + INVITATION = 'INVITATION', + } \ No newline at end of file diff --git a/libs/http-exception.filter.ts b/libs/http-exception.filter.ts index b97052c30..73299122f 100644 --- a/libs/http-exception.filter.ts +++ b/libs/http-exception.filter.ts @@ -13,8 +13,9 @@ export class HttpExceptionFilter implements ExceptionFilter { return throwError(() => new RpcException({ message, statusCode: httpStatus, error })); } - private getExceptionDetails(exception): { httpStatus: number, message: string, error: string } { - switch (exception.error.name) { + private getExceptionDetails(exception): { httpStatus: number; message: string; error: string } { + const exceptionName = exception?.error?.name || exception?.name; + switch (exceptionName) { case 'HttpException': return this.handleHttpException(exception); case 'RpcException': @@ -28,9 +29,9 @@ export class HttpExceptionFilter implements ExceptionFilter { } } - private handleHttpException(exception): { httpStatus: number, message: string, error: string } { + private handleHttpException(exception): { httpStatus: number; message: string; error: string } { this.logger.error(`It's HttpException`); - const httpStatus = exception.getStatus() || HttpStatus.BAD_REQUEST; + const httpStatus = exception?.getStatus() || HttpStatus.BAD_REQUEST; const message = exception?.getResponse() || exception.message; return { httpStatus, message, error: ResponseMessages.errorMessages.serverError }; } @@ -40,16 +41,21 @@ export class HttpExceptionFilter implements ExceptionFilter { throw exception.getError(); } - private handlePrismaClientKnownRequestError(exception): { httpStatus: number, message: string, error: string } { + private handlePrismaClientKnownRequestError(exception): { httpStatus: number; message: string; error: string } { this.logger.error(`It's PrismaClientKnownRequestError`); - const errorCode = exception.error.code; - const message = exception?.error?.meta?.message ?? exception?.error?.meta?.cause ?? exception?.message; + const errorCode = exception?.error?.code ?? exception?.code; + const message = + exception?.error?.meta?.message ?? exception?.error?.meta?.cause ?? exception?.message ?? exception?.meta; switch (errorCode) { case 'P2002': // Unique constraint failed on the {constraint} case 'P2010': // Raw query failed. Code: {code}. Message: {message} case 'P2011': // Null constraint violation on the {constraint} - return { httpStatus: HttpStatus.INTERNAL_SERVER_ERROR, message, error: ResponseMessages.errorMessages.serverError }; + return { + httpStatus: HttpStatus.INTERNAL_SERVER_ERROR, + message, + error: ResponseMessages.errorMessages.serverError + }; case 'P2000': // The provided value for the column is too long for the column's type. Column: {column_name} case 'P2005': // The value {field_value} stored in the database for the field {field_name} is invalid for the field's type case 'P2006': // The provided value {field_value} for {model_name} field {field_name} is not valid @@ -65,36 +71,37 @@ export class HttpExceptionFilter implements ExceptionFilter { case 'P2023': // Inconsistent column data: {message} return { httpStatus: HttpStatus.BAD_REQUEST, message, error: ResponseMessages.errorMessages.badRequest }; default: - return { httpStatus: HttpStatus.INTERNAL_SERVER_ERROR, message, error: ResponseMessages.errorMessages.serverError }; + return { + httpStatus: HttpStatus.INTERNAL_SERVER_ERROR, + message, + error: ResponseMessages.errorMessages.serverError + }; } } - private handlePrismaClientValidationError(exception): { httpStatus: number, message: string, error: string } { + private handlePrismaClientValidationError(exception): { httpStatus: number; message: string; error: string } { this.logger.error(`It's PrismaClientValidationError`); const httpStatus = HttpStatus.BAD_REQUEST; - const message = exception?.meta?.message ?? exception?.error?.meta?.cause ?? exception?.message ?? exception?.response?.message; + const message = + exception?.meta?.message ?? exception?.error?.meta?.cause ?? exception?.message ?? exception?.response?.message; return { httpStatus, message, error: ResponseMessages.errorMessages.badRequest }; } - private handleUnknownException(exception): { httpStatus: number, message: string, error: string } { + private handleUnknownException(exception): { httpStatus: number; message: string; error: string } { this.logger.error(`It's an Unknown Exception`); const httpStatus = - exception.response?.status ?? - exception.response?.statusCode ?? - exception?.error?.meta?.cause ?? - exception.code ?? - exception.error.statusCode ?? + exception?.response?.status ?? + exception?.response?.statusCode ?? + exception?.error?.statusCode ?? HttpStatus.INTERNAL_SERVER_ERROR; const message = - exception.response?.data?.message ?? - exception.response?.message ?? + exception?.response?.data?.message ?? + exception?.response?.message ?? exception?.error?.meta?.cause ?? exception?.error?.message ?? exception?.message ?? 'Internal server error'; - const error = - exception?.error?.error ?? - ResponseMessages.errorMessages.serverError; + const error = exception?.error?.error ?? ResponseMessages.errorMessages.serverError; return { httpStatus, message, error }; } } diff --git a/libs/prisma-service/prisma/migrations/20240610111750_added_email_user_org_delete_activity/migration.sql b/libs/prisma-service/prisma/migrations/20240610111750_added_email_user_org_delete_activity/migration.sql new file mode 100644 index 000000000..cca557ff9 --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240610111750_added_email_user_org_delete_activity/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `userEmail` to the `user_org_delete_activity` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "user_org_delete_activity" ADD COLUMN "userEmail" TEXT NOT NULL; diff --git a/libs/prisma-service/prisma/migrations/20240610142316_remove_unique_user_id_user_org_delete_activity/migration.sql b/libs/prisma-service/prisma/migrations/20240610142316_remove_unique_user_id_user_org_delete_activity/migration.sql new file mode 100644 index 000000000..9b748ff0c --- /dev/null +++ b/libs/prisma-service/prisma/migrations/20240610142316_remove_unique_user_id_user_org_delete_activity/migration.sql @@ -0,0 +1,5 @@ +-- DropIndex +DROP INDEX "user_org_delete_activity_orgId_key"; + +-- DropIndex +DROP INDEX "user_org_delete_activity_userId_key"; diff --git a/libs/prisma-service/prisma/schema.prisma b/libs/prisma-service/prisma/schema.prisma index 03d01cb4b..3c71febf6 100644 --- a/libs/prisma-service/prisma/schema.prisma +++ b/libs/prisma-service/prisma/schema.prisma @@ -517,22 +517,24 @@ model ledgerConfig { lastChangedBy String @default("1") deletedAt DateTime? @db.Timestamp(6) } + model user_org_delete_activity { - id String @id @default(uuid()) @db.Uuid - userId String @unique @db.Uuid - orgId String @unique @db.Uuid - recordType RecordType - txnMetadata Json - deletedBy String @db.Uuid - deleteDateTime DateTime @default(now()) @db.Timestamptz(6) + id String @id @default(uuid()) @db.Uuid + userEmail String + userId String @db.Uuid + orgId String @db.Uuid + recordType RecordType + txnMetadata Json + deletedBy String @db.Uuid + deleteDateTime DateTime @default(now()) @db.Timestamptz(6) } enum RecordType { - VERIFICATION_RECORD - ISSUANCE_RECORD - CONNECTION - ECOSYSTEM_MEMBER - ORGANIZATION - WALLET - INVITATION - } \ No newline at end of file + VERIFICATION_RECORD + ISSUANCE_RECORD + CONNECTION + ECOSYSTEM_MEMBER + ORGANIZATION + WALLET + INVITATION +} diff --git a/libs/user-activity/repositories/index.ts b/libs/user-activity/repositories/index.ts index 72c64cbd7..c2a613493 100644 --- a/libs/user-activity/repositories/index.ts +++ b/libs/user-activity/repositories/index.ts @@ -1,12 +1,16 @@ /* eslint-disable camelcase */ -import { Injectable, Logger } from '@nestjs/common'; +import { HttpException, Inject, Injectable, Logger } from '@nestjs/common'; import { IUsersActivity} from '../interface'; import { PrismaService } from '@credebl/prisma-service'; -import { RecordType, user_activity, user_org_delete_activity } from '@prisma/client'; +import { RecordType, user, user_activity, user_org_delete_activity } from '@prisma/client'; +import { map } from 'rxjs'; +import { ClientProxy } from '@nestjs/microservices'; @Injectable() export class UserActivityRepository { - constructor(private readonly prisma: PrismaService, private readonly logger: Logger) { } + constructor(private readonly prisma: PrismaService, private readonly logger: Logger, + @Inject('NATS_CLIENT') private readonly userActivityServiceProxy: ClientProxy + ) { } async logActivity(userId: string, orgId: string, action: string, details: string): Promise { @@ -22,14 +26,15 @@ export class UserActivityRepository { }); } - async deletedActivity(userId: string, orgId: string, recordType: RecordType, txnMetadata:object): Promise { + async deletedActivity(userId: string, orgId: string, recordType: RecordType, txnMetadata:object, userEmail?: string): Promise { return this.prisma.user_org_delete_activity.create({ data: { userId, orgId, recordType, txnMetadata, - deletedBy: userId + deletedBy: userId, + userEmail } }); } @@ -55,5 +60,36 @@ export class UserActivityRepository { }); } - + async _orgDeletedActivity(orgId: string, user: user, txnMetadata: object, recordType: RecordType): Promise<{ + message: string; + }> { + try { + const pattern = { cmd: 'org-deleted-activity' }; + const payload = { orgId, userId: user?.id, deletedBy: user?.id, recordType, userEmail: user?.email, txnMetadata }; + + return this.userActivityServiceProxy + .send(pattern, payload) + .pipe( + map((message) => ({ + message + })) + ) + .toPromise() + .catch((error) => { + this.logger.error(`catch: ${JSON.stringify(error)}`); + + throw new HttpException( + { + status: error?.error?.statusCode, + error: error?.error?.error, + message: error?.error?.message ?? error?.message + }, + error.error + ); + }); + } catch (error) { + this.logger.error(`[_orgDeletedActivity] - error in delete wallet : ${JSON.stringify(error)}`); + throw error; + } + } } \ No newline at end of file From 7100363ee12a2644519b19a626cafe82a0aaf7f4 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 11 Jun 2024 14:59:14 +0530 Subject: [PATCH 2/4] fix: added the await in the user service Signed-off-by: KulkarniShashank --- apps/user/src/user.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index 31e1572b3..35e4fe530 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -1147,7 +1147,7 @@ export class UserService { async updateOrgDeletedActivity(orgId: string, userId: string, deletedBy: string, recordType: RecordType, userEmail: string, txnMetadata: object): Promise { try { - return this.userRepository.updateOrgDeletedActivity(orgId, userId, deletedBy, recordType, userEmail, txnMetadata); + return await this.userRepository.updateOrgDeletedActivity(orgId, userId, deletedBy, recordType, userEmail, txnMetadata); } catch (error) { this.logger.error(`In updateOrgDeletedActivity : ${JSON.stringify(error)}`); throw error; From 74b6f90a8cf0d2fb8bf2077d6e2a70894b60c72d Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 11 Jun 2024 15:14:38 +0530 Subject: [PATCH 3/4] fix: added the http status code in enum on the agent service Signed-off-by: KulkarniShashank --- apps/agent-service/src/agent-service.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 57a6c78a4..836a5105c 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -8,6 +8,7 @@ import { BadRequestException, ConflictException, HttpException, + HttpStatus, Inject, Injectable, InternalServerErrorException, @@ -1670,7 +1671,7 @@ export class AgentServiceService { }) .then(async (response) => response); - if (deleteWallet.status === 204) { + if (deleteWallet.status === HttpStatus.NO_CONTENT) { const {orgDid, agentInvitation, deleteOrgAgent} = await this.agentServiceRepository.deleteOrgAgentByOrg(orgId); this.logger.log(`orgDid :::: ${JSON.stringify(orgDid)}`); From 726801aafaafbb2d13f16acc6c93de5566f01752 Mon Sep 17 00:00:00 2001 From: KulkarniShashank Date: Tue, 11 Jun 2024 15:32:09 +0530 Subject: [PATCH 4/4] fix: added the recordType as a type in user and organization service Signed-off-by: KulkarniShashank --- apps/agent-service/src/agent-service.service.ts | 4 ++-- apps/organization/src/organization.service.ts | 4 ++-- apps/user/repositories/user.repository.ts | 3 +-- apps/user/src/user.service.ts | 4 ++-- libs/enum/src/enum.ts | 12 +----------- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/apps/agent-service/src/agent-service.service.ts b/apps/agent-service/src/agent-service.service.ts index 836a5105c..f61eff9c3 100644 --- a/apps/agent-service/src/agent-service.service.ts +++ b/apps/agent-service/src/agent-service.service.ts @@ -55,9 +55,9 @@ import { IAgentConfigure, OrgDid } from './interface/agent-service.interface'; -import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType, RecordType } from '@credebl/enum/enum'; +import { AgentSpinUpStatus, AgentType, DidMethod, Ledgers, OrgAgentType } from '@credebl/enum/enum'; import { AgentServiceRepository } from './repositories/agent-service.repository'; -import { Prisma, ledgers, org_agents, organisation, platform_config, user } from '@prisma/client'; +import { Prisma, RecordType, ledgers, org_agents, organisation, platform_config, user } from '@prisma/client'; import { CommonConstants } from '@credebl/common/common.constant'; import { CommonService } from '@credebl/common'; import { GetSchemaAgentRedirection } from 'apps/ledger/src/schema/schema.interface'; diff --git a/apps/organization/src/organization.service.ts b/apps/organization/src/organization.service.ts index bdc3ad871..056432f1e 100644 --- a/apps/organization/src/organization.service.ts +++ b/apps/organization/src/organization.service.ts @@ -1,6 +1,6 @@ /* eslint-disable prefer-destructuring */ // eslint-disable-next-line camelcase -import { org_invitations, organisation, user } from '@prisma/client'; +import { RecordType, org_invitations, organisation, user } from '@prisma/client'; import { Injectable, Logger, @@ -26,7 +26,7 @@ import { sendEmail } from '@credebl/common/send-grid-helper-file'; import { CreateOrganizationDto } from '../dtos/create-organization.dto'; import { BulkSendInvitationDto } from '../dtos/send-invitation.dto'; import { UpdateInvitationDto } from '../dtos/update-invitation.dt'; -import { Invitation, RecordType, transition } from '@credebl/enum/enum'; +import { Invitation, transition } from '@credebl/enum/enum'; import { IGetOrgById, IGetOrganization, IUpdateOrganization, IOrgAgent, IClientCredentials, ICreateConnectionUrl, IOrgRole, IDidList, IPrimaryDidDetails } from '../interfaces/organization.interface'; import { UserActivityService } from '@credebl/user-activity'; import { ClientRegistrationService } from '@credebl/client-registration/client-registration.service'; diff --git a/apps/user/repositories/user.repository.ts b/apps/user/repositories/user.repository.ts index 199d30416..fe757aafa 100644 --- a/apps/user/repositories/user.repository.ts +++ b/apps/user/repositories/user.repository.ts @@ -16,8 +16,7 @@ import { import { InternalServerErrorException } from '@nestjs/common'; import { PrismaService } from '@credebl/prisma-service'; // eslint-disable-next-line camelcase -import { schema, token, user } from '@prisma/client'; -import { RecordType } from '@credebl/enum/enum'; +import { RecordType, schema, token, user } from '@prisma/client'; interface UserQueryOptions { id?: string; // Use the appropriate type based on your data model diff --git a/apps/user/src/user.service.ts b/apps/user/src/user.service.ts index 35e4fe530..935a947e8 100644 --- a/apps/user/src/user.service.ts +++ b/apps/user/src/user.service.ts @@ -26,7 +26,7 @@ import { UserOrgRolesService } from '@credebl/user-org-roles'; import { UserRepository } from '../repositories/user.repository'; import { VerifyEmailTokenDto } from '../dtos/verify-email.dto'; import { sendEmail } from '@credebl/common/send-grid-helper-file'; -import { user } from '@prisma/client'; +import { RecordType, user } from '@prisma/client'; import { Attribute, ICheckUserDetails, @@ -48,7 +48,7 @@ import { UserActivityService } from '@credebl/user-activity'; import { SupabaseService } from '@credebl/supabase'; import { UserDevicesRepository } from '../repositories/user-device.repository'; import { v4 as uuidv4 } from 'uuid'; -import { EcosystemConfigSettings, Invitation, RecordType, UserCertificateId } from '@credebl/enum/enum'; +import { EcosystemConfigSettings, Invitation, UserCertificateId } from '@credebl/enum/enum'; import { WinnerTemplate } from '../templates/winner-template'; import { ParticipantTemplate } from '../templates/participant-template'; import { ArbiterTemplate } from '../templates/arbiter-template'; diff --git a/libs/enum/src/enum.ts b/libs/enum/src/enum.ts index 74ba09bb9..3ed5d31d7 100644 --- a/libs/enum/src/enum.ts +++ b/libs/enum/src/enum.ts @@ -138,14 +138,4 @@ export enum IssueCredentialType { export enum TemplateIdentifier { EMAIL_COLUMN = 'email_identifier' -} - -export enum RecordType { - VERIFICATION_RECORD = 'VERIFICATION_RECORD', - ISSUANCE_RECORD = 'ISSUANCE_RECORD', - CONNECTION = 'CONNECTION', - ECOSYSTEM_MEMBER = 'ECOSYSTEM_MEMBER', - ORGANIZATION = 'ORGANIZATION', - WALLET = 'WALLET', - INVITATION = 'INVITATION', - } \ No newline at end of file +} \ No newline at end of file