Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat delete user #442

Merged
merged 5 commits into from
Jun 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/api/simplybook/simplybook-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,24 @@ export const getBookingsForDate: (date: Date) => Promise<BookingInfo[]> = async
}
};

// Not currently used but might be used in future implementations so am keeping
export const deleteClient: (clientId: string) => Promise<string> = async (clientId) => {
const token = await getAuthToken();

try {
const bookingsResponse = await axios.get(`${SIMPLYBOOK_API_BASE_URL}/client/${clientId}`, {
headers: {
'Content-Type': 'application/json',
'X-Company-Login': simplybookCompanyName,
'X-Token': `${token}`,
},
});
return bookingsResponse.data.data;
} catch (error) {
handleError(`Failed to delete client ${clientId} from Simplybook.`, error);
}
};

const handleError = (error, message: string) => {
LOGGER.error(message, error);
throw new Error(`${message}: ${error})`);
Expand Down
16 changes: 16 additions & 0 deletions src/firebase/firebase.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SlackMessageClient } from 'src/api/slack/slack-api';
import { ZapierWebhookClient } from 'src/api/zapier/zapier-webhook-client';
import { CourseUserService } from 'src/course-user/course-user.service';
import { CourseUserEntity } from 'src/entities/course-user.entity';
import { PartnerAccessEntity } from 'src/entities/partner-access.entity';
import { PartnerAdminEntity } from 'src/entities/partner-admin.entity';
import { PartnerEntity } from 'src/entities/partner.entity';
import { SubscriptionUserEntity } from 'src/entities/subscription-user.entity';
import { SubscriptionEntity } from 'src/entities/subscription.entity';
import { TherapySessionEntity } from 'src/entities/therapy-session.entity';
import { UserEntity } from 'src/entities/user.entity';
import { PartnerService } from 'src/partner/partner.service';
import { SubscriptionUserService } from 'src/subscription-user/subscription-user.service';
import { SubscriptionService } from 'src/subscription/subscription.service';
import { TherapySessionService } from 'src/therapy-session/therapy-session.service';
import { PartnerAccessService } from '../partner-access/partner-access.service';
import { UserService } from '../user/user.service';
import { FIREBASE, firebaseFactory } from './firebase-factory';
Expand All @@ -19,6 +27,9 @@ import { FIREBASE, firebaseFactory } from './firebase-factory';
PartnerAccessEntity,
CourseUserEntity,
PartnerAdminEntity,
SubscriptionUserEntity,
SubscriptionEntity,
TherapySessionEntity,
]),
],
providers: [
Expand All @@ -27,6 +38,11 @@ import { FIREBASE, firebaseFactory } from './firebase-factory';
PartnerAccessService,
CourseUserService,
PartnerService,
SubscriptionUserService,
SubscriptionService,
TherapySessionService,
ZapierWebhookClient,
SlackMessageClient,
],
exports: [FIREBASE],
})
Expand Down
24 changes: 23 additions & 1 deletion src/partner-access/partner-access.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SlackMessageClient } from 'src/api/slack/slack-api';
import { ZapierWebhookClient } from 'src/api/zapier/zapier-webhook-client';
import { CourseUserService } from 'src/course-user/course-user.service';
import { CourseUserEntity } from 'src/entities/course-user.entity';
import { PartnerAccessEntity } from 'src/entities/partner-access.entity';
import { PartnerAdminEntity } from 'src/entities/partner-admin.entity';
import { PartnerEntity } from 'src/entities/partner.entity';
import { SubscriptionUserEntity } from 'src/entities/subscription-user.entity';
import { SubscriptionEntity } from 'src/entities/subscription.entity';
import { TherapySessionEntity } from 'src/entities/therapy-session.entity';
import { UserEntity } from 'src/entities/user.entity';
import { PartnerService } from 'src/partner/partner.service';
import { SubscriptionUserService } from 'src/subscription-user/subscription-user.service';
import { SubscriptionService } from 'src/subscription/subscription.service';
import { TherapySessionService } from 'src/therapy-session/therapy-session.service';
import { AuthService } from '../auth/auth.service';
import { FirebaseModule } from '../firebase/firebase.module';
import { UserService } from '../user/user.service';
Expand All @@ -21,10 +29,24 @@ import { PartnerAccessService } from './partner-access.service';
PartnerAccessEntity,
PartnerAdminEntity,
CourseUserEntity,
SubscriptionUserEntity,
TherapySessionEntity,
SubscriptionEntity,
]),
FirebaseModule,
],
controllers: [PartnerAccessController],
providers: [PartnerAccessService, AuthService, UserService, CourseUserService, PartnerService],
providers: [
PartnerAccessService,
AuthService,
UserService,
CourseUserService,
PartnerService,
SubscriptionUserService,
SubscriptionService,
TherapySessionService,
ZapierWebhookClient,
SlackMessageClient,
],
})
export class PartnerAccessModule {}
16 changes: 16 additions & 0 deletions src/session-user/session-user.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SlackMessageClient } from 'src/api/slack/slack-api';
import { ZapierWebhookClient } from 'src/api/zapier/zapier-webhook-client';
import { CourseUserEntity } from 'src/entities/course-user.entity';
import { CourseEntity } from 'src/entities/course.entity';
import { PartnerAccessEntity } from 'src/entities/partner-access.entity';
import { PartnerAdminEntity } from 'src/entities/partner-admin.entity';
import { PartnerEntity } from 'src/entities/partner.entity';
import { SessionUserEntity } from 'src/entities/session-user.entity';
import { SessionEntity } from 'src/entities/session.entity';
import { SubscriptionUserEntity } from 'src/entities/subscription-user.entity';
import { SubscriptionEntity } from 'src/entities/subscription.entity';
import { TherapySessionEntity } from 'src/entities/therapy-session.entity';
import { UserEntity } from 'src/entities/user.entity';
import { PartnerService } from 'src/partner/partner.service';
import { SubscriptionUserService } from 'src/subscription-user/subscription-user.service';
import { SubscriptionService } from 'src/subscription/subscription.service';
import { TherapySessionService } from 'src/therapy-session/therapy-session.service';
import { CourseUserService } from '../course-user/course-user.service';
import { CourseService } from '../course/course.service';
import { PartnerAccessService } from '../partner-access/partner-access.service';
Expand All @@ -28,6 +36,9 @@ import { SessionUserService } from './session-user.service';
PartnerAccessEntity,
CourseEntity,
PartnerAdminEntity,
SubscriptionUserEntity,
TherapySessionEntity,
SubscriptionEntity,
]),
],
controllers: [SessionUserController],
Expand All @@ -39,6 +50,11 @@ import { SessionUserService } from './session-user.service';
PartnerAccessService,
CourseService,
PartnerService,
SubscriptionUserService,
TherapySessionService,
SubscriptionService,
ZapierWebhookClient,
SlackMessageClient,
],
})
export class SessionUserModule {}
3 changes: 2 additions & 1 deletion src/subscription-user/subscription-user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ export class SubscriptionUserController {
@Body() updateSubscriptionsDto: UpdateSubscriptionUserDto,
): Promise<ISubscriptionUser | undefined> {
return this.subscriptionUserService.cancelWhatsappSubscription(
req['user'],
req['user'].user.id,
req['user'].user.email,
updateSubscriptionsDto,
id,
);
Expand Down
7 changes: 7 additions & 0 deletions src/subscription-user/subscription-user.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SlackMessageClient } from 'src/api/slack/slack-api';
import { PartnerAccessEntity } from 'src/entities/partner-access.entity';
import { PartnerAdminEntity } from 'src/entities/partner-admin.entity';
import { PartnerEntity } from 'src/entities/partner.entity';
import { SubscriptionUserEntity } from 'src/entities/subscription-user.entity';
import { SubscriptionEntity } from 'src/entities/subscription.entity';
import { TherapySessionEntity } from 'src/entities/therapy-session.entity';
import { UserEntity } from 'src/entities/user.entity';
import { PartnerService } from 'src/partner/partner.service';
import { TherapySessionService } from 'src/therapy-session/therapy-session.service';
import { ZapierWebhookClient } from '../api/zapier/zapier-webhook-client';
import { FirebaseModule } from '../firebase/firebase.module';
import { PartnerAccessService } from '../partner-access/partner-access.service';
Expand All @@ -24,6 +27,7 @@ import { SubscriptionUserService } from './subscription-user.service';
PartnerAccessEntity,
PartnerEntity,
PartnerAdminEntity,
TherapySessionEntity,
]),
FirebaseModule,
],
Expand All @@ -35,6 +39,9 @@ import { SubscriptionUserService } from './subscription-user.service';
PartnerAccessService,
ZapierWebhookClient,
PartnerService,
TherapySessionService,
SlackMessageClient,
],
exports: [SubscriptionUserService],
})
export class SubscriptionUserModule {}
64 changes: 64 additions & 0 deletions src/subscription-user/subscription-user.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { DeepMocked, createMock } from '@golevelup/ts-jest/lib/mocks';
import { Test, TestingModule } from '@nestjs/testing';
import { getRepositoryToken } from '@nestjs/typeorm';
import { ZapierWebhookClient } from 'src/api/zapier/zapier-webhook-client';
import { SubscriptionUserEntity } from 'src/entities/subscription-user.entity';
import { SubscriptionService } from 'src/subscription/subscription.service';
import { mockSubscriptionUserEntity, mockUserEntity } from 'test/utils/mockData';
import {
mockSubscriptionUserRepositoryMethods,
mockZapierWebhookClientMethods,
} from 'test/utils/mockedServices';
import { Repository } from 'typeorm/repository/Repository';
import { SubscriptionUserService } from './subscription-user.service';

describe('SubscriptionUserService', () => {
let service: SubscriptionUserService;
let mockedSubscriptionUserRepository: DeepMocked<Repository<SubscriptionUserEntity>>;

let mockSubscriptionService: DeepMocked<SubscriptionService>;
const mockedZapierWebhookClient = createMock<ZapierWebhookClient>(mockZapierWebhookClientMethods);

beforeEach(async () => {
mockedSubscriptionUserRepository = createMock<Repository<SubscriptionUserEntity>>(
mockSubscriptionUserRepositoryMethods,
);
mockSubscriptionService = createMock<SubscriptionService>(mockSubscriptionService);

const module: TestingModule = await Test.createTestingModule({
providers: [
SubscriptionUserService,
{
provide: getRepositoryToken(SubscriptionUserEntity),
useValue: mockedSubscriptionUserRepository,
},
{
provide: SubscriptionService,
useValue: mockSubscriptionService,
},
{
provide: ZapierWebhookClient,
useValue: mockedZapierWebhookClient,
},
],
}).compile();

service = module.get<SubscriptionUserService>(SubscriptionUserService);
});

it('should be defined', () => {
expect(service).toBeDefined();
});

describe('softDeleteSubscriptions', () => {
it('when supplied with correct data should delete number from respond.io and redact number', async () => {
const response = await service.softDeleteSubscriptionsForUser(
mockUserEntity.id,
mockUserEntity.email,
);
expect(response).toMatchObject([
{ ...mockSubscriptionUserEntity, subscriptionInfo: 'Number Redacted' },
]);
});
});
});
44 changes: 40 additions & 4 deletions src/subscription-user/subscription-user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,20 +66,21 @@ export class SubscriptionUserService {
}

async cancelWhatsappSubscription(
{ user }: GetUserDto,
userId: string,
userEmail: string,
{ cancelledAt }: UpdateSubscriptionUserDto,
id: string,
) {
const subscription = await this.subscriptionUserRepository
.createQueryBuilder('subscription_user')
.where('subscription_user.subscriptionUserId = :id', { id })
.andWhere('subscription_user.userId = :userId', { userId: user.id })
.andWhere('subscription_user.userId = :userId', { userId: userId })
.getOne();

if (subscription) {
if (!subscription.cancelledAt) {
this.logger.log(
`Triggering zapier to remove contact (number: ${subscription.subscriptionInfo}) from respond.io for user ${user.email}.`,
`Triggering zapier to remove contact (number: ${subscription.subscriptionInfo}) from respond.io for user ${userEmail}.`,
);
await this.zapierClient.deleteContactFromRespondIO({
phonenumber: subscription.subscriptionInfo,
Expand All @@ -88,7 +89,7 @@ export class SubscriptionUserService {
subscription.cancelledAt = cancelledAt;
await this.subscriptionUserRepository.save(subscription);

return this.getFullSubscriptionInfo({ id: subscription.id, userId: user.id });
return this.getFullSubscriptionInfo({ id: subscription.id, userId });
} else {
throw new HttpException('Subscription has already been cancelled', HttpStatus.CONFLICT);
}
Expand Down Expand Up @@ -117,4 +118,39 @@ export class SubscriptionUserService {
sanitizePhonenumber = (phonenumber: string) => {
return phonenumber.replace(/\s/g, ''); // remove spaces
};

async softDeleteSubscriptionsForUser(userId, userEmail): Promise<SubscriptionUserEntity[]> {
try {
const subscriptions = await this.subscriptionUserRepository.find({
where: { userId: userId },
});
const cancelledAt = new Date();

const updatedSubscriptions: SubscriptionUserEntity[] = await Promise.all(
subscriptions.map(async (subs): Promise<SubscriptionUserEntity> => {
if (subs.cancelledAt !== null) {
await this.cancelWhatsappSubscription(userId, userEmail, { cancelledAt }, subs.id);
}
const subscription = await this.subscriptionUserRepository.findOne({
where: { id: subs.id },
});
const updatedSubscription = {
...subscription,
subscriptionInfo: `Number Redacted`,
};

return await this.subscriptionUserRepository.save(updatedSubscription);
}),
);
this.logger.log(
`Redacted number for ${updatedSubscriptions.length} subscription(s) for user with email ${userEmail}`,
);
return updatedSubscriptions;
} catch (err) {
throw new HttpException(
`softDeleteSubscriptionUser error - ${err}`,
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
}
1 change: 1 addition & 0 deletions src/subscription/subscription.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ import { SubscriptionService } from './subscription.service';
@Module({
imports: [TypeOrmModule.forFeature([SubscriptionEntity])],
providers: [SubscriptionService],
exports: [SubscriptionService],
})
export class SubscriptionModule {}
12 changes: 12 additions & 0 deletions src/therapy-session/therapy-session.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SlackMessageClient } from 'src/api/slack/slack-api';
import { TherapySessionEntity } from 'src/entities/therapy-session.entity';
import { TherapySessionService } from './therapy-session.service';

@Module({
imports: [TypeOrmModule.forFeature([TherapySessionEntity])],
providers: [SlackMessageClient],
exports: [TherapySessionService],
})
export class TherapySessionModule {}
Loading
Loading