-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Resolve dep cycle * Add likes endpoints and tests * Add route to check if user has liked an entity * Implement like and unliking entities * Created liked_by view * Change husky to run tests pre-push
- Loading branch information
1 parent
1453b48
commit 108ab48
Showing
28 changed files
with
1,532 additions
and
67 deletions.
There are no files selected for viewing
File renamed without changes.
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,13 @@ | ||
|
||
@postId = 65f34799838f561749795f94 | ||
|
||
@token = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiaWQiOiI2NWYyNWNmN2EwMDhlMzI2OTY2Mzc3MmYiLCJpYXQiOjE3MTA1NTk1MDEsImV4cCI6MTcxMDU2MzEwMX0.lNZkns3CWyu88knl6eUYslPEw9J9-sFFH43ItHzFgqE | ||
|
||
POST http://localhost:3001/api/likes | ||
Content-Type: application/json | ||
Authorization: bearer {{token}} | ||
|
||
{ | ||
"entityId": "{{postId}}", | ||
"entityModel": "Post" | ||
} |
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
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,14 @@ | ||
import mongoose from 'mongoose'; | ||
|
||
export default new mongoose.Schema( | ||
{ | ||
url: { | ||
type: String, | ||
required: true, | ||
}, | ||
publicId: { | ||
type: String, | ||
required: true, | ||
}, | ||
}, | ||
); |
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,33 @@ | ||
import mongoose from 'mongoose'; | ||
|
||
const likeSchema = new mongoose.Schema({ | ||
user: { | ||
type: mongoose.Schema.Types.ObjectId, | ||
required: true, | ||
ref: 'User', | ||
}, | ||
likedEntity: { | ||
id: { | ||
type: mongoose.Schema.Types.ObjectId, | ||
required: true, | ||
refPath: 'model', | ||
}, | ||
model: { | ||
type: String, | ||
required: true, | ||
enum: ['Post', 'Comment'], | ||
}, | ||
}, | ||
}, { | ||
timestamps: true, | ||
}); | ||
|
||
likeSchema.set('toJSON', { | ||
transform: (_document, returnedObject) => { | ||
returnedObject.id = returnedObject._id.toString(); | ||
delete returnedObject._id; | ||
delete returnedObject.__v; | ||
}, | ||
}); | ||
|
||
export default mongoose.model('Like', likeSchema); |
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
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,100 @@ | ||
import express from 'express'; | ||
import { authenticator } from '../utils/middleware'; | ||
import logger from '../utils/logger'; | ||
import likeService from '../services/like-service'; | ||
|
||
const router = express.Router(); | ||
|
||
router.post('/', authenticator(), async (req, res, next) => { | ||
const userId = req.userToken!.id; | ||
|
||
try { | ||
await likeService.addLike({ | ||
userId, | ||
entityId: req.body.entityId, | ||
entityModel: req.body.entityModel, | ||
}); | ||
|
||
return res.status(201).end(); | ||
} catch (error) { | ||
const errorMessage = logger.getErrorMessage(error); | ||
logger.error(errorMessage); | ||
|
||
if (/not found/i.test(errorMessage)) { | ||
return res.status(404).send({ error: errorMessage }); | ||
} | ||
|
||
if (/same entity twice/i.test(errorMessage)) { | ||
return res.status(400).send({ error: errorMessage }); | ||
} | ||
|
||
return next(error); | ||
} | ||
}); | ||
|
||
router.delete('/:entityId', authenticator(), async (req, res, next) => { | ||
const userId = req.userToken!.id; | ||
const { entityId } = req.params; | ||
|
||
try { | ||
await likeService.removeLikeByUserIdAndEntityId({ | ||
user: userId, | ||
entityId, | ||
}); | ||
|
||
return res.status(204).end(); | ||
} catch (error) { | ||
const errorMessage = logger.getErrorMessage(error); | ||
logger.error(errorMessage); | ||
|
||
return next(error); | ||
} | ||
}); | ||
|
||
router.get('/:entityId/likeCount', async (req, res, next) => { | ||
const { entityId } = req.params; | ||
|
||
try { | ||
const likeCount = await likeService.getLikeCountByEntityId(entityId); | ||
|
||
return res.status(200).send({ likeCount }); | ||
} catch (error) { | ||
const errorMessage = logger.getErrorMessage(error); | ||
logger.error(errorMessage); | ||
|
||
return next(error); | ||
} | ||
}); | ||
|
||
router.get('/:entityId/likes', async (req, res, next) => { | ||
const { entityId } = req.params; | ||
|
||
try { | ||
const likes = await likeService.getLikeUsersByEntityId(entityId); | ||
|
||
return res.status(200).send({ likes }); | ||
} catch (error) { | ||
const errorMessage = logger.getErrorMessage(error); | ||
logger.error(errorMessage); | ||
|
||
return next(error); | ||
} | ||
}); | ||
|
||
router.get('/:entityId/hasLiked', authenticator(), async (req, res, next) => { | ||
const userId = req.userToken!.id; | ||
const { entityId } = req.params; | ||
|
||
try { | ||
const hasLiked = await likeService.hasUserLikedEntity(userId, entityId); | ||
|
||
return res.status(200).send({ hasLiked }); | ||
} catch (error) { | ||
const errorMessage = logger.getErrorMessage(error); | ||
logger.error(errorMessage); | ||
|
||
return next(error); | ||
} | ||
}); | ||
|
||
export default router; |
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,77 @@ | ||
import { | ||
Like, Post, Comment, | ||
} from '../mongo'; | ||
import { NewLike } from '../types'; | ||
|
||
const addLike = async (newLikeFields: NewLike) => { | ||
let entity; | ||
|
||
if (newLikeFields.entityModel === 'Post') { | ||
entity = await Post.findById(newLikeFields.entityId); | ||
} else if (newLikeFields.entityModel === 'Comment') { | ||
entity = await Comment.findById(newLikeFields.entityId); | ||
} | ||
|
||
if (!entity) { | ||
throw new Error('Entity not found'); | ||
} | ||
|
||
const existingLike = await Like.findOne({ | ||
'likedEntity.id': newLikeFields.entityId, | ||
user: newLikeFields.userId, | ||
}); | ||
|
||
if (existingLike) { | ||
throw new Error('Can not like the same entity twice'); | ||
} | ||
|
||
await Like.create({ | ||
user: newLikeFields.userId, | ||
likedEntity: { | ||
id: newLikeFields.entityId, | ||
model: newLikeFields.entityModel, | ||
}, | ||
}); | ||
}; | ||
|
||
const removeLikeByUserIdAndEntityId = async ( | ||
{ user, entityId } : { user: string, entityId: string, }, | ||
) => { | ||
await Like.findOneAndDelete({ | ||
'likedEntity.id': entityId, | ||
user, | ||
}); | ||
}; | ||
|
||
const getLikeCountByEntityId = async (entityId: string) => { | ||
const likeCount = await Like.countDocuments({ | ||
'likedEntity.id': entityId, | ||
}); | ||
|
||
return likeCount; | ||
}; | ||
|
||
const getLikeUsersByEntityId = async (entityId: string) => { | ||
const likes = await Like.find({ | ||
'likedEntity.id': entityId, | ||
}).populate('user', 'username'); | ||
|
||
return likes.map((like) => like.user); | ||
}; | ||
|
||
const hasUserLikedEntity = async (userId: string, entityId: string) => { | ||
const like = await Like.findOne({ | ||
'likedEntity.id': entityId, | ||
user: userId, | ||
}); | ||
|
||
return !!like; | ||
}; | ||
|
||
export default { | ||
addLike, | ||
removeLikeByUserIdAndEntityId, | ||
getLikeCountByEntityId, | ||
getLikeUsersByEntityId, | ||
hasUserLikedEntity, | ||
}; |
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.