diff --git a/src/comments/comments.controller.ts b/src/comments/comments.controller.ts index 6c64e0e..78c4217 100644 --- a/src/comments/comments.controller.ts +++ b/src/comments/comments.controller.ts @@ -8,10 +8,12 @@ import { Param, Delete, UseGuards, + UseInterceptors, } from '@nestjs/common'; import { ApiTags, ApiCookieAuth, ApiOperation } from '@nestjs/swagger'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { IRequestWithUser } from '../users/interfaces/request-with-user.interface'; +import { ValidIdInterceptor } from '../utils/interceptors/valid-id.interceptor'; import { CommentsService } from './comments.service'; import { CreateCommentDto } from './dto/create-comment.dto'; import { UpdateCommentDto } from './dto/update-comment.dto'; @@ -44,6 +46,7 @@ export class CommentsController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Find one comment by id' }) + @UseInterceptors(ValidIdInterceptor) findOne(@Param('id') id: string) { return this.commentsService.findCommentById(id); } @@ -51,6 +54,7 @@ export class CommentsController { @Patch(':id') @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') + @UseInterceptors(ValidIdInterceptor) update( @Param('id') id: string, @Body() updateCommentDto: UpdateCommentDto, @@ -67,6 +71,7 @@ export class CommentsController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Delete a comment' }) + @UseInterceptors(ValidIdInterceptor) remove(@Param('id') id: string, @Request() request: IRequestWithUser) { return this.commentsService.removeComment(id, request.user); } diff --git a/src/comments/comments.service.ts b/src/comments/comments.service.ts index 6ec8284..1c72fe7 100644 --- a/src/comments/comments.service.ts +++ b/src/comments/comments.service.ts @@ -10,6 +10,7 @@ import { ResourceDocument } from '../resources/schemas/resource.schema'; import { TrackDocument } from '../tracks/schemas/track.schema'; import { TracksService } from '../tracks/tracks.service'; import { UserDocument } from '../users/schemas/user.schema'; +import { isValidId } from '../utils/isValidId'; import { CreateCommentDto, ModelType } from './dto/create-comment.dto'; import { UpdateCommentDto } from './dto/update-comment.dto'; import { Comment, CommentDocument } from './schemas/comment.schema'; @@ -71,19 +72,22 @@ export class CommentsService { updateCommentDto: UpdateCommentDto, owner: UserDocument, ) { + isValidId(id); return `This action updates a #${id} comment`; } async removeComment(id: string, owner: UserDocument) { + isValidId(id); const comment = await this.isUserTheOwnerOfComment(id, owner); - await this.commentModel.deleteOne({ id: comment._id }); + await comment.remove(); return { msg: `Comment ${comment._id.toString()} deleted`, }; } private async isUserTheOwnerOfComment(id: string, owner: UserDocument) { + isValidId(id); const comment = await this.findCommentById(id); if (!comment) { throw new NotFoundException('Somthing wrong with the server'); diff --git a/src/playlists/playlists.controller.spec.ts b/src/playlists/playlists.controller.spec.ts index 4336e7b..27aea19 100644 --- a/src/playlists/playlists.controller.spec.ts +++ b/src/playlists/playlists.controller.spec.ts @@ -145,7 +145,7 @@ describe('PlaylistsController', () => { }); describe('find one playlist by id', () => { - it('shoul return one comment', () => { + it('shoul return one playlist', () => { return request(app.getHttpServer()) .get(`/playlists/${playlist1._id}`) .expect(200) diff --git a/src/playlists/playlists.controller.ts b/src/playlists/playlists.controller.ts index 2773536..9579b2f 100644 --- a/src/playlists/playlists.controller.ts +++ b/src/playlists/playlists.controller.ts @@ -9,6 +9,7 @@ import { UseGuards, Request, Query, + UseInterceptors, } from '@nestjs/common'; import { PlaylistsService } from './playlists.service'; import { CreatePlaylistDto } from './dto/create-playlist.dto'; @@ -21,6 +22,7 @@ import { } from '@nestjs/swagger'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { IRequestWithUser } from '../users/interfaces/request-with-user.interface'; +import { ValidIdInterceptor } from '../utils/interceptors/valid-id.interceptor'; @ApiTags('playlists') @Controller('playlists') @@ -54,6 +56,7 @@ export class PlaylistsController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Find one playlist by id' }) + @UseInterceptors(ValidIdInterceptor) findOne(@Param('id') id: string) { return this.playlistsService.findPlaylistById(id); } @@ -62,6 +65,7 @@ export class PlaylistsController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Update title and add or delete tracks' }) + @UseInterceptors(ValidIdInterceptor) update( @Param('id') id: string, @Body() updatePlaylistDto: UpdatePlaylistDto, @@ -78,6 +82,7 @@ export class PlaylistsController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Delete a playlist' }) + @UseInterceptors(ValidIdInterceptor) remove(@Param('id') id: string, @Request() request: IRequestWithUser) { return this.playlistsService.removePlaylist(id, request.user); } diff --git a/src/playlists/playlists.service.spec.ts b/src/playlists/playlists.service.spec.ts index dbcb0b6..a8fd0e0 100644 --- a/src/playlists/playlists.service.spec.ts +++ b/src/playlists/playlists.service.spec.ts @@ -15,6 +15,7 @@ import { ICreateTrackResponse } from '../tracks/interfaces/track-create-response import { FilesService } from '../files/files.service'; import { MinioClientService } from '../minio-client/minio-client.service'; import { PaymentsService } from '../payments/payments.service'; +import { NonValidIdException } from '../utils/isValidId'; const abdou = data.users.abdou; const jayz = data.users.jayz; @@ -27,7 +28,8 @@ let user: UserDocument; let artist: UserDocument; let encore: ICreateTrackResponse; let threat: ICreateTrackResponse; -let playlistId: string; +let playlist1_id: string; +let playlist2_id: string; describe('PlaylistsService', () => { let playlistService: PlaylistsService; @@ -136,13 +138,14 @@ describe('PlaylistsService', () => { it('should return the first playlist', async () => { const playlist = await playlistService.createPlaylist(my_playlist1, user); - playlistId = playlist._id; + playlist1_id = playlist._id; expect(playlist._id).toBeDefined(); expect(playlist.title).toBe(my_playlist1.title); }); it('should return the second playlist', async () => { const playlist = await playlistService.createPlaylist(my_playlist2, user); + playlist2_id = playlist._id; expect(playlist._id).toBeDefined(); expect(playlist.title).toBe(my_playlist2.title); }); @@ -175,7 +178,7 @@ describe('PlaylistsService', () => { describe('When find one playlist by id', () => { it('should return one release', async () => { - const result = await playlistService.findPlaylistById(playlistId); + const result = await playlistService.findPlaylistById(playlist1_id); expect(result.title).toBe(my_playlist1.title); expect(result.owner).toStrictEqual(user._id); @@ -183,14 +186,32 @@ describe('PlaylistsService', () => { }); }); + describe('When find one playlist by id', () => { + it('should return an exception of id invalidity', async () => { + await expect(() => playlistService.findPlaylistById('1')).rejects.toThrow( + NonValidIdException, + ); + }); + }); + describe('When remove one playlist', () => { - it('should return one playlist infos', async () => { - const msg = 'Playlist deleted'; + const msg = 'Playlist deleted'; - const result = await playlistService.removePlaylist(playlistId, user); - expect(result.id).toBeDefined(); + it('should return one playlist infos', async () => { + const result = await playlistService.removePlaylist(playlist1_id, user); + expect(result.id).not.toBe(''); + expect(result.id).not.toBe(playlist2_id); expect(result.title).toBe(my_playlist1.title); expect(result.msg).toBe(msg); }); + + it('should return the second playlist infos', async () => { + const result = await playlistService.removePlaylist(playlist2_id, user); + + expect(result.id).not.toBe(''); + expect(result.id).not.toBe(playlist1_id); + expect(result.title).toBe(my_playlist2.title); + expect(result.msg).toBe(msg); + }); }); }); diff --git a/src/playlists/playlists.service.ts b/src/playlists/playlists.service.ts index 0293952..e463647 100644 --- a/src/playlists/playlists.service.ts +++ b/src/playlists/playlists.service.ts @@ -8,6 +8,7 @@ import { Model } from 'mongoose'; import { Track } from '../tracks/schemas/track.schema'; import { TracksService } from '../tracks/tracks.service'; import { UserDocument } from '../users/schemas/user.schema'; +import { isValidId } from '../utils/isValidId'; import { CreatePlaylistDto } from './dto/create-playlist.dto'; import { PlaylistUpdateTaskAction, @@ -51,6 +52,7 @@ export class PlaylistsService { } async findPlaylistById(id: string): Promise { + isValidId(id); const playlist = await this.playlistModel.findById(id); if (!playlist) { throw new NotFoundException(`Playlist with ID "${id}" not found.`); @@ -71,6 +73,7 @@ export class PlaylistsService { updatePlaylistDto: UpdatePlaylistDto, owner: UserDocument, ) { + isValidId(id); const playlist = await this.isUserTheOwnerOfPlaylist(id, owner); const trackId = updatePlaylistDto.trackId; @@ -103,7 +106,6 @@ export class PlaylistsService { title: updatePlaylistDto.title || playlist.title, tracks: newTracks, }; - await this.playlistModel.updateOne({ id: playlist._id }, updatePlaylist); return { @@ -114,9 +116,15 @@ export class PlaylistsService { } async removePlaylist(id: string, owner: UserDocument) { + isValidId(id); const playlist = await this.isUserTheOwnerOfPlaylist(id, owner); - await this.playlistModel.deleteOne({ id: playlist._id }); + try { + await playlist.remove(); + } catch (error) { + throw new Error("Can't delete playlist"); + } + return { id: playlist._id.toString(), title: playlist.title, diff --git a/src/releases/releases.controller.spec.ts b/src/releases/releases.controller.spec.ts index 9189043..3bfde66 100644 --- a/src/releases/releases.controller.spec.ts +++ b/src/releases/releases.controller.spec.ts @@ -153,7 +153,7 @@ describe('ReleasesController', () => { }); describe('find one release by id', () => { - it('shoul return one release', () => { + it('should return one release', () => { return request(app.getHttpServer()) .get(`/releases/${release._id}`) .expect(200) diff --git a/src/releases/releases.controller.ts b/src/releases/releases.controller.ts index 8c69460..fb24350 100644 --- a/src/releases/releases.controller.ts +++ b/src/releases/releases.controller.ts @@ -32,6 +32,7 @@ import { import { ApiMultiFileWithMetadata } from '../utils/swagger/multiple-file.decorator'; import { CreateReleaseWraperDto } from './dto/create-release-wraper.dto'; import { CreateReleaseDto } from './dto/create-release.dto'; +import { ValidIdInterceptor } from '../utils/interceptors/valid-id.interceptor'; @ApiTags('releases') @Controller('releases') @@ -77,6 +78,7 @@ export class ReleasesController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Find one release by id' }) + @UseInterceptors(ValidIdInterceptor) findOne(@Param('id') id: string) { return this.releasesService.findReleaseById(id); } @@ -92,6 +94,7 @@ export class ReleasesController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Update a release' }) + @UseInterceptors(ValidIdInterceptor) updateRelease( @Param('id') id: string, @Body() updateReleaseDto: UpdateReleaseDto, @@ -109,6 +112,7 @@ export class ReleasesController { @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Delete a release' }) @ApiCookieAuth('Set-Cookie') + @UseInterceptors(ValidIdInterceptor) removeRelease(@Param('id') id: string, @Request() request: IRequestWithUser) { return this.releasesService.removeRelease(id, request.user); } diff --git a/src/releases/releases.service.ts b/src/releases/releases.service.ts index 6acad7f..0ab212b 100644 --- a/src/releases/releases.service.ts +++ b/src/releases/releases.service.ts @@ -14,6 +14,7 @@ import { TracksService } from '../tracks/tracks.service'; import { UsersService } from '../users/users.service'; import { ICreateTrackResponse } from '../tracks/interfaces/track-create-response.interface'; import { UpdateReleaseDto } from './dto/update-release.dto'; +import { isValidId } from '../utils/isValidId'; @Injectable() export class ReleasesService { @@ -118,6 +119,7 @@ export class ReleasesService { } async findReleaseById(id: string): Promise { + isValidId(id); const release = await this.releaseModel.findById(id); if (!release) { throw new BadRequestException(`Release with ID "${id}" not found.`); @@ -138,10 +140,12 @@ export class ReleasesService { updateReleaseDto: UpdateReleaseDto, owner: UserDocument, ) { + isValidId(id); return `This action updates a #${id} release`; } async removeRelease(id: string, owner: UserDocument) { + isValidId(id); const release = await this.isUserTheOwnerOfRelease(id, owner); const session = await this.connection.startSession(); @@ -149,7 +153,7 @@ export class ReleasesService { session.startTransaction(); try { await this.tracksService.removeManyTracks(release.tracks, session); - await this.releaseModel.deleteOne({ id: release._id }); + await release.remove(); return { id: release._id.toString(), title: release.title, @@ -185,6 +189,7 @@ export class ReleasesService { } private async isUserTheOwnerOfRelease(id: string, owner: UserDocument) { + isValidId(id); const release = await this.findReleaseById(id); if (!release) { throw new NotFoundException('Somthing wrong with the server'); diff --git a/src/resource-packs/resource-packs.controller.ts b/src/resource-packs/resource-packs.controller.ts index 2a6a3e2..2e28860 100644 --- a/src/resource-packs/resource-packs.controller.ts +++ b/src/resource-packs/resource-packs.controller.ts @@ -30,6 +30,7 @@ import { FilesInterceptor } from '@nestjs/platform-express'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; import { ResourcePackFormDataParserInterceptor } from '../utils/interceptors/create-resource-pack.interceptor copy'; import { ApiMultiFileWithMetadata } from '../utils/swagger/multiple-file.decorator'; +import { ValidIdInterceptor } from '../utils/interceptors/valid-id.interceptor'; @ApiTags('resource-packs') @Controller('resource-packs') @@ -80,6 +81,7 @@ export class ResourcePacksController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Find one resource pack by id' }) + @UseInterceptors(ValidIdInterceptor) findOne(@Param('id') id: string) { return this.resourcePacksService.findResourcePackById(id); } @@ -88,6 +90,7 @@ export class ResourcePacksController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Update a resource pack' }) + @UseInterceptors(ValidIdInterceptor) update( @Param('id') id: string, @Body() updateResourcePackDto: UpdateResourcePackDto, @@ -105,6 +108,7 @@ export class ResourcePacksController { @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Delete a resource pack' }) @ApiCookieAuth('Set-Cookie') + @UseInterceptors(ValidIdInterceptor) remove(@Param('id') id: string, @Request() request: IRequestWithUser) { return this.resourcePacksService.removeResourcePack(id, request.user); } diff --git a/src/resource-packs/resource-packs.service.ts b/src/resource-packs/resource-packs.service.ts index 910487b..c91c4cf 100644 --- a/src/resource-packs/resource-packs.service.ts +++ b/src/resource-packs/resource-packs.service.ts @@ -16,6 +16,7 @@ import { import { ResourcesService } from '../resources/resources.service'; import { ICreateResourceResponse } from '../resources/interfaces/resource-create-response.interface'; import { IResourcePackResponse } from './interfaces/resource-pack-response.interface'; +import { isValidId } from '../utils/isValidId'; @Injectable() export class ResourcePacksService { @@ -125,6 +126,7 @@ export class ResourcePacksService { } async findResourcePackById(id: string): Promise { + isValidId(id); const resourcePack = await this.resourcePackModel.findById(id); if (!resourcePack) { throw new BadRequestException(`Resource pack with ID "${id}" not found.`); @@ -147,10 +149,12 @@ export class ResourcePacksService { updateResourcePackDto: UpdateResourcePackDto, owner: UserDocument, ) { + isValidId(id); return `This action updates a #${id} resource pack`; } async removeResourcePack(id: string, owner: UserDocument) { + isValidId(id); const resourcePack = await this.isUserTheOwnerOfResourcePack(id, owner); const session = await this.connection.startSession(); @@ -162,7 +166,7 @@ export class ResourcePacksService { session, ); - await this.resourcePackModel.deleteOne({ id: resourcePack._id }); + await resourcePack.remove(); return { id: resourcePack._id.toString(), title: resourcePack.title, diff --git a/src/resources/resources.service.ts b/src/resources/resources.service.ts index 6e0fbe5..c387896 100644 --- a/src/resources/resources.service.ts +++ b/src/resources/resources.service.ts @@ -12,6 +12,7 @@ import { Resource, ResourceDocument } from './schemas/resource.schema'; import { ICreateResourceResponse } from './interfaces/resource-create-response.interface'; import { IDeleteResourceResponse } from './interfaces/resource-delete-response.interface copy'; import { BucketName } from '../minio-client/minio-client.service'; +import { isValidId } from '../utils/isValidId'; @Injectable() export class ResourcesService { @@ -56,6 +57,7 @@ export class ResourcesService { } async findResourceById(id: string): Promise { + isValidId(id); const resource = await this.resourceModel.findById(id); if (!resource) { throw new BadRequestException(`Resource with ID "${id}" doesn't exist`); @@ -82,7 +84,7 @@ export class ResourcesService { if (!resource) { throw new NotFoundException('Somthing wrong with the server'); } - await this.resourceModel.deleteOne({ id: resource._id }, session); + await resource.remove(session); return { id: resource._id, title: resource.title, diff --git a/src/test-utils/data/mock_data.json b/src/test-utils/data/mock_data.json index 1da3083..c00aefd 100644 --- a/src/test-utils/data/mock_data.json +++ b/src/test-utils/data/mock_data.json @@ -1,31 +1,31 @@ { "users": { "abdou": { - "_id": "0", + "_id": "000000000000000000000000", "username": "al/ghom", "email": "96abdou96@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "yoni": { - "_id": "1", + "_id": "000000000000000000000001", "username": "Vagahbond", "email": "vagahbond@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "jayz": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "pharrell": { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "kanye": { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -33,12 +33,12 @@ }, "releases": { "black_album": { - "_id": "0", + "_id": "000000000000000000000000", "title": "balck album", "description": "one of the greatest", "coverUrl": "https://www.release.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -46,18 +46,18 @@ "feats": [], "tracks": [ { - "_id": "0", + "_id": "000000000000000000000000", "title": "change clothes", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -65,11 +65,11 @@ ] }, { - "_id": "1", + "_id": "000000000000000000000001", "title": "encore", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -77,12 +77,12 @@ "feats": [] }, { - "_id": "2", + "_id": "000000000000000000000002", "title": "threat", "fileName": "https://www.example.com", "author": [ { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -92,19 +92,19 @@ ] }, "wtt": { - "_id": "1", + "_id": "000000000000000000000001", "title": "Watch the Throne", "description": "just legendary", "coverUrl": "https://www.release.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -112,18 +112,18 @@ ], "tracks": [ { - "_id": "3", + "_id": "000000000000000000000003", "title": "New Day", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -131,18 +131,18 @@ ] }, { - "_id": "4", + "_id": "000000000000000000000004", "title": "Murder to Excellence", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -150,18 +150,18 @@ ] }, { - "_id": "5", + "_id": "000000000000000000000005", "title": "gotta have it", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -171,12 +171,12 @@ ] }, "imm": { - "_id": "2", + "_id": "000000000000000000000002", "title": "In My Mind", "description": "cool to hear", "coverUrl": "https://www.release.com", "author": { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -184,18 +184,18 @@ "feat": [], "tracks": { "0": { - "_id": "6", + "_id": "000000000000000000000006", "title": "Young Girl", "fileName": "https://www.example.com", "author": { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -207,7 +207,7 @@ "title": "angel", "fileName": "https://www.example.com", "author": { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -218,14 +218,14 @@ "title": "Number One", "fileName": "https://www.example.com", "author": { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -237,12 +237,12 @@ }, "resource_packs": { "resource_pack1": { - "_id": "0", + "_id": "000000000000000000000000", "title": "resource pack 1", "description": "my resource pack 1", "coverUrl": "https://www.resource-pack.com", "previewUrl": "https://www.resource-pack.com", - "author": "0", + "author": "000000000000000000000000", "resources": [ { "title": "resource 1", @@ -263,12 +263,12 @@ ] }, "resource_pack2": { - "_id": "1", + "_id": "000000000000000000000001", "title": "resource pack 2", "description": "my resource pack 2", "coverUrl": "https://www.resource-pack.com", "previewUrl": "https://www.resource-pack.com", - "author": "2", + "author": "000000000000000000000002", "resources": [ { "title": "resource 3", @@ -283,23 +283,31 @@ }, "playlists": { "fav_1": { - "_id": "0", + "_id": "000000000000000000000000", "title": "my playlist 1", - "owner": "0", - "tracks": ["0", "1", "2"] + "owner": "000000000000000000000000", + "tracks": [ + "000000000000000000000000", + "000000000000000000000001", + "000000000000000000000002" + ] }, "fav_2": { - "_id": "1", + "_id": "000000000000000000000001", "title": "my playlist 2", - "owner": "0", - "tracks": ["3", "4", "5"] + "owner": "000000000000000000000000", + "tracks": [ + "000000000000000000000003", + "000000000000000000000004", + "000000000000000000000005" + ] } }, "comments": { "comment_1": { - "_id": "0", + "_id": "000000000000000000000000", "content": "cool track", - "contentId": "0", + "contentId": "000000000000000000000000", "isPositive": true, "modelType": "Track", "owner": { @@ -308,9 +316,9 @@ } }, "comment_2": { - "_id": "1", + "_id": "000000000000000000000001", "content": "bad track", - "contentId": "1", + "contentId": "000000000000000000000001", "isPositive": false, "modelType": "Track", "owner": { @@ -319,9 +327,9 @@ } }, "comment_3": { - "_id": "2", + "_id": "000000000000000000000002", "content": "cool resource", - "contentId": "1", + "contentId": "000000000000000000000001", "isPositive": true, "modelType": "Resource", "owner": { @@ -332,18 +340,18 @@ }, "tracks": { "change_clothes": { - "_id": "0", + "_id": "000000000000000000000000", "title": "change clothes", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -351,11 +359,11 @@ ] }, "encore": { - "_id": "1", + "_id": "000000000000000000000001", "title": "encore", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -363,12 +371,12 @@ "feats": [] }, "threat": { - "_id": "2", + "_id": "000000000000000000000002", "title": "threat", "fileName": "https://www.example.com", "author": [ { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -376,18 +384,18 @@ ] }, "new_day": { - "_id": "3", + "_id": "000000000000000000000003", "title": "New Day", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -395,18 +403,18 @@ ] }, "mte": { - "_id": "4", + "_id": "000000000000000000000004", "title": "Murder to Excellence", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -414,18 +422,18 @@ ] }, "ghi": { - "_id": "5", + "_id": "000000000000000000000005", "title": "gotta have it", "fileName": "https://www.example.com", "author": { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -433,18 +441,18 @@ ] }, "young_girl": { - "_id": "6", + "_id": "000000000000000000000006", "title": "Young Girl", "fileName": "https://www.example.com", "author": { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "2", + "_id": "000000000000000000000002", "username": "jay-z", "email": "jay-z@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -456,7 +464,7 @@ "title": "angel", "fileName": "https://www.example.com", "author": { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -467,14 +475,14 @@ "title": "Number One", "fileName": "https://www.example.com", "author": { - "_id": "3", + "_id": "000000000000000000000003", "username": "pharrell williams", "email": "pharrell.williams@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" }, "feats": [ { - "_id": "4", + "_id": "000000000000000000000004", "username": "kanye west", "email": "kanye.west@gmail.com", "password": "$2b$10$v5vECbZOx0ybssiOkrS/o.s7Q6ejf/bri2jwH8WW48trelGBdW3Mm" @@ -484,28 +492,28 @@ }, "resources": { "resource_1": { - "_id": "0", + "_id": "000000000000000000000000", "title": "resource 1", "fileName": "https://www.example.com", - "author": "0" + "author": "000000000000000000000000" }, "resource_2": { - "_id": "1", + "_id": "000000000000000000000001", "title": "resource 2", "fileName": "https://www.example.com", - "author": "0" + "author": "000000000000000000000000" }, "resource_3": { - "_id": "2", + "_id": "000000000000000000000002", "title": "resource 3", "fileName": "https://www.example.com", - "author": "2" + "author": "000000000000000000000002" }, "resource_4": { - "_id": "3", + "_id": "000000000000000000000003", "title": "resource 3", "fileName": "https://www.example.com", - "author": "2" + "author": "000000000000000000000002" } }, "create_users": { @@ -819,7 +827,7 @@ 99, 104, 97, 110, 103, 101, 32, 99, 108, 111, 116, 104, 101, 115 ] }, - "author": "0" + "author": "000000000000000000000000" }, "resource2": { "title": "resource 2", @@ -828,7 +836,7 @@ "type": "Buffer", "data": [101, 110, 99, 111, 114, 101] }, - "author": "0" + "author": "000000000000000000000000" }, "resource3": { "title": "resource 3", @@ -841,19 +849,19 @@ }, "create_comments": { "comment_1": { - "contentId": "0", + "contentId": "000000000000000000000000", "isPositive": true, "content": "cool track", "typeOfContent": "Track" }, "comment_2": { - "contentId": "1", + "contentId": "000000000000000000000001", "isPositive": false, "content": "bad track", "typeOfContent": "Track" }, "comment_3": { - "contentId": "2", + "contentId": "000000000000000000000002", "isPositive": true, "content": "cool resource", "typeOfContent": "Resource" diff --git a/src/tracks/tracks.service.ts b/src/tracks/tracks.service.ts index 6976088..1e7e0ac 100644 --- a/src/tracks/tracks.service.ts +++ b/src/tracks/tracks.service.ts @@ -14,6 +14,7 @@ import { UserDocument } from '../users/schemas/user.schema'; import { IDeleteTrackResponse } from './interfaces/track-delete-response.interface copy'; import { FileMimeType } from '../files/dto/simple-create-file.dto'; import { BucketName } from '../minio-client/minio-client.service'; +import { isValidId } from '../utils/isValidId'; @Injectable() export class TracksService { @@ -75,6 +76,7 @@ export class TracksService { } async findTrackById(id: string): Promise { + isValidId(id); const track = await this.trackModel.findById(id); if (!track) { throw new BadRequestException(`Track with ID "${id}" doesn't exist`); @@ -100,7 +102,7 @@ export class TracksService { if (!track) { throw new NotFoundException('Somthing wrong with the server'); } - await this.trackModel.deleteOne({ id: track._id }, session); + await track.remove(session); return { id: track._id, title: track.title, diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts index cfdf413..1555adf 100644 --- a/src/users/users.controller.spec.ts +++ b/src/users/users.controller.spec.ts @@ -4,14 +4,12 @@ import { ValidationPipe, } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import RepoMockModel from '../test-utils/mocks/standard-mock.service.test'; +import { data2list } from '../test-utils/mocks/standard-mock.service.test'; import { UsersController } from './users.controller'; import { UsersService } from './users.service'; import * as request from 'supertest'; import { JwtModule } from '@nestjs/jwt'; import { AuthService } from '../auth/auth.service'; -import { getModelToken } from '@nestjs/mongoose'; -import { User } from './schemas/user.schema'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { JwtStrategy } from '../auth/strategies/jwt.strategy'; import { LocalStrategy } from '../auth/strategies/local.strategy'; @@ -24,6 +22,20 @@ import { PaymentsService } from '../payments/payments.service'; const author = data.users.jayz; const user = data.users.kanye; +const users = data2list(data.users).map((user) => ({ + id: user._id, + username: user.username, + email: user.email, +})); +const findByUsernameExpected = { + id: user._id, + username: user.username, + email: user.email, +}; +const delete_expected = { + email: user.email, + msg: 'user deleted', +}; describe('UsersController', () => { let app: INestApplication; @@ -42,7 +54,6 @@ describe('UsersController', () => { ], controllers: [UsersController, AuthController], providers: [ - UsersService, AuthService, JwtStrategy, FilesService, @@ -70,8 +81,33 @@ describe('UsersController', () => { }, }, { - provide: getModelToken(User.name), - useValue: new RepoMockModel(data.users, 4, 2), + provide: UsersService, + useValue: { + findUserByUsername: jest.fn(() => { + return { + ...user, + }; + }), + findUsers: jest.fn((username: string) => { + return username ? findByUsernameExpected : users; + }), + findUserById: jest.fn(() => { + return { + ...user, + }; + }), + updateUser: jest.fn(() => { + return {}; + }), + removeUser: jest.fn(() => { + return { + ...delete_expected, + }; + }), + find: jest.fn(() => { + return users; + }), + }, }, ], }) diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 9e8c14e..fbbdb19 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -6,6 +6,7 @@ import { UseGuards, Request, Query, + UseInterceptors, } from '@nestjs/common'; import { UsersService } from './users.service'; import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; @@ -16,6 +17,7 @@ import { ApiQuery, ApiTags, } from '@nestjs/swagger'; +import { ValidIdInterceptor } from '../utils/interceptors/valid-id.interceptor'; @ApiTags('users') @Controller('users') @@ -35,6 +37,7 @@ export class UsersController { @UseGuards(JwtAuthGuard) @ApiCookieAuth('Set-Cookie') @ApiOperation({ summary: 'Find one user by id' }) + @UseInterceptors(ValidIdInterceptor) findOneById(@Param('id') id: string) { return this.usersService.findUserById(id); } diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 2c99a27..2097a81 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -10,6 +10,7 @@ import { IRemoveResponse } from './interfaces/remove-response.interface'; import { IUserResponse } from './interfaces/user-response.interface'; import { User, UserDocument } from './schemas/user.schema'; import { PaymentsService } from '../payments/payments.service'; +import { isValidId } from '../utils/isValidId'; @Injectable() export class UsersService { @@ -47,11 +48,12 @@ export class UsersService { } async removeUser(userId: string): Promise { + isValidId(userId); const user = await this.userModel.findById(userId); if (!user) { throw new NotFoundException('Somthing wrong with the server'); } - await this.userModel.deleteOne({ id: user._id }); + await user.remove(); return { email: user.email, msg: 'user deleted', @@ -88,6 +90,7 @@ export class UsersService { } async findById(userId: string): Promise { + isValidId(userId); const user = await this.userModel.findById(userId); if (!user) { throw new BadRequestException("This user doesn't exist"); diff --git a/src/utils/interceptors/valid-id.interceptor.ts b/src/utils/interceptors/valid-id.interceptor.ts new file mode 100644 index 0000000..52ce2cc --- /dev/null +++ b/src/utils/interceptors/valid-id.interceptor.ts @@ -0,0 +1,17 @@ +import { + Injectable, + NestInterceptor, + ExecutionContext, + CallHandler, +} from '@nestjs/common'; +import { Observable } from 'rxjs'; +import { isValidId } from '../isValidId'; + +@Injectable() +export class ValidIdInterceptor implements NestInterceptor { + intercept(context: ExecutionContext, next: CallHandler): Observable { + const request = context.switchToHttp().getRequest(); + isValidId(request.params.id); + return next.handle(); + } +} diff --git a/src/utils/isValidId.ts b/src/utils/isValidId.ts new file mode 100644 index 0000000..ca91e3a --- /dev/null +++ b/src/utils/isValidId.ts @@ -0,0 +1,11 @@ +import { BadRequestException } from '@nestjs/common'; + +export const isValidId = (id: string): void => { + if (!/^[0-9a-fA-F]{24}$/.test(id)) throw new NonValidIdException(); +}; + +export class NonValidIdException extends Error { + constructor() { + super('Not valid id'); + } +}