Skip to content

Commit

Permalink
Merge pull request #763 from credebl/feat/delete-organization-and-wallet
Browse files Browse the repository at this point in the history
feat: added deleted metadata function and table
  • Loading branch information
KulkarniShashank authored Jun 11, 2024
2 parents f4012ce + 726801a commit d852bfa
Show file tree
Hide file tree
Showing 20 changed files with 316 additions and 148 deletions.
4 changes: 2 additions & 2 deletions apps/agent-service/src/agent-service.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,8 @@ export class AgentServiceController {
}

@MessagePattern({ cmd: 'delete-wallet' })
async deleteWallet(payload: { orgId }): Promise<object> {
return this.agentServiceService.deleteWallet(payload.orgId);
async deleteWallet(payload: { orgId, user }): Promise<object> {
return this.agentServiceService.deleteWallet(payload.orgId, payload.user);
}

@MessagePattern({ cmd: 'agent-receive-invitation-url' })
Expand Down
4 changes: 3 additions & 1 deletion apps/agent-service/src/agent-service.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
Expand All @@ -31,7 +32,8 @@ import { getNatsOptions } from '@credebl/common/nats.config';
PrismaService,
Logger,
ConnectionService,
ConnectionRepository
ConnectionRepository,
UserActivityRepository
],
exports: [AgentServiceService, AgentServiceRepository, AgentServiceModule]
})
Expand Down
105 changes: 66 additions & 39 deletions apps/agent-service/src/agent-service.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
BadRequestException,
ConflictException,
HttpException,
HttpStatus,
Inject,
Injectable,
InternalServerErrorException,
Expand Down Expand Up @@ -56,7 +57,7 @@ import {
} from './interface/agent-service.interface';
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 } 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';
Expand All @@ -72,6 +73,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()
Expand All @@ -83,7 +85,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<string> {
Expand Down Expand Up @@ -953,8 +956,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
Expand Down Expand Up @@ -1021,9 +1024,7 @@ export class AgentServiceService {
);
}

private async storeDid(createdDidDetails): Promise<
OrgDid
> {
private async storeDid(createdDidDetails): Promise<OrgDid> {
const storeDidDetails = await this.agentServiceRepository.storeDidDetails(createdDidDetails);

if (!storeDidDetails) {
Expand Down Expand Up @@ -1632,53 +1633,79 @@ export class AgentServiceService {
}
}

async deleteWallet(orgId: string): Promise<object> {
async deleteWallet(orgId: string, user: user): Promise<object> {
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 === HttpStatus.NO_CONTENT) {
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<void> => {
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<string> {
try {
const getApiKey = await this.getOrgAgentApiKey(orgId);
Expand Down
11 changes: 7 additions & 4 deletions apps/agent-service/src/repositories/agent-service.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,20 +485,23 @@ export class AgentServiceRepository {
}
}

// eslint-disable-next-line camelcase
async deleteOrgAgentByOrg(orgId: string): Promise<org_agents> {
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 } })
]);

// 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}`);
Expand Down
3 changes: 2 additions & 1 deletion apps/api-gateway/src/organization/organization.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,10 +526,11 @@ export class OrganizationController {
@Roles(OrgRoles.OWNER)
async deleteOrganization(
@Param('orgId') orgId: string,
@User() user: user,
@Res() res: Response
): Promise<Response> {

await this.organizationService.deleteOrganization(orgId);
await this.organizationService.deleteOrganization(orgId, user);

const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
Expand Down
7 changes: 4 additions & 3 deletions apps/api-gateway/src/organization/organization.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -203,9 +203,10 @@ export class OrganizationService extends BaseService {
}

async deleteOrganization(
orgId: string
orgId: string,
user: user
): Promise<IDeleteOrganization> {
const payload = { orgId };
const payload = { orgId, user };

return this.sendNatsMessage(this.serviceProxy, 'delete-organization', payload);
}
Expand Down
12 changes: 8 additions & 4 deletions apps/organization/repositories/organization.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,11 @@ export class OrganizationRepository {
}
}

async deleteOrg(id: string): Promise<IDeleteOrganization> {
async deleteOrg(id: string):Promise<{
deletedUserActivity: Prisma.BatchPayload;
deletedUserOrgRole: Prisma.BatchPayload;
deleteOrg: IDeleteOrganization
}> {
const tablesToCheck = [
'org_invitations',
'org_agents',
Expand Down Expand Up @@ -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}`);
Expand Down
6 changes: 3 additions & 3 deletions apps/organization/src/organization.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -227,8 +227,8 @@ export class OrganizationController {
}

@MessagePattern({ cmd: 'delete-organization' })
async deleteOrganization(payload: { orgId: string }): Promise<IDeleteOrganization> {
return this.organizationService.deleteOrganization(payload.orgId);
async deleteOrganization(payload: { orgId: string, user: user }): Promise<IDeleteOrganization> {
return this.organizationService.deleteOrganization(payload.orgId, payload.user);
}

@MessagePattern({ cmd: 'delete-org-client-credentials' })
Expand Down
2 changes: 1 addition & 1 deletion apps/organization/src/organization.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
45 changes: 37 additions & 8 deletions apps/organization/src/organization.service.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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(
Expand All @@ -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
) {}


/**
*
Expand Down Expand Up @@ -1298,14 +1301,40 @@ export class OrganizationService {
}
}

async deleteOrganization(orgId: string): Promise<IDeleteOrganization> {
async deleteOrganization(orgId: string, user: user): Promise<IDeleteOrganization> {
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<void> => {
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);
}
}

Expand Down
Loading

0 comments on commit d852bfa

Please sign in to comment.