Skip to content

Commit

Permalink
delete like endpoint, return liked posts
Browse files Browse the repository at this point in the history
  • Loading branch information
yebrahim committed Sep 4, 2022
1 parent 0b0fcb2 commit 70c9135
Show file tree
Hide file tree
Showing 12 changed files with 87 additions and 30 deletions.
1 change: 1 addition & 0 deletions server/datastore/dao/LikeDao.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Like } from '@codersquare/shared';

export interface LikeDao {
createLike(like: Like): Promise<void>;
deleteLike(like: Like): Promise<void>;
getLikes(postId: string): Promise<number>;
exists(like: Like): Promise<boolean>;
}
4 changes: 2 additions & 2 deletions server/datastore/dao/PostDao.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Post } from '@codersquare/shared';

export interface PostDao {
listPosts(): Promise<Post[]>;
listPosts(userId?: string): Promise<Post[]>;
createPost(post: Post): Promise<void>;
getPost(id: string): Promise<Post | undefined>;
getPost(id: string, userId?: string): Promise<Post | undefined>;
deletePost(id: string): Promise<void>;
}
28 changes: 24 additions & 4 deletions server/datastore/sql/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,13 @@ export class SqlDataStore implements Datastore {
return this.db.get<User>(`SELECT * FROM users WHERE userName = ?`, userName);
}

listPosts(): Promise<Post[]> {
return this.db.all<Post[]>('SELECT * FROM posts ORDER BY postedAt DESC');
listPosts(userId?: string): Promise<Post[]> {
return this.db.all<Post[]>(
`SELECT *, EXISTS(
SELECT 1 FROM likes WHERE likes.postId = posts.id AND likes.userId = ?
) as liked FROM posts ORDER BY postedAt DESC`,
userId
);
}

async createPost(post: Post): Promise<void> {
Expand All @@ -63,8 +68,15 @@ export class SqlDataStore implements Datastore {
);
}

async getPost(id: string): Promise<Post | undefined> {
return await this.db.get<Post>('SELECT * FROM posts WHERE id = ?', id);
async getPost(id: string, userId: string): Promise<Post | undefined> {
return await this.db.get<Post>(
`SELECT *, EXISTS(
SELECT 1 FROM likes WHERE likes.postId = ? AND likes.userId = ?
) as liked FROM posts WHERE id = ?`,
id,
userId,
id
);
}

async deletePost(id: string): Promise<void> {
Expand All @@ -75,6 +87,14 @@ export class SqlDataStore implements Datastore {
await this.db.run('INSERT INTO likes(userId, postId) VALUES(?,?)', like.userId, like.postId);
}

async deleteLike(like: Like): Promise<void> {
await this.db.run(
'DELETE FROM likes WHERE userId = ? AND postId = ?',
like.userId,
like.postId
);
}

async createComment(comment: Comment): Promise<void> {
await this.db.run(
'INSERT INTO Comments(id, userId, postId, comment, postedAt) VALUES(?,?,?,?,?)',
Expand Down
2 changes: 1 addition & 1 deletion server/handlers/commentHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class CommentHandler {
if (!req.params.postId) return res.status(400).send({ error: 'Post ID is missing' });
if (!req.body.comment) return res.status(400).send({ error: 'Comment is missing' });

if (!(await this.db.getPost(req.params.postId))) {
if (!(await this.db.getPost(req.params.postId, res.locals.userId))) {
return res.status(404).send({ error: 'No post found with this ID' });
}

Expand Down
24 changes: 19 additions & 5 deletions server/handlers/likeHandler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CreateLikeResponse, Like, ListLikesResponse } from '@codersquare/shared';
import { Like, ListLikesResponse } from '@codersquare/shared';

import { Datastore } from '../datastore';
import { ExpressHandlerWithParams } from '../types';
Expand All @@ -10,10 +10,7 @@ export class LikeHandler {
this.db = db;
}

public create: ExpressHandlerWithParams<{ postId: string }, null, CreateLikeResponse> = async (
req,
res
) => {
public create: ExpressHandlerWithParams<{ postId: string }, null, {}> = async (req, res) => {
if (!req.params.postId) {
return res.status(400).send({ error: 'Post ID missing' });
}
Expand All @@ -38,6 +35,23 @@ export class LikeHandler {
return res.sendStatus(200);
};

public delete: ExpressHandlerWithParams<{ postId: string }, null, {}> = async (req, res) => {
if (!req.params.postId) {
return res.status(400).send({ error: 'Post ID missing' });
}
if (!(await this.db.getPost(req.params.postId))) {
return res.status(404).send({ error: 'No post found with this ID' });
}

const likeForDelete: Like = {
postId: req.params.postId,
userId: res.locals.userId,
};

this.db.deleteLike(likeForDelete);
return res.sendStatus(200);
};

public list: ExpressHandlerWithParams<{ postId: string }, null, ListLikesResponse> = async (
req,
res
Expand Down
5 changes: 3 additions & 2 deletions server/handlers/postHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ export class PostHandler {

public list: ExpressHandler<ListPostsRequest, ListPostsResponse> = async (_, res) => {
// TODO: add pagination and filtering
return res.send({ posts: await this.db.listPosts() });
const userId = res.locals.userId;
return res.send({ posts: await this.db.listPosts(userId) });
};

public create: ExpressHandler<CreatePostRequest, CreatePostResponse> = async (req, res) => {
Expand Down Expand Up @@ -54,7 +55,7 @@ export class PostHandler {
res
) => {
if (!req.params.id) return res.sendStatus(400);
const postToReturn: Post | undefined = await this.db.getPost(req.params.id);
const postToReturn: Post | undefined = await this.db.getPost(req.params.id, res.locals.userId);
if (!postToReturn) {
return res.sendStatus(404);
}
Expand Down
1 change: 1 addition & 0 deletions server/handlers/userHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export class UserHandler {
return res.sendStatus(500);
}
return res.send({
id: user.id,
firstName: user.firstName,
lastName: user.lastName,
userName: user.userName,
Expand Down
29 changes: 19 additions & 10 deletions server/middleware/authMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
import { verifyJwt } from '../auth';
import { db } from '../datastore';
import { ExpressHandler } from '../types';
import { ExpressHandler, JwtObject } from '../types';

export const authMiddleware: ExpressHandler<any, any> = async (req, res, next) => {
export const jwtParseMiddleware: ExpressHandler<any, any> = async (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.sendStatus(401);
return next();
}

let payload: JwtObject;
try {
const payload = verifyJwt(token);
const user = await db.getUserById(payload.userId);
if (!user) {
return res.status(401).send({ error: 'User not found' });
}
res.locals.userId = user.id;
next();
payload = verifyJwt(token);
} catch {
return res.status(401).send({ error: 'Bad token' });
}

const user = await db.getUserById(payload.userId);
if (!user) {
return res.status(401).send({ error: 'User not found' });
}
res.locals.userId = user.id;
return next();
};

export const enforceJwtMiddleware: ExpressHandler<any, any> = async (_, res, next) => {
if (!res.locals.userId) {
return res.sendStatus(401);
}
return next();
};
12 changes: 9 additions & 3 deletions server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { CommentHandler } from './handlers/commentHandler';
import { LikeHandler } from './handlers/likeHandler';
import { PostHandler } from './handlers/postHandler';
import { UserHandler } from './handlers/userHandler';
import { authMiddleware } from './middleware/authMiddleware';
import { enforceJwtMiddleware, jwtParseMiddleware } from './middleware/authMiddleware';
import { errHandler } from './middleware/errorMiddleware';
import { loggerMiddleware } from './middleware/loggerMiddleware';

Expand Down Expand Up @@ -54,6 +54,7 @@ export async function createServer(dbPath: string, logRequests = true) {

[Endpoints.listLikes]: likeHandler.list,
[Endpoints.createLike]: likeHandler.create,
[Endpoints.deleteLike]: likeHandler.delete,

[Endpoints.countComments]: commentHandler.count,
[Endpoints.listComments]: commentHandler.list,
Expand All @@ -67,8 +68,13 @@ export async function createServer(dbPath: string, logRequests = true) {
const handler = HANDLERS[entry as Endpoints];

config.auth
? app[config.method](config.url, authMiddleware, asyncHandler(handler))
: app[config.method](config.url, asyncHandler(handler));
? app[config.method](
config.url,
jwtParseMiddleware,
enforceJwtMiddleware,
asyncHandler(handler)
)
: app[config.method](config.url, jwtParseMiddleware, asyncHandler(handler));
});

app.use(errHandler);
Expand Down
7 changes: 4 additions & 3 deletions shared/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ export interface ListCommentsResponse {
export type DeleteCommentResponse = {};

// Like APIs
export interface CreateLikeResponse {}

export interface ListLikesResponse {
likes: Number;
}
Expand All @@ -56,7 +54,10 @@ export type GetUserRequest = {};
export type GetUserResponse = Pick<User, 'id' | 'firstName' | 'lastName' | 'userName'>;

export type GetCurrentUserRequest = {};
export type GetCurrentUserResponse = Pick<User, 'firstName' | 'lastName' | 'userName' | 'email'>;
export type GetCurrentUserResponse = Pick<
User,
'id' | 'firstName' | 'lastName' | 'userName' | 'email'
>;

export type GetUserByEmailRequest = { emailId: string };
export interface GetUserByEmailResponse {
Expand Down
2 changes: 2 additions & 0 deletions shared/src/endpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export enum Endpoints {

listLikes = 'listLikes',
createLike = 'createLike',
deleteLike = 'deleteLike',

countComments = 'countComments',
listComments = 'listComments',
Expand All @@ -37,6 +38,7 @@ export const ENDPOINT_CONFIGS: { [key in Endpoints]: EndpointConfig } = {

[Endpoints.listLikes]: { method: 'get', url: '/api/v1/likes/:postId' },
[Endpoints.createLike]: { method: 'post', url: '/api/v1/likes/:postId', auth: true },
[Endpoints.deleteLike]: { method: 'delete', url: '/api/v1/likes/:postId', auth: true },

[Endpoints.countComments]: { method: 'get', url: '/api/v1/comments/:postId/count' },
[Endpoints.listComments]: { method: 'get', url: '/api/v1/comments/:postId' },
Expand Down
2 changes: 2 additions & 0 deletions shared/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface Post {
url: string;
userId: string;
postedAt: number;
liked?: boolean;
}

export interface Like {
Expand All @@ -26,4 +27,5 @@ export interface Comment {
postId: string;
comment: string;
postedAt: number;
liked?: boolean;
}

0 comments on commit 70c9135

Please sign in to comment.