Skip to content

Commit 8ef5d9e

Browse files
#429: Implement GET /subscription_user Endpoint (#580)
* refactor: add GetSubscriptionUserDto and getUserSubscriptions endpoint [WIP][Refactor][Tests] * refactor: remove userId property from GetSubscriptionUserDto, fixed overly nested response. * test: add getUserSubscriptions service tests * refactor: add try catch to getSubscriptions service. * chore: abstract serialization into utils * Remove GetSubscriptionUsersDto * Update get-subscription-user.dto.ts --------- Co-authored-by: eleanorreem <[email protected]>
1 parent ca7a904 commit 8ef5d9e

5 files changed

+111
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { ApiProperty } from '@nestjs/swagger';
2+
import { Expose } from 'class-transformer';
3+
import { IsDefined, IsNotEmpty, IsOptional, IsString } from 'class-validator';
4+
5+
@Expose()
6+
export class GetSubscriptionUserDto {
7+
@ApiProperty()
8+
@IsString()
9+
@IsDefined()
10+
@IsNotEmpty()
11+
id: string;
12+
13+
@ApiProperty()
14+
@IsString()
15+
@IsDefined()
16+
@IsNotEmpty()
17+
subscriptionId: string;
18+
19+
@ApiProperty()
20+
@IsString()
21+
@IsDefined()
22+
@IsNotEmpty()
23+
@Expose()
24+
subscriptionName: string;
25+
26+
@ApiProperty()
27+
@IsString()
28+
@IsDefined()
29+
@IsNotEmpty()
30+
subscriptionInfo: string;
31+
32+
@ApiProperty()
33+
@IsDefined()
34+
createdAt: Date;
35+
36+
@ApiProperty()
37+
@IsOptional()
38+
cancelledAt: Date | null;
39+
}

src/subscription-user/subscription-user.controller.ts

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { Body, Controller, Param, Patch, Post, Req, UseGuards } from '@nestjs/common';
1+
import { Body, Controller, Get, Param, Patch, Post, Req, UseGuards } from '@nestjs/common';
22
import { ApiBearerAuth, ApiOperation, ApiParam, ApiTags } from '@nestjs/swagger';
33
import { Request } from 'express';
44
import { UserEntity } from 'src/entities/user.entity';
5+
import { mapToSubscriptionUserDtos } from 'src/utils/serialize';
56
import { FirebaseAuthGuard } from '../firebase/firebase-auth.guard';
67
import { ControllerDecorator } from '../utils/controller.decorator';
78
import { CreateSubscriptionUserDto } from './dto/create-subscription-user.dto';
9+
import { GetSubscriptionUserDto } from './dto/get-subscription-user.dto';
810
import { UpdateSubscriptionUserDto } from './dto/update-subscription-user.dto';
911
import { ISubscriptionUser } from './subscription-user.interface';
1012
import { SubscriptionUserService } from './subscription-user.service';
@@ -15,6 +17,23 @@ import { SubscriptionUserService } from './subscription-user.service';
1517
export class SubscriptionUserController {
1618
constructor(private readonly subscriptionUserService: SubscriptionUserService) {}
1719

20+
@Get('/')
21+
@ApiBearerAuth('access-token')
22+
@ApiOperation({
23+
description: 'Returns all the subscriptions of the authenticated user.',
24+
})
25+
@UseGuards(FirebaseAuthGuard)
26+
async getUserSubscriptions(@Req() req: Request): Promise<GetSubscriptionUserDto[]> {
27+
const user = req['userEntity'] as UserEntity;
28+
const userId = user.id;
29+
30+
const userSubscriptions = await this.subscriptionUserService.getSubscriptions(userId);
31+
32+
const subscriptionDtos = mapToSubscriptionUserDtos(userSubscriptions);
33+
34+
return subscriptionDtos;
35+
}
36+
1837
@Post('/whatsapp')
1938
@ApiBearerAuth('access-token')
2039
@ApiOperation({

src/subscription-user/subscription-user.service.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,24 @@ describe('SubscriptionUserService', () => {
6161
]);
6262
});
6363
});
64+
65+
describe('getUserSubscriptions', () => {
66+
it('should return a list of subscriptions for the given userId', async () => {
67+
const result = await service.getSubscriptions(mockUserEntity.id);
68+
69+
expect(result).toMatchObject([mockSubscriptionUserEntity]);
70+
});
71+
72+
it('should return an empty array if the user has no subscriptions', async () => {
73+
mockedSubscriptionUserRepository.find.mockResolvedValueOnce([]);
74+
75+
const result = await service.getSubscriptions(mockUserEntity.id);
76+
77+
expect(result).toEqual([]);
78+
expect(mockedSubscriptionUserRepository.find).toHaveBeenCalledWith({
79+
where: { userId: mockUserEntity.id },
80+
relations: ['subscription'],
81+
});
82+
});
83+
});
6484
});

src/subscription-user/subscription-user.service.ts

+15
Original file line numberDiff line numberDiff line change
@@ -153,4 +153,19 @@ export class SubscriptionUserService {
153153
);
154154
}
155155
}
156+
157+
async getSubscriptions(userId: string): Promise<SubscriptionUserEntity[]> {
158+
try {
159+
const userSubscriptions = await this.subscriptionUserRepository.find({
160+
where: { userId: userId },
161+
relations: ['subscription'],
162+
});
163+
164+
this.logger.log(`User ${userId} has ${userSubscriptions.length} subscriptions`);
165+
166+
return userSubscriptions;
167+
} catch (err) {
168+
throw new HttpException(`getSubscriptions error - ${err}`, HttpStatus.INTERNAL_SERVER_ERROR);
169+
}
170+
}
156171
}

src/utils/serialize.ts

+17
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { PartnerAdminEntity } from 'src/entities/partner-admin.entity';
22
import { PartnerEntity } from 'src/entities/partner.entity';
33
import { IPartnerFeature } from 'src/partner-feature/partner-feature.interface';
44
import { IPartner } from 'src/partner/partner.interface';
5+
import { GetSubscriptionUserDto } from 'src/subscription-user/dto/get-subscription-user.dto';
56
import { CourseUserEntity } from '../entities/course-user.entity';
67
import { PartnerAccessEntity } from '../entities/partner-access.entity';
78
import { SubscriptionUserEntity } from '../entities/subscription-user.entity';
@@ -203,3 +204,19 @@ export const formatSubscriptionObject = (
203204
cancelledAt: userSubscription.cancelledAt,
204205
};
205206
};
207+
208+
export const mapToSubscriptionUserDtos = (
209+
userSubscriptions: SubscriptionUserEntity[],
210+
): GetSubscriptionUserDto[] => {
211+
return userSubscriptions.map((subscriptionUser) => {
212+
const dto = new GetSubscriptionUserDto();
213+
dto.id = subscriptionUser.id;
214+
dto.subscriptionId = subscriptionUser.subscription.id;
215+
dto.subscriptionName = subscriptionUser.subscription.name;
216+
dto.subscriptionInfo = subscriptionUser.subscription.info;
217+
dto.createdAt = subscriptionUser.createdAt;
218+
dto.cancelledAt = subscriptionUser.cancelledAt;
219+
dto.subscriptionInfo = subscriptionUser.subscriptionInfo;
220+
return dto;
221+
});
222+
};

0 commit comments

Comments
 (0)