-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2 from adelinaenache/feat/connections-controller
feat: add connections controller and move specific endpoints from use…
- Loading branch information
Showing
9 changed files
with
280 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Type } from 'class-transformer'; | ||
import { IsDate } from 'class-validator'; | ||
|
||
export class ConnectionDto { | ||
name?: string; | ||
headline?: string; | ||
profilePictureUrl?: string; | ||
id: string; | ||
email: string; | ||
@Type(() => Date) | ||
@IsDate() | ||
joinedAt: Date; | ||
|
||
reviewsCount: number; | ||
connectionsCount: number; | ||
isConnection: boolean; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { ConnectionsController } from './connections.controller'; | ||
|
||
describe('ConnectionsController', () => { | ||
let controller: ConnectionsController; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
controllers: [ConnectionsController], | ||
}).compile(); | ||
|
||
controller = module.get<ConnectionsController>(ConnectionsController); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(controller).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import { Controller, Delete, Get, Param, Post } from '@nestjs/common'; | ||
import { CurrentUser } from 'src/decorators/current-user.decorator'; | ||
import { ConnectionsService } from './connections.service'; | ||
import { ConnectionDto } from './DTO/Connection.dto'; | ||
import { | ||
ApiBadRequestResponse, | ||
ApiBearerAuth, | ||
ApiNotFoundResponse, | ||
} from '@nestjs/swagger'; | ||
import { User } from '@prisma/client'; | ||
import { Public } from 'src/decorators/public.decorator'; | ||
|
||
@ApiBearerAuth() | ||
@Controller('connections') | ||
export class ConnectionsController { | ||
constructor(private connectionsService: ConnectionsService) {} | ||
/** | ||
* | ||
* Get all of the current user's connections | ||
*/ | ||
@Get() | ||
async getAllConnections(@CurrentUser() user: User): Promise<ConnectionDto[]> { | ||
return this.connectionsService.getUserConnections(user.id); | ||
} | ||
|
||
/** | ||
* Get "suggested for review" connections. | ||
*/ | ||
@Get('/suggested') | ||
async getSuggestedConnections( | ||
@CurrentUser() user: User, | ||
): Promise<ConnectionDto[]> { | ||
return this.connectionsService.getUserConnections(user.id); | ||
} | ||
|
||
/** | ||
* Search an user by query. | ||
*/ | ||
@Get('/search/:query') | ||
@Public() | ||
@ApiBadRequestResponse() | ||
async searchUser( | ||
@Param('query') query: string, | ||
@CurrentUser() user?: User, | ||
): Promise<ConnectionDto[]> { | ||
return this.connectionsService.searchUsers(user?.id, query); | ||
} | ||
|
||
/** | ||
* Create a connection between current User and another user. | ||
*/ | ||
@Post('/connect/:userId') | ||
async connectWithUser( | ||
@CurrentUser() user: User, | ||
@Param('userId') userId: string, | ||
): Promise<ConnectionDto> { | ||
return this.connectionsService.connectWithUser(user.id, userId); | ||
} | ||
/** | ||
* | ||
* Remove connection between current user and another user. | ||
*/ | ||
@Delete('/connect/:userId') | ||
async unconnectWithUser( | ||
@CurrentUser() user: User, | ||
@Param('userId') userId: string, | ||
): Promise<ConnectionDto> { | ||
return this.connectionsService.unconnectWithUser(user.id, userId); | ||
} | ||
|
||
/** | ||
* Get a connection. | ||
*/ | ||
@Get('/:userId') | ||
@ApiNotFoundResponse() | ||
async getUser( | ||
@CurrentUser() user: User, | ||
@Param('userId') userId: string, | ||
): Promise<ConnectionDto> { | ||
return this.connectionsService.getConnection(userId, user.id); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { ConnectionsService } from './connections.service'; | ||
import { ConnectionsController } from './connections.controller'; | ||
|
||
@Module({ | ||
providers: [ConnectionsService], | ||
controllers: [ConnectionsController], | ||
}) | ||
export class ConnectionsModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { Test, TestingModule } from '@nestjs/testing'; | ||
import { ConnectionsService } from './connections.service'; | ||
|
||
describe('ConnectionsService', () => { | ||
let service: ConnectionsService; | ||
|
||
beforeEach(async () => { | ||
const module: TestingModule = await Test.createTestingModule({ | ||
providers: [ConnectionsService], | ||
}).compile(); | ||
|
||
service = module.get<ConnectionsService>(ConnectionsService); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(service).toBeDefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import { | ||
BadRequestException, | ||
Injectable, | ||
NotFoundException, | ||
} from '@nestjs/common'; | ||
import { User } from '@prisma/client'; | ||
import { PrismaService } from 'src/prisma/prisma.service'; | ||
import { ConnectionDto } from './DTO/Connection.dto'; | ||
import { ApiTags } from '@nestjs/swagger'; | ||
|
||
@Injectable() | ||
@ApiTags('Connections controller') | ||
export class ConnectionsService { | ||
constructor(private readonly prisma: PrismaService) {} | ||
|
||
// retuns an object to be used with prisma's include | ||
private includeWithUserConnection(currentUserId?: User['id']) { | ||
return { | ||
_count: { | ||
select: { | ||
followings: true, | ||
reviewsReceived: true, | ||
}, | ||
}, | ||
followers: currentUserId | ||
? { | ||
where: { | ||
followerId: currentUserId, | ||
}, | ||
} | ||
: false, | ||
}; | ||
} | ||
|
||
// transforms an user from db to a Connection DTO | ||
private transformUserConnection( | ||
user: User & { _count: { followings: number; reviewsReceived: number } } & { | ||
followers?: { id: number; followerId: string; followingId: string }[]; | ||
}, | ||
): ConnectionDto { | ||
return { | ||
id: user.id, | ||
email: user.email, | ||
name: user.name, | ||
headline: user.headline, | ||
profilePictureUrl: user.profilePictureUrl, | ||
|
||
joinedAt: user.joinedAt, | ||
|
||
reviewsCount: user._count.reviewsReceived, | ||
connectionsCount: user._count.followings, | ||
isConnection: user.followers && user.followers.length !== 0, | ||
}; | ||
} | ||
|
||
async getConnection(connectionId: User['id'], currentUserId: User['id']) { | ||
const user = await this.prisma.user.findUnique({ | ||
where: { | ||
id: connectionId, | ||
}, | ||
include: this.includeWithUserConnection(currentUserId), | ||
}); | ||
|
||
if (!user) { | ||
throw new NotFoundException(`User with ${connectionId} not found`); | ||
} | ||
|
||
return this.transformUserConnection(user); | ||
} | ||
|
||
async getUserConnections(userId: User['id']) { | ||
const connections = await this.prisma.connection.findMany({ | ||
where: { | ||
followerId: userId, | ||
}, | ||
include: { | ||
following: { | ||
include: this.includeWithUserConnection(userId), | ||
}, | ||
}, | ||
}); | ||
|
||
return connections.map((c) => this.transformUserConnection(c.following)); | ||
} | ||
|
||
async connectWithUser(currentUserId: User['id'], userId: User['id']) { | ||
await this.prisma.connection.upsert({ | ||
where: { | ||
followerId_followingId: { | ||
followerId: currentUserId, | ||
followingId: userId, | ||
}, | ||
}, | ||
update: {}, | ||
create: { | ||
followerId: currentUserId, | ||
followingId: userId, | ||
}, | ||
}); | ||
|
||
return this.getConnection(userId, currentUserId); | ||
} | ||
|
||
async unconnectWithUser(currentUserId: User['id'], userId: User['id']) { | ||
await this.prisma.connection.delete({ | ||
where: { | ||
followerId_followingId: { | ||
followerId: currentUserId, | ||
followingId: userId, | ||
}, | ||
}, | ||
}); | ||
return this.getConnection(userId, currentUserId); | ||
} | ||
|
||
async searchUsers(userId?: User['id'], searchTerm?: string) { | ||
if (!searchTerm || searchTerm === '') { | ||
throw new BadRequestException('Search term is required'); | ||
} | ||
const users = await this.prisma.user.findMany({ | ||
where: { | ||
OR: [ | ||
{ email: { contains: searchTerm, mode: 'insensitive' } }, | ||
{ name: { contains: searchTerm, mode: 'insensitive' } }, | ||
], | ||
}, | ||
include: this.includeWithUserConnection(userId), | ||
}); | ||
|
||
return users.map((u) => this.transformUserConnection(u)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.