chore: initial commit
This commit is contained in:
21
src/services/users/current/__docs__/get.yaml
Normal file
21
src/services/users/current/__docs__/get.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
/users/current:
|
||||
get:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'users'
|
||||
summary: 'GET the current connected user'
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/BadRequestError'
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- '200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/User'
|
||||
- $ref: '#/definitions/UserSettingsObject'
|
||||
- $ref: '#/definitions/UserCurrentStrategy'
|
||||
- $ref: '#/definitions/UserStrategies'
|
45
src/services/users/current/__docs__/put.yaml
Normal file
45
src/services/users/current/__docs__/put.yaml
Normal file
@ -0,0 +1,45 @@
|
||||
/users/current:
|
||||
put:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'users'
|
||||
summary: 'Edit the current connected user info'
|
||||
requestBody:
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: 'object'
|
||||
properties:
|
||||
email:
|
||||
type: 'string'
|
||||
format: 'email'
|
||||
name:
|
||||
type: 'string'
|
||||
minLength: 3
|
||||
maxLength: 30
|
||||
example: 'user'
|
||||
biography:
|
||||
type: 'string'
|
||||
maxLength: 160
|
||||
example: 'biography'
|
||||
status:
|
||||
type: 'string'
|
||||
maxLength: 100
|
||||
example: '👀 Working on secrets projects...'
|
||||
logo:
|
||||
type: 'string'
|
||||
format: 'binary'
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/BadRequestError'
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- $ref: '#/definitions/PayloadTooLargeError'
|
||||
- '200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/User'
|
||||
- $ref: '#/definitions/UserCurrentStrategy'
|
46
src/services/users/current/__test__/get.test.ts
Normal file
46
src/services/users/current/__test__/get.test.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import { authenticateUserTest } from '../../../../__test__/utils/authenticateUser'
|
||||
import application from '../../../../application'
|
||||
|
||||
describe('GET /users/current', () => {
|
||||
it('succeeds with valid Bearer accessToken', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.get('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send()
|
||||
.expect(200)
|
||||
expect(response.body.user).not.toBeNull()
|
||||
expect(response.body.settings).not.toBeNull()
|
||||
expect(response.body.currentStrategy).toEqual('local')
|
||||
expect(Array.isArray(response.body.strategies)).toBeTruthy()
|
||||
expect(response.body.strategies.includes('local')).toBeTruthy()
|
||||
})
|
||||
|
||||
it('fails with unconfirmed account', async () => {
|
||||
const userToken = await authenticateUserTest({ shouldBeConfirmed: false })
|
||||
const response = await request(application)
|
||||
.get('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send()
|
||||
.expect(401)
|
||||
expect(response.body.errors.length).toEqual(1)
|
||||
})
|
||||
|
||||
it('fails ForbiddenError with invalid Bearer accessToken', async () => {
|
||||
await request(application)
|
||||
.get('/users/current')
|
||||
.set('Authorization', 'Bearer invalidtoken')
|
||||
.send()
|
||||
.expect(403)
|
||||
})
|
||||
|
||||
it('fails NotAuthorizedError with invalid accessToken', async () => {
|
||||
await request(application)
|
||||
.get('/users/current')
|
||||
.set('Authorization', 'invalidtoken')
|
||||
.send()
|
||||
.expect(401)
|
||||
})
|
||||
})
|
228
src/services/users/current/__test__/put.test.ts
Normal file
228
src/services/users/current/__test__/put.test.ts
Normal file
@ -0,0 +1,228 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import { authenticateUserTest } from '../../../../__test__/utils/authenticateUser'
|
||||
import { formatErrors } from '../../../../__test__/utils/formatErrors'
|
||||
import application from '../../../../application'
|
||||
import User from '../../../../models/User'
|
||||
import { commonErrorsMessages } from '../../../../tools/configurations/constants'
|
||||
import { randomString } from '../../../../tools/utils/random'
|
||||
import { errorsMessages } from '../index'
|
||||
|
||||
describe('PUT /users/current', () => {
|
||||
it('succeeds with valid accessToken, valid email and valid name', async () => {
|
||||
const name = 'test2'
|
||||
const email = 'test2@test2.com'
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ name, email })
|
||||
.expect(200)
|
||||
expect(response.body.user).not.toBeNull()
|
||||
expect(response.body.user.name).toBe(name)
|
||||
expect(response.body.user.email).toBe(email)
|
||||
expect(response.body.currentStrategy).not.toBeNull()
|
||||
})
|
||||
|
||||
it('succeeds and only change the email', async () => {
|
||||
const name = 'John'
|
||||
const email = 'contact@test.com'
|
||||
const userToken = await authenticateUserTest({
|
||||
name,
|
||||
email
|
||||
})
|
||||
let user = (await User.findAll())[0]
|
||||
expect(user.email).toEqual(email)
|
||||
expect(user.name).toEqual(name)
|
||||
|
||||
const email2 = 'test2@test2.com'
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ email: email2 })
|
||||
.expect(200)
|
||||
expect(response.body.user).not.toBeNull()
|
||||
expect(response.body.user.email).toEqual(email2)
|
||||
expect(response.body.user.name).toEqual(name)
|
||||
|
||||
user = (await User.findAll())[0]
|
||||
expect(user.email).toEqual(email2)
|
||||
expect(user.name).toEqual(name)
|
||||
expect(user.isConfirmed).toBe(false)
|
||||
})
|
||||
|
||||
it('succeeds and only change the name', async () => {
|
||||
const name = 'John'
|
||||
const email = 'contact@test.com'
|
||||
const userToken = await authenticateUserTest({
|
||||
name,
|
||||
email
|
||||
})
|
||||
let user = (await User.findAll())[0]
|
||||
expect(user.email).toEqual(email)
|
||||
expect(user.name).toEqual(name)
|
||||
|
||||
const name2 = 'test2'
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ name: name2 })
|
||||
.expect(200)
|
||||
expect(response.body.user).not.toBeNull()
|
||||
|
||||
user = (await User.findAll())[0]
|
||||
expect(user.email).toEqual(email)
|
||||
expect(user.name).toEqual(name2)
|
||||
})
|
||||
|
||||
it('succeeds and only change the status', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const status = '👀 Working on secret projects...'
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ status })
|
||||
.expect(200)
|
||||
expect(response.body.user).not.toBeNull()
|
||||
expect(response.body.user.status).toBe(status)
|
||||
})
|
||||
|
||||
it('succeeds and only change the biography', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const biography = 'My awesome biography'
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ biography })
|
||||
.expect(200)
|
||||
expect(response.body.user).not.toBeNull()
|
||||
expect(response.body.user.biography).toBe(biography)
|
||||
})
|
||||
|
||||
it('fails with unconfirmed account', async () => {
|
||||
const userToken = await authenticateUserTest({
|
||||
name: 'John',
|
||||
email: 'contact@john.com',
|
||||
shouldBeConfirmed: false
|
||||
})
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send()
|
||||
.expect(401)
|
||||
expect(response.body.errors.length).toEqual(1)
|
||||
})
|
||||
|
||||
it('fails with invalid status', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const status = randomString(110)
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ status })
|
||||
.expect(400)
|
||||
expect(response.body.errors.length).toEqual(1)
|
||||
expect(response.body.errors[0].message).toEqual(
|
||||
commonErrorsMessages.charactersLength('status', { max: 100 })
|
||||
)
|
||||
})
|
||||
|
||||
it('fails with invalid biography', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const biography = randomString(170)
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ biography })
|
||||
.expect(400)
|
||||
expect(response.body.errors.length).toEqual(1)
|
||||
expect(response.body.errors[0].message).toEqual(
|
||||
commonErrorsMessages.charactersLength('biography', { max: 160 })
|
||||
)
|
||||
})
|
||||
|
||||
it('fails with invalid name and invalid email', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({
|
||||
name: 'jo',
|
||||
email: 'test2@test2'
|
||||
})
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(2)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([
|
||||
errorsMessages.email.mustBeValid,
|
||||
commonErrorsMessages.charactersLength('name', { max: 30, min: 3 })
|
||||
])
|
||||
)
|
||||
})
|
||||
|
||||
it('fails with name and email already used', async () => {
|
||||
const firstUserName = 'Test'
|
||||
const firstUserEmail = 'test@test.com'
|
||||
await authenticateUserTest({
|
||||
name: firstUserName,
|
||||
email: firstUserEmail,
|
||||
shouldBeConfirmed: true
|
||||
})
|
||||
const secondUserToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set(
|
||||
'Authorization',
|
||||
`${secondUserToken.type} ${secondUserToken.accessToken}`
|
||||
)
|
||||
.send({
|
||||
name: firstUserName,
|
||||
email: firstUserEmail
|
||||
})
|
||||
.expect(400)
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors.length).toEqual(2)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining(['Name already used', 'Email already used'])
|
||||
)
|
||||
})
|
||||
|
||||
it('fails with name identical to the current user name', async () => {
|
||||
const name = 'Test'
|
||||
const email = 'test@test.com'
|
||||
const userToken = await authenticateUserTest({
|
||||
name,
|
||||
email
|
||||
})
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ name })
|
||||
.expect(400)
|
||||
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([errorsMessages.name.alreadyConnected])
|
||||
)
|
||||
})
|
||||
|
||||
it('fails with email identical to the current user email', async () => {
|
||||
const name = 'Test'
|
||||
const email = 'test@test.com'
|
||||
const userToken = await authenticateUserTest({
|
||||
name,
|
||||
email
|
||||
})
|
||||
const response = await request(application)
|
||||
.put('/users/current')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ email })
|
||||
.expect(400)
|
||||
|
||||
const errors = formatErrors(response.body.errors)
|
||||
expect(errors).toEqual(
|
||||
expect.arrayContaining([errorsMessages.email.alreadyConnected])
|
||||
)
|
||||
})
|
||||
})
|
36
src/services/users/current/get.ts
Normal file
36
src/services/users/current/get.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
|
||||
import { authenticateUser } from '../../../tools/middlewares/authenticateUser'
|
||||
import OAuth, { AuthenticationStrategy } from '../../../models/OAuth'
|
||||
import UserSetting from '../../../models/UserSetting'
|
||||
import { ForbiddenError } from '../../../tools/errors/ForbiddenError'
|
||||
|
||||
export const getCurrentRouter = Router()
|
||||
|
||||
getCurrentRouter.get(
|
||||
'/users/current',
|
||||
authenticateUser,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const settings = await UserSetting.findOne({
|
||||
where: { userId: req.user.current.id }
|
||||
})
|
||||
const OAuths = await OAuth.findAll({
|
||||
where: { userId: req.user.current.id }
|
||||
})
|
||||
const strategies: AuthenticationStrategy[] = OAuths.map((oauth) => {
|
||||
return oauth.provider
|
||||
})
|
||||
if (req.user.current.password != null) {
|
||||
strategies.push('local')
|
||||
}
|
||||
return res.status(200).json({
|
||||
user: req.user.current,
|
||||
settings,
|
||||
currentStrategy: req.user.currentStrategy,
|
||||
strategies
|
||||
})
|
||||
}
|
||||
)
|
21
src/services/users/current/index.ts
Normal file
21
src/services/users/current/index.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { Router } from 'express'
|
||||
|
||||
import { getCurrentRouter } from './get'
|
||||
import { putCurrentRouter } from './put'
|
||||
import { currentSettingsRouter } from './settings'
|
||||
|
||||
export const currentRouter = Router()
|
||||
|
||||
export const errorsMessages = {
|
||||
email: {
|
||||
mustBeValid: 'Email must be valid',
|
||||
alreadyConnected: 'You are already connected with this email address'
|
||||
},
|
||||
name: {
|
||||
alreadyConnected: 'You are already connected with this name'
|
||||
}
|
||||
}
|
||||
|
||||
currentRouter.use('/', getCurrentRouter)
|
||||
currentRouter.use('/', putCurrentRouter)
|
||||
currentRouter.use('/', currentSettingsRouter)
|
141
src/services/users/current/put.ts
Normal file
141
src/services/users/current/put.ts
Normal file
@ -0,0 +1,141 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
import fileUpload from 'express-fileupload'
|
||||
import { body, query } from 'express-validator'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { authenticateUser } from '../../../tools/middlewares/authenticateUser'
|
||||
import { validateRequest } from '../../../tools/middlewares/validateRequest'
|
||||
import User from '../../../models/User'
|
||||
import {
|
||||
commonErrorsMessages,
|
||||
imageFileUploadOptions,
|
||||
usersLogoPath
|
||||
} from '../../../tools/configurations/constants'
|
||||
import { alreadyUsedValidation } from '../../../tools/validations/alreadyUsedValidation'
|
||||
import { ForbiddenError } from '../../../tools/errors/ForbiddenError'
|
||||
import { uploadImage } from '../../../tools/utils/uploadImage'
|
||||
import { deleteEveryRefreshTokens } from '../__utils__/deleteEveryRefreshTokens'
|
||||
import UserSetting from '../../../models/UserSetting'
|
||||
import { sendEmail } from '../../../tools/email/sendEmail'
|
||||
|
||||
export const errorsMessages = {
|
||||
email: {
|
||||
mustBeValid: 'Email must be valid',
|
||||
alreadyConnected: 'You are already connected with this email address'
|
||||
},
|
||||
name: {
|
||||
alreadyConnected: 'You are already connected with this name'
|
||||
}
|
||||
}
|
||||
|
||||
export const putCurrentRouter = Router()
|
||||
|
||||
putCurrentRouter.put(
|
||||
'/users/current',
|
||||
authenticateUser,
|
||||
fileUpload(imageFileUploadOptions),
|
||||
[
|
||||
body('email')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.isEmail()
|
||||
.withMessage(errorsMessages.email.mustBeValid)
|
||||
.custom(async (email: string, meta) => {
|
||||
if (email === meta.req.user?.current.email) {
|
||||
return await Promise.reject(
|
||||
new Error(errorsMessages.email.alreadyConnected)
|
||||
)
|
||||
}
|
||||
return await alreadyUsedValidation(User, 'email', email)
|
||||
}),
|
||||
body('name')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 30, min: 3 })
|
||||
.withMessage(
|
||||
commonErrorsMessages.charactersLength('name', { max: 30, min: 3 })
|
||||
)
|
||||
.custom(async (name: string, meta) => {
|
||||
if (name === meta.req.user?.current.name) {
|
||||
return await Promise.reject(
|
||||
new Error(errorsMessages.name.alreadyConnected)
|
||||
)
|
||||
}
|
||||
return await alreadyUsedValidation(User, 'name', name)
|
||||
}),
|
||||
body('biography')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 160 })
|
||||
.withMessage(
|
||||
commonErrorsMessages.charactersLength('biography', { max: 160 })
|
||||
),
|
||||
body('status')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.escape()
|
||||
.isLength({ max: 100 })
|
||||
.withMessage(
|
||||
commonErrorsMessages.charactersLength('status', { max: 100 })
|
||||
),
|
||||
query('redirectURI').optional({ nullable: true }).trim()
|
||||
],
|
||||
validateRequest,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const { name, email, status, biography } = req.body as {
|
||||
name?: string
|
||||
email?: string
|
||||
status?: string
|
||||
biography?: string
|
||||
}
|
||||
const logo = req.files?.logo
|
||||
const { redirectURI } = req.query as { redirectURI?: string }
|
||||
const user = req.user.current
|
||||
|
||||
user.name = name ?? user.name
|
||||
user.status = status ?? user.status
|
||||
user.biography = biography ?? user.biography
|
||||
|
||||
const resultUpload = await uploadImage({
|
||||
image: logo,
|
||||
propertyName: 'logo',
|
||||
oldImage: user.logo,
|
||||
imagesPath: usersLogoPath.filePath
|
||||
})
|
||||
if (resultUpload != null) {
|
||||
user.logo = `${usersLogoPath.name}/${resultUpload}`
|
||||
}
|
||||
|
||||
if (email != null) {
|
||||
user.email = email
|
||||
if (req.user.currentStrategy === 'local') {
|
||||
await deleteEveryRefreshTokens(user.id)
|
||||
}
|
||||
const tempToken = uuidv4()
|
||||
user.tempToken = tempToken
|
||||
user.isConfirmed = false
|
||||
const userSettings = await UserSetting.findOne({
|
||||
where: { userId: user.id }
|
||||
})
|
||||
const redirectQuery =
|
||||
redirectURI != null ? `&redirectURI=${redirectURI}` : ''
|
||||
await sendEmail({
|
||||
type: 'confirm-email',
|
||||
email,
|
||||
url: `${process.env.API_BASE_URL}/users/confirmEmail?tempToken=${tempToken}${redirectQuery}`,
|
||||
language: userSettings?.language,
|
||||
theme: userSettings?.theme
|
||||
})
|
||||
}
|
||||
|
||||
const userSaved = await user.save()
|
||||
return res
|
||||
.status(200)
|
||||
.json({ user: userSaved, strategy: req.user.currentStrategy })
|
||||
}
|
||||
)
|
24
src/services/users/current/settings/__docs__/put.yaml
Normal file
24
src/services/users/current/settings/__docs__/put.yaml
Normal file
@ -0,0 +1,24 @@
|
||||
/users/current/settings:
|
||||
put:
|
||||
security:
|
||||
- bearerAuth: []
|
||||
tags:
|
||||
- 'users'
|
||||
summary: 'Edit the current connected user settings'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/UserSettings'
|
||||
responses:
|
||||
allOf:
|
||||
- $ref: '#/definitions/BadRequestError'
|
||||
- $ref: '#/definitions/UnauthorizedError'
|
||||
- $ref: '#/definitions/ForbiddenError'
|
||||
- '200':
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: '#/definitions/UserSettingsObject'
|
66
src/services/users/current/settings/__test__/put.test.ts
Normal file
66
src/services/users/current/settings/__test__/put.test.ts
Normal file
@ -0,0 +1,66 @@
|
||||
import request from 'supertest'
|
||||
|
||||
import { authenticateUserTest } from '../../../../../__test__/utils/authenticateUser'
|
||||
import application from '../../../../../application'
|
||||
|
||||
describe('PUT /users/current/settings', () => {
|
||||
it('should succeeds and edit theme, language and isPublicEmail', async () => {
|
||||
const isPublicEmail = true
|
||||
const theme = 'light'
|
||||
const language = 'fr'
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.put('/users/current/settings')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ isPublicEmail, theme, language })
|
||||
.expect(200)
|
||||
expect(response.body.settings).not.toBeNull()
|
||||
expect(response.body.settings.theme).toEqual(theme)
|
||||
expect(response.body.settings.language).toEqual(language)
|
||||
expect(response.body.settings.isPublicEmail).toEqual(isPublicEmail)
|
||||
})
|
||||
|
||||
it('fails with unconfirmed account', async () => {
|
||||
const userToken = await authenticateUserTest({
|
||||
name: 'John',
|
||||
email: 'contact@john.com',
|
||||
shouldBeConfirmed: false
|
||||
})
|
||||
const response = await request(application)
|
||||
.put('/users/current/settings')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send()
|
||||
.expect(401)
|
||||
expect(response.body.errors.length).toEqual(1)
|
||||
})
|
||||
|
||||
it('fails with invalid theme', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.put('/users/current/settings')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ theme: 'random theme value' })
|
||||
.expect(400)
|
||||
expect(response.body.errors.length).toEqual(1)
|
||||
})
|
||||
|
||||
it('fails with invalid language', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.put('/users/current/settings')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ language: 'random language value' })
|
||||
.expect(400)
|
||||
expect(response.body.errors.length).toEqual(1)
|
||||
})
|
||||
|
||||
it('fails with invalid isPublicEmail', async () => {
|
||||
const userToken = await authenticateUserTest()
|
||||
const response = await request(application)
|
||||
.put('/users/current/settings')
|
||||
.set('Authorization', `${userToken.type} ${userToken.accessToken}`)
|
||||
.send({ isPublicEmail: 'not a boolean value' })
|
||||
.expect(400)
|
||||
expect(response.body.errors.length).toEqual(1)
|
||||
})
|
||||
})
|
7
src/services/users/current/settings/index.ts
Normal file
7
src/services/users/current/settings/index.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { Router } from 'express'
|
||||
|
||||
import { putCurrentSettingsRouter } from './put'
|
||||
|
||||
export const currentSettingsRouter = Router()
|
||||
|
||||
currentSettingsRouter.use('/', putCurrentSettingsRouter)
|
63
src/services/users/current/settings/put.ts
Normal file
63
src/services/users/current/settings/put.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { Request, Response, Router } from 'express'
|
||||
import { body } from 'express-validator'
|
||||
|
||||
import { authenticateUser } from '../../../../tools/middlewares/authenticateUser'
|
||||
import { validateRequest } from '../../../../tools/middlewares/validateRequest'
|
||||
import UserSetting, {
|
||||
themes,
|
||||
Theme,
|
||||
languages,
|
||||
Language
|
||||
} from '../../../../models/UserSetting'
|
||||
import { ForbiddenError } from '../../../../tools/errors/ForbiddenError'
|
||||
import { NotFoundError } from '../../../../tools/errors/NotFoundError'
|
||||
import { onlyPossibleValuesValidation } from '../../../../tools/validations/onlyPossibleValuesValidation'
|
||||
|
||||
export const putCurrentSettingsRouter = Router()
|
||||
|
||||
putCurrentSettingsRouter.put(
|
||||
'/users/current/settings',
|
||||
authenticateUser,
|
||||
[
|
||||
body('isPublicEmail').optional({ nullable: true }).isBoolean(),
|
||||
body('theme')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.isString()
|
||||
.custom(async (theme: Theme) => {
|
||||
return await onlyPossibleValuesValidation([...themes], 'theme', theme)
|
||||
}),
|
||||
body('language')
|
||||
.optional({ nullable: true })
|
||||
.trim()
|
||||
.isString()
|
||||
.custom(async (language: Language) => {
|
||||
return await onlyPossibleValuesValidation(
|
||||
languages,
|
||||
'language',
|
||||
language
|
||||
)
|
||||
})
|
||||
],
|
||||
validateRequest,
|
||||
async (req: Request, res: Response) => {
|
||||
if (req.user == null) {
|
||||
throw new ForbiddenError()
|
||||
}
|
||||
const { isPublicEmail, theme, language } = req.body as {
|
||||
isPublicEmail?: boolean
|
||||
theme?: Theme
|
||||
language?: Language
|
||||
}
|
||||
const user = req.user.current
|
||||
const settings = await UserSetting.findOne({ where: { id: user.id } })
|
||||
if (settings == null) {
|
||||
throw new NotFoundError()
|
||||
}
|
||||
settings.isPublicEmail = isPublicEmail ?? settings.isPublicEmail
|
||||
settings.theme = theme ?? settings.theme
|
||||
settings.language = language ?? settings.language
|
||||
await settings.save()
|
||||
return res.status(200).json({ settings })
|
||||
}
|
||||
)
|
Reference in New Issue
Block a user