Changed TSC in favor of SWC. Added new tests, added new endpoints.

This commit is contained in:
Hackntosh 2023-06-26 14:01:40 -03:00
parent b21a832c13
commit d07ccddb10
33 changed files with 379 additions and 72 deletions

1
.swcrc
View file

@ -10,7 +10,6 @@
"baseUrl": "./"
},
"exclude": [
"tests/",
"@types/",
"interfaces/"
],

View file

@ -4,6 +4,7 @@
A simple social media created with React Native and Express.
## To-do - Backend
- Create/update/delete Posts
- Password recuperation
- Two step verification
@ -13,7 +14,8 @@ A simple social media created with React Native and Express.
- Probably pinned posts
- Authentication ✅
- Add more verification (like, if the password is too short)
- Set display name
## License
[MIT](https://choosealicense.com/licenses/mit/)

View file

@ -0,0 +1,22 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import { Router } from 'express'
// Controllers
import postCreateController from './posts/post-create'
import postDeleteController from './posts/post-delete'
import postInfoController from './posts/post-info'
import postUpdateController from './posts/post-update'
// Middlewares
import ensureAuthenticated from '../middlewares/ensure-authenticated'
const postsRouter = Router()
// Posts related
postsRouter.post('/create', ensureAuthenticated, postCreateController)
postsRouter.post('/delete', ensureAuthenticated, postDeleteController)
postsRouter.get('/info', postInfoController)
postsRouter.put('/update', postUpdateController)
export default postsRouter

View file

@ -1,11 +1,11 @@
import createPostService from '../../services/post/create-post'
import { posts } from '../../services/index'
import { Request, Response } from 'express'
async function createPostController (req: Request, res: Response): Promise<void> {
async function postCreateController (req: Request, res: Response): Promise<void> {
const { content } = req.body
const id: string = req.user?.id ?? ''
const result = await createPostService(content, id)
const result = await posts.postCreate(content, id)
if (result instanceof Error) {
res.status(400).json({
@ -17,4 +17,4 @@ async function createPostController (req: Request, res: Response): Promise<void>
res.json(result)
}
export default createPostController
export default postCreateController

View file

@ -0,0 +1,20 @@
import { posts } from '../../services/index'
import { Request, Response } from 'express'
async function postDeleteController (req: Request, res: Response): Promise<void> {
const userId = req.user?.id ?? ''
const postId = req.body.postId
const result = await posts.postDelete(postId, userId)
if (result instanceof Error) {
res.status(400).json({
error: result.message
})
return
}
res.json(result)
}
export default postDeleteController

View file

@ -0,0 +1,26 @@
import { posts } from '../../services/index'
import { Request, Response } from 'express'
async function postInfoController (req: Request, res: Response): Promise<void> {
const id = req.query.id as string
if (id === undefined) {
res.status(400).json({
error: 'Missing username'
})
return
}
const result = await posts.postInfo(id)
if (result instanceof Error) {
res.status(400).json({
error: result.message
})
return
}
res.json(result)
}
export default postInfoController

View file

@ -0,0 +1,17 @@
import { posts } from '../../services/index'
import { Request, Response } from 'express'
async function postUpdateController (req: Request, res: Response): Promise<void> {
const result = await posts.postUpdate()
if (result instanceof Error) {
res.status(400).json({
error: result.message
})
return
}
res.json(result)
}
export default postUpdateController

View file

@ -0,0 +1,24 @@
/* eslint-disable @typescript-eslint/no-misused-promises */
import { Router } from 'express'
// Controllers
import userAuthController from './users/user-auth'
import userDeleteController from './users/user-delete'
import userInfoController from './users/user-info'
import userSignupController from './users/user-signup'
import userUpdateController from './users/user-update'
// Middlewares
import ensureAuthenticated from '../middlewares/ensure-authenticated'
const usersRouter = Router()
// Users related
usersRouter.post('/auth', userAuthController)
usersRouter.post('/delete', ensureAuthenticated, userDeleteController)
usersRouter.get('/info', userInfoController)
usersRouter.post('/signup', userSignupController)
usersRouter.put('/update', ensureAuthenticated, userUpdateController)
export default usersRouter

View file

@ -1,10 +1,10 @@
import userAuthService from '../../services/user/user-auth'
import { users } from '../../services/index'
import type { Request, Response } from 'express'
async function userAuthController (req: Request, res: Response): Promise<void> {
const { email, password } = req.body
const result = await userAuthService(email, password)
const result = await users.userAuth(email, password)
if (result instanceof Error) {
res.status(400).json({

View file

@ -0,0 +1,18 @@
import { users } from '../../services'
import { Request, Response } from 'express'
async function userDeleteController (req: Request, res: Response): Promise<void> {
const userId = req.user?.id ?? ''
const result = await users.userDelete(userId)
if (result instanceof Error) {
res.status(400).json({
error: result.message
})
return
}
res.json(result)
}
export default userDeleteController

View file

@ -1,10 +1,17 @@
import userInfoService from '../../services/user/user-info'
import { users } from '../../services'
import type { Request, Response } from 'express'
async function userInfoController (req: Request, res: Response): Promise<void> {
const id = req.user?.id ?? ''
const username = req.query.u as string
const result = await userInfoService(id)
if (username === undefined) {
res.status(400).json({
error: 'Missing username'
})
return
}
const result = await users.userInfo(username.toLowerCase())
if (result instanceof Error) {
res.status(400).json({

View file

@ -1,10 +1,10 @@
import userSignupService from '../../services/user/user-signup'
import { users } from '../../services'
import type { Request, Response } from 'express'
async function userSignupController (req: Request, res: Response): Promise<void> {
const { username, email, password } = req.body
const result = await userSignupService(username, email, password)
const result = await users.userSignup(username, email, password)
if (result instanceof Error) {
res.status(400).json({

View file

@ -0,0 +1,17 @@
import { users } from '../../services'
import { Request, Response } from 'express'
async function userUpdateController (req: Request, res: Response): Promise<void> {
const result = await users.userUpdate()
if (result instanceof Error) {
res.status(400).json({
error: result.message
})
return
}
res.json(result)
}
export default userUpdateController

View file

@ -1,22 +1,12 @@
import { Router } from 'express'
// Controllers
import userSignupController from './controllers/user/user-signup'
import userAuthController from './controllers/user/user-auth'
import userInfoController from './controllers/user/user-info'
import createPostController from './controllers/post/create-post'
// Middlewares
import ensureAuthenticated from './middlewares/ensure-authenticated'
// Routers
import usersRouter from './controllers/users-router'
import postsRouter from './controllers/posts-router'
const router = Router()
// User related
router.post('/user/auth', userAuthController)
router.post('/user/create', userSignupController)
router.get('/user/info', ensureAuthenticated, userInfoController)
// Post related
router.post('/post/create', ensureAuthenticated, createPostController)
router.use('/user', usersRouter)
router.use('/post', postsRouter)
export default router

29
src/services/index.ts Normal file
View file

@ -0,0 +1,29 @@
import userAuthService from './users/user-auth'
import userDeleteService from './users/user-delete'
import userInfoService from './users/user-info'
import userSignupService from './users/user-signup'
import userUpdateService from './users/user-update'
import postCreateService from './posts/post-create'
import postDeleteService from './posts/post-delete'
import postInfoService from './posts/post-info'
import postUpdateService from './posts/post-update'
// User services
const users = {
userAuth: userAuthService,
userDelete: userDeleteService,
userInfo: userInfoService,
userSignup: userSignupService,
userUpdate: userUpdateService
}
// Post services
const posts = {
postCreate: postCreateService,
postDelete: postDeleteService,
postInfo: postInfoService,
postUpdate: postUpdateService
}
export { users, posts }

View file

@ -1,6 +1,6 @@
import prisma from '../../db'
async function createPostService (content: string, authorId: string): Promise<Object | Error> {
async function postCreateService (content: string, authorId: string): Promise<Object | Error> {
const user = await prisma.user.findFirst({ where: { id: authorId } })
if (user === null) {
@ -17,4 +17,4 @@ async function createPostService (content: string, authorId: string): Promise<Ob
return post
}
export default createPostService
export default postCreateService

View file

@ -0,0 +1,29 @@
import prisma from '../../db'
async function postDeleteService (postId: string, userId: string): Promise<Object | Error> {
const post = await prisma.post.findFirst({ where: { id: postId } })
console.log(postId, userId)
if (post === null) {
return new Error('Post not found')
}
if (await prisma.user.findFirst({ where: { id: userId } }) === null) {
return new Error('User not found')
}
if (post.authorId !== userId) {
return new Error('Forbidden')
}
await prisma.post.deleteMany({
where: {
id: postId
}
})
return {}
}
export default postDeleteService

View file

@ -0,0 +1,29 @@
import prisma from '../../db'
async function postInfoService (id: string): Promise<Object | Error> {
const post = await prisma.post.findFirst({
where: {
id
},
select: {
id: true,
content: true,
createdAt: true,
updatedAt: true,
author: {
select: {
displayName: true,
username: true
}
}
}
})
if (post === null) {
return new Error('Post not found')
}
return post
}
export default postInfoService

View file

@ -0,0 +1,6 @@
import prisma from '../../db'
async function postUpdateService (): Promise<Object | Error> {
return {}
}
export default postUpdateService

View file

@ -0,0 +1,29 @@
import prisma from '../../db'
async function userDeleteService (userId: string): Promise<Object | Error> {
const user = await prisma.user.findFirst({ where: { id: userId } })
if (user === null) {
return new Error('User not found')
}
if (user.id !== userId) {
return new Error('Forbidden')
}
await prisma.post.deleteMany({
where: {
authorId: user.id
}
})
await prisma.user.deleteMany({
where: {
id: userId
}
})
return {}
}
export default userDeleteService

View file

@ -1,15 +1,22 @@
import prisma from '../../db'
async function userInfoService (id: string): Promise<Object> {
async function userInfoService (username: string): Promise<Object> {
const user = await prisma.user.findFirst({
where: {
id
username
},
select: {
id: true,
displayName: true,
username: true,
createdAt: true
createdAt: true,
posts: {
select: {
id: true,
content: true,
createdAt: true,
updatedAt: true
}
}
}
})

View file

@ -28,7 +28,7 @@ async function userSignupService (username: string, email: string, password: str
const user = await prisma.user.create({
data: {
username,
username: username.toLowerCase(),
email,
password: hashedPassword
},

View file

@ -0,0 +1,7 @@
import prisma from '../../db'
async function userUpdateService (): Promise<Object | Error> {
return {}
}
export default userUpdateService

View file

@ -6,7 +6,7 @@ let token = ''
describe('POST /post/create', () => {
beforeAll(async () => {
await request(app).post('/user/create').send({
await request(app).post('/user/signup').send({
username: 'dummmyuser7',
email: 'random1@email.com',
password: 'pass'

View file

@ -0,0 +1,9 @@
import prisma from '../../db'
import app from '../../app'
import request from 'supertest'
describe('DELETE /post/delete', () => {
test('should ignore', () => {
expect(1 + 1).toBe(2)
})
})

View file

@ -0,0 +1,9 @@
import prisma from '../../db'
import app from '../../app'
import request from 'supertest'
describe('POST /post/info', () => {
test('should ignore', () => {
expect(1 + 1).toBe(2)
})
})

View file

@ -0,0 +1,9 @@
import prisma from '../../db'
import app from '../../app'
import request from 'supertest'
describe('PUT /post/update', () => {
test('should ignore', () => {
expect(1 + 1).toBe(2)
})
})

View file

@ -0,0 +1,9 @@
import prisma from '../../db'
import app from '../../app'
import request from 'supertest'
describe('DELETE /user/delete', () => {
test('should ignore', () => {
expect(1 + 1).toBe(2)
})
})

View file

@ -2,24 +2,7 @@ import prisma from '../../db'
import app from '../../app'
import request from 'supertest'
let token = ''
describe('POST /user/info', () => {
beforeAll(async () => {
await request(app).post('/user/create').send({
username: 'dummmyuser5',
email: 'random3@email.com',
password: 'pass'
})
const response = await request(app).post('/user/auth').send({
email: 'random3@email.com',
password: 'pass'
}).expect(200)
token = response.body.token
})
afterAll(async () => {
await prisma.user.deleteMany({
where: {
@ -30,16 +13,24 @@ describe('POST /user/info', () => {
})
it('should respond with 200 status code and return the user data', async () => {
const response = await request(app).get('/user/info').set('Authorization', `Bearer ${token}`).expect(200)
await prisma.user.create({
data: {
username: 'dummmyuser5',
email: 'random3@email.com',
password: 'pass'
}
})
const response = await request(app).get('/user/info?u=dummmyuser5').expect(200)
expect(response.body).toHaveProperty('id')
expect(response.body).toHaveProperty('displayName')
expect(response.body).toHaveProperty('username')
expect(response.body).toHaveProperty('createdAt')
expect(response.body).toHaveProperty('posts')
})
it('should respond with 400 status code if the user send no token', async () => {
const response = await request(app).get('/user/info').expect(401)
it('should respond with 400 status code if the user send no username', async () => {
const response = await request(app).get('/user/info?u=').expect(400)
expect(response.body).toHaveProperty('error')
})

View file

@ -8,9 +8,9 @@ const mockUser = {
password: 'totallysafepass'
}
describe('POST /user/create', () => {
describe('POST /user/signup', () => {
it('should respond with a 200 status code', async () => {
const response = await request(app).post('/user/create').send(mockUser).expect(200)
const response = await request(app).post('/user/signup').send(mockUser).expect(200)
expect(response.body).toHaveProperty('displayName')
expect(response.body).toHaveProperty('username')
@ -18,7 +18,7 @@ describe('POST /user/create', () => {
})
it('should respond with a 400 status code if sent any invalid data', async () => {
await request(app).post('/user/create').send({
await request(app).post('/user/signup').send({
username: 'username12@',
email: mockUser.email,
password: mockUser.password
@ -34,7 +34,7 @@ describe('POST /user/create', () => {
}
})
const response = await request(app).post('/user/create').send({
const response = await request(app).post('/user/signup').send({
username: 'dummmyuser2',
email: 'user1@email.com',
password: 'reallystrongpass'
@ -52,7 +52,7 @@ describe('POST /user/create', () => {
}
})
const response = await request(app).post('/user/create').send({
const response = await request(app).post('/user/signup').send({
username: 'dummmyuser4',
email: 'user13@email.com',
password: '12345'
@ -62,7 +62,7 @@ describe('POST /user/create', () => {
})
it('should respond with a 400 status code if receive an empty body', async () => {
const response = await request(app).post('/user/create').send({}).expect(400)
const response = await request(app).post('/user/signup').send({}).expect(400)
expect(response.body).toHaveProperty('error')
})

View file

@ -0,0 +1,9 @@
import prisma from '../../db'
import app from '../../app'
import request from 'supertest'
describe('PUT /user/update', () => {
test('should ignore', () => {
expect(1 + 1).toBe(2)
})
})

View file

@ -26,14 +26,7 @@
"rootDir": "./", /* Specify the root folder within your source files. */
// "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
"baseUrl": "./src", /* Specify the base directory to resolve non-relative module names. */
"paths": {
"@services/*": [
"services/*"
],
"@db/*": [
"../prisma/*"
]
}, /* Specify a set of entries that re-map imports to additional lookup locations. */
/* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
"typeRoots": [
"src/@types",