From babd2ca7431d6f01ab625115249f3928e28b4b0c Mon Sep 17 00:00:00 2001 From: Lucas Orts Date: Wed, 31 Jul 2024 17:02:34 +0200 Subject: [PATCH] api refactoring index into handlers files and middleware files #60 --- .../api/handlers/authenticateUserHandler.js | 32 + .../ponies/api/handlers/createPostHandler.js | 21 + .../ponies/api/handlers/deletePostHandler.js | 21 + .../api/handlers/getAllFavPostsHandler.js | 19 + .../getAllFollowingUserPostsHandler.js | 19 + .../ponies/api/handlers/getAllPostsHandler.js | 19 + .../ponies/api/handlers/getUserNameHandler.js | 21 + staff/lucas-orts/ponies/api/handlers/index.js | 27 + .../api/handlers/registerUserHandler.js | 19 + .../api/handlers/toggleFavPostHandler.js | 21 + .../api/handlers/toggleFollowUserHandler.js | 21 + .../api/handlers/toggleLikePostHandler.js | 21 + .../api/handlers/updatePostCaptionHandler.js | 23 + staff/lucas-orts/ponies/api/index.js | 301 +---- staff/lucas-orts/ponies/api/index1.js | 532 ++++++++ .../lucas-orts/ponies/api/middlewares/cors.js | 7 + .../ponies/api/middlewares/errorHandler.js | 22 + .../ponies/api/middlewares/index.js | 11 + .../ponies/api/middlewares/jsonBodyParser.js | 11 + .../ponies/api/middlewares/jwtVerifier.js | 25 + staff/lucas-orts/ponies/api/package-lock.json | 280 +++- staff/lucas-orts/ponies/api/package.json | 10 +- .../ponies/api/test/authenticate-user.sh | 2 +- .../lucas-orts/ponies/api/test/create-post.sh | 2 +- .../ponies/api/test/register-user.sh | 2 +- .../ponies/api/test/toggle-fav-post.sh | 2 +- .../lucas-orts/ponies/app/logic/createPost.js | 14 +- .../lucas-orts/ponies/app/logic/deletePost.js | 14 +- .../ponies/app/logic/getAllFavPosts.js | 13 +- .../app/logic/getAllFollowingUserPosts.js | 14 +- .../ponies/app/logic/getAllPosts.js | 14 +- staff/lucas-orts/ponies/app/logic/getUser.js | 14 +- .../ponies/app/logic/getUserName.js | 16 +- .../ponies/app/logic/getUserUsername.js | 8 +- .../ponies/app/logic/isUserLoggedIn.js | 12 +- .../lucas-orts/ponies/app/logic/loginUser.js | 15 +- .../lucas-orts/ponies/app/logic/logoutUser.js | 4 +- .../ponies/app/logic/registerUser.js | 9 +- .../ponies/app/logic/toggleFavPost.js | 14 +- .../ponies/app/logic/toggleFollowUser.js | 14 +- .../ponies/app/logic/toggleLikePost.js | 14 +- .../ponies/app/logic/updatePostCaption.js | 14 +- staff/lucas-orts/ponies/app/package-lock.json | 13 +- staff/lucas-orts/ponies/app/package.json | 3 +- .../app/util/extractPayloadFromToken.js | 9 + staff/lucas-orts/ponies/app/views/Login.jsx | 9 + .../lucas-orts/ponies/app/views/Register.jsx | 14 +- staff/lucas-orts/ponies/com/errors.js | 67 + staff/lucas-orts/ponies/com/index.js | 7 + staff/lucas-orts/ponies/com/package.json | 13 + staff/lucas-orts/ponies/com/validate.js | 72 ++ .../lucas-orts/ponies/cor/data/deletePost.js | 44 - .../ponies/cor/data/deletePost.test.js | 11 - staff/lucas-orts/ponies/cor/data/findPost.js | 29 - .../ponies/cor/data/findPost.test.js | 11 - staff/lucas-orts/ponies/cor/data/findPosts.js | 29 - .../ponies/cor/data/findPosts.test.js | 11 - staff/lucas-orts/ponies/cor/data/findUser.js | 29 - .../ponies/cor/data/findUser.test.js | 12 - staff/lucas-orts/ponies/cor/data/index.js | 20 +- .../lucas-orts/ponies/cor/data/insertPost.js | 39 - .../ponies/cor/data/insertPost.test.js | 13 - .../lucas-orts/ponies/cor/data/insertUser.js | 39 - .../ponies/cor/data/insertUser.test.js | 19 - staff/lucas-orts/ponies/cor/data/models.js | 67 + staff/lucas-orts/ponies/cor/data/posts.json | 1 - .../lucas-orts/ponies/cor/data/updatePost.js | 48 - .../ponies/cor/data/updatePost.test.js | 21 - .../lucas-orts/ponies/cor/data/updateUser.js | 48 - .../ponies/cor/data/updateUser.test.js | 19 - staff/lucas-orts/ponies/cor/data/users.json | 1 - staff/lucas-orts/ponies/cor/index.js | 9 + .../ponies/cor/logic/authenticateUser.js | 49 +- .../ponies/cor/logic/authenticateUser.spec.js | 148 +++ .../ponies/cor/logic/authenticateUser.test.js | 24 +- .../lucas-orts/ponies/cor/logic/createPost.js | 52 +- .../ponies/cor/logic/createPost.spec.js | 148 +++ .../ponies/cor/logic/createPost.test.js | 25 +- .../lucas-orts/ponies/cor/logic/deletePost.js | 61 +- .../ponies/cor/logic/deletePost.spec.js | 189 +++ .../ponies/cor/logic/deletePost.test.js | 25 +- .../ponies/cor/logic/getAllFavPosts.js | 87 +- .../ponies/cor/logic/getAllFavPosts.spec.js | 121 ++ .../ponies/cor/logic/getAllFavPosts.test.js | 24 +- .../cor/logic/getAllFollowingUserPosts.js | 86 +- .../logic/getAllFollowingUserPosts.spec.js | 121 ++ .../logic/getAllFollowingUserPosts.test.js | 25 +- .../ponies/cor/logic/getAllPosts.js | 95 +- .../ponies/cor/logic/getAllPosts.spec.js | 128 ++ .../ponies/cor/logic/getAllPosts.test.js | 25 +- staff/lucas-orts/ponies/cor/logic/getUser.js | 53 +- .../ponies/cor/logic/getUser.spec.js | 128 ++ .../ponies/cor/logic/getUser.test.js | 26 +- .../ponies/cor/logic/getUserName.js | 49 +- .../ponies/cor/logic/getUserName.spec.js | 142 ++ .../ponies/cor/logic/getUserName.test.js | 26 +- .../ponies/cor/logic/registerUser.js | 91 +- .../ponies/cor/logic/registerUser.spec.js | 254 ++++ .../ponies/cor/logic/registerUser.test.js | 25 +- .../ponies/cor/logic/toggleFavPost.js | 69 +- .../ponies/cor/logic/toggleFavPost.spec.js | 190 +++ .../ponies/cor/logic/toggleFavPost.test.js | 26 +- .../ponies/cor/logic/toggleFollowUser.js | 69 +- .../ponies/cor/logic/toggleFollowUser.spec.js | 182 +++ .../ponies/cor/logic/toggleFollowUser.test.js | 25 +- .../ponies/cor/logic/toggleLikePost.js | 78 +- .../ponies/cor/logic/toggleLikePost.spec.js | 190 +++ .../ponies/cor/logic/toggleLikePost.test.js | 23 +- .../ponies/cor/logic/updatePostCaption.js | 64 +- .../cor/logic/updatePostCaption.spec.js | 198 +++ .../cor/logic/updatePostCaption.test.js | 24 +- staff/lucas-orts/ponies/cor/package-lock.json | 1151 +++++++++++++++++ staff/lucas-orts/ponies/cor/package.json | 15 +- staff/lucas-orts/ponies/cor/validate.js | 74 -- 114 files changed, 5527 insertions(+), 1499 deletions(-) create mode 100644 staff/lucas-orts/ponies/api/handlers/authenticateUserHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/createPostHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/deletePostHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/getAllFavPostsHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/getAllFollowingUserPostsHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/getAllPostsHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/getUserNameHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/index.js create mode 100644 staff/lucas-orts/ponies/api/handlers/registerUserHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/toggleFavPostHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/toggleFollowUserHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/toggleLikePostHandler.js create mode 100644 staff/lucas-orts/ponies/api/handlers/updatePostCaptionHandler.js create mode 100644 staff/lucas-orts/ponies/api/index1.js create mode 100644 staff/lucas-orts/ponies/api/middlewares/cors.js create mode 100644 staff/lucas-orts/ponies/api/middlewares/errorHandler.js create mode 100644 staff/lucas-orts/ponies/api/middlewares/index.js create mode 100644 staff/lucas-orts/ponies/api/middlewares/jsonBodyParser.js create mode 100644 staff/lucas-orts/ponies/api/middlewares/jwtVerifier.js create mode 100644 staff/lucas-orts/ponies/app/util/extractPayloadFromToken.js create mode 100644 staff/lucas-orts/ponies/com/errors.js create mode 100644 staff/lucas-orts/ponies/com/index.js create mode 100644 staff/lucas-orts/ponies/com/package.json create mode 100644 staff/lucas-orts/ponies/com/validate.js delete mode 100644 staff/lucas-orts/ponies/cor/data/deletePost.js delete mode 100644 staff/lucas-orts/ponies/cor/data/deletePost.test.js delete mode 100644 staff/lucas-orts/ponies/cor/data/findPost.js delete mode 100644 staff/lucas-orts/ponies/cor/data/findPost.test.js delete mode 100644 staff/lucas-orts/ponies/cor/data/findPosts.js delete mode 100644 staff/lucas-orts/ponies/cor/data/findPosts.test.js delete mode 100644 staff/lucas-orts/ponies/cor/data/findUser.js delete mode 100644 staff/lucas-orts/ponies/cor/data/findUser.test.js delete mode 100644 staff/lucas-orts/ponies/cor/data/insertPost.js delete mode 100644 staff/lucas-orts/ponies/cor/data/insertPost.test.js delete mode 100644 staff/lucas-orts/ponies/cor/data/insertUser.js delete mode 100644 staff/lucas-orts/ponies/cor/data/insertUser.test.js create mode 100644 staff/lucas-orts/ponies/cor/data/models.js delete mode 100644 staff/lucas-orts/ponies/cor/data/posts.json delete mode 100644 staff/lucas-orts/ponies/cor/data/updatePost.js delete mode 100644 staff/lucas-orts/ponies/cor/data/updatePost.test.js delete mode 100644 staff/lucas-orts/ponies/cor/data/updateUser.js delete mode 100644 staff/lucas-orts/ponies/cor/data/updateUser.test.js delete mode 100644 staff/lucas-orts/ponies/cor/data/users.json create mode 100644 staff/lucas-orts/ponies/cor/logic/authenticateUser.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/createPost.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/deletePost.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/getAllFavPosts.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/getAllPosts.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/getUser.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/getUserName.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/registerUser.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/toggleFavPost.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/toggleFollowUser.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/toggleLikePost.spec.js create mode 100644 staff/lucas-orts/ponies/cor/logic/updatePostCaption.spec.js create mode 100644 staff/lucas-orts/ponies/cor/package-lock.json delete mode 100644 staff/lucas-orts/ponies/cor/validate.js diff --git a/staff/lucas-orts/ponies/api/handlers/authenticateUserHandler.js b/staff/lucas-orts/ponies/api/handlers/authenticateUserHandler.js new file mode 100644 index 000000000..bf77708dd --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/authenticateUserHandler.js @@ -0,0 +1,32 @@ +import jwt from 'jsonwebtoken' + +import { logic } from '../../cor/index.js' +import { errors } from '../../com/index.js' + +const { SessionError } = errors + +export default (req, res, next) => { + const { username, password } = req.body + + try { + logic.authenticateUser(username, password, error => { + if (error) { + next(error) + + return + } + + jwt.sign({ sub: username }, process.env.JWT_SECRET, (error, token) => { + if (error) { + next(new SessionError(error.message)) + + return + } + + res.json(token) + }) + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/createPostHandler.js b/staff/lucas-orts/ponies/api/handlers/createPostHandler.js new file mode 100644 index 000000000..8dd228c01 --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/createPostHandler.js @@ -0,0 +1,21 @@ +import { logic } from '../../cor/index.js' + +export default (req, res, next) => { + const { username } = req + + const { image, caption } = req.body + + try { + logic.createPost(username, image, caption, error => { + if (error) { + next(error) + + return + } + + res.status(201).send() + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/deletePostHandler.js b/staff/lucas-orts/ponies/api/handlers/deletePostHandler.js new file mode 100644 index 000000000..351e7ace3 --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/deletePostHandler.js @@ -0,0 +1,21 @@ +import { logic } from '../../cor/index.js' + +export default (req, res, next) => { + const { username } = req + + const { postId } = req.params + + try { + logic.deletePost(username, postId, error => { + if (error) { + next(error) + + return + } + + res.status(204).send() + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/getAllFavPostsHandler.js b/staff/lucas-orts/ponies/api/handlers/getAllFavPostsHandler.js new file mode 100644 index 000000000..6e83a910d --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/getAllFavPostsHandler.js @@ -0,0 +1,19 @@ +import { logic } from '../../cor/index.js' + +export default (req, res, next) => { + const { username } = req + + try { + logic.getAllFavPosts(username, (error, posts) => { + if (error) { + next(error) + + return + } + + res.json(posts) + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/getAllFollowingUserPostsHandler.js b/staff/lucas-orts/ponies/api/handlers/getAllFollowingUserPostsHandler.js new file mode 100644 index 000000000..8eb35b425 --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/getAllFollowingUserPostsHandler.js @@ -0,0 +1,19 @@ +import { logic } from '../../cor/index.js' + +export default (req, res, next) => { + const { username } = req + + try { + logic.getAllFollowingUserPosts(username, (error, posts) => { + if (error) { + next(error) + + return + } + + res.json(posts) + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/getAllPostsHandler.js b/staff/lucas-orts/ponies/api/handlers/getAllPostsHandler.js new file mode 100644 index 000000000..4b128af79 --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/getAllPostsHandler.js @@ -0,0 +1,19 @@ +import { logic } from '../../cor/index.js' + +export default (req, res, next) => { + const { username } = req + + try { + logic.getAllPosts(username, (error, posts) => { + if (error) { + next(error) + + return + } + + res.json(posts) + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/getUserNameHandler.js b/staff/lucas-orts/ponies/api/handlers/getUserNameHandler.js new file mode 100644 index 000000000..d57b8edb5 --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/getUserNameHandler.js @@ -0,0 +1,21 @@ +import { logic } from '../../cor/index.js' + +export default (req, res, next) => { + const { username } = req + + const { targetUsername } = req.params + + try { + logic.getUserName(username, targetUsername, (error, name) => { + if (error) { + next(error) + + return + } + + res.json(name) + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/index.js b/staff/lucas-orts/ponies/api/handlers/index.js new file mode 100644 index 000000000..982371aa0 --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/index.js @@ -0,0 +1,27 @@ +import registerUserHandler from './registerUserHandler.js' +import authenticateUserHandler from './authenticateUserHandler.js' +import getUserNameHandler from './getUserNameHandler.js' +import getAllPostsHandler from './getAllPostsHandler.js' +import getAllFollowingUserPostsHandler from './getAllFollowingUserPostsHandler.js' +import getAllFavPostsHandler from './getAllFavPostsHandler.js' +import createPostHandler from './createPostHandler.js' +import deletePostHandler from './deletePostHandler.js' +import toggleLikePostHandler from './toggleLikePostHandler.js' +import toggleFavPostHandler from './toggleFavPostHandler.js' +import toggleFollowUserHandler from './toggleFollowUserHandler.js' +import updatePostCaptionHandler from './updatePostCaptionHandler.js' + +export { + registerUserHandler, + authenticateUserHandler, + getUserNameHandler, + getAllPostsHandler, + getAllFollowingUserPostsHandler, + getAllFavPostsHandler, + createPostHandler, + deletePostHandler, + toggleLikePostHandler, + toggleFavPostHandler, + toggleFollowUserHandler, + updatePostCaptionHandler +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/registerUserHandler.js b/staff/lucas-orts/ponies/api/handlers/registerUserHandler.js new file mode 100644 index 000000000..84caf4ffc --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/registerUserHandler.js @@ -0,0 +1,19 @@ +import { logic } from 'cor' + +export default (req, res, next) => { + const { name, surname, email, username, password, passwordRepeat } = req.body + + try { + logic.registerUser(name, surname, email, username, password, passwordRepeat, error => { + if (error) { + next(error) + + return + } + + res.status(201).send() + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/toggleFavPostHandler.js b/staff/lucas-orts/ponies/api/handlers/toggleFavPostHandler.js new file mode 100644 index 000000000..07ca32d8c --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/toggleFavPostHandler.js @@ -0,0 +1,21 @@ +import { logic } from '../../cor/index.js' + +export default (req, res, next) => { + const { username } = req + + const { postId } = req.params + + try { + logic.toggleFavPost(username, postId, error => { + if (error) { + next(error) + + return + } + + res.status(204).send() + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/toggleFollowUserHandler.js b/staff/lucas-orts/ponies/api/handlers/toggleFollowUserHandler.js new file mode 100644 index 000000000..e0a6a820b --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/toggleFollowUserHandler.js @@ -0,0 +1,21 @@ +import { logic } from '../../cor/index.js' + +export default (req, res, next) => { + const { username } = req + + const { targetUsername } = req.params + + try { + logic.toggleFollowUser(username, targetUsername, error => { + if (error) { + next(error) + + return + } + + res.status(204).send() + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/toggleLikePostHandler.js b/staff/lucas-orts/ponies/api/handlers/toggleLikePostHandler.js new file mode 100644 index 000000000..ffe5d90d3 --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/toggleLikePostHandler.js @@ -0,0 +1,21 @@ +import { logic } from '../../cor/index.js' + +export default (req, res, next) => { + const { username } = req + + const { postId } = req.params + + try { + logic.toggleLikePost(username, postId, error => { + if (error) { + next(error) + + return + } + + res.status(204).send() + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/handlers/updatePostCaptionHandler.js b/staff/lucas-orts/ponies/api/handlers/updatePostCaptionHandler.js new file mode 100644 index 000000000..352afc65a --- /dev/null +++ b/staff/lucas-orts/ponies/api/handlers/updatePostCaptionHandler.js @@ -0,0 +1,23 @@ +import { logic } from 'cor' + +export default (req, res, next) => { + const { username } = req + + const { postId } = req.params + + const { caption } = req.body + + try { + logic.updatePostCaption(username, postId, caption, error => { + if (error) { + next(error) + + return + } + + res.status(204).send() + }) + } catch (error) { + next(error) + } +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/index.js b/staff/lucas-orts/ponies/api/index.js index e71c81979..be8043a6b 100644 --- a/staff/lucas-orts/ponies/api/index.js +++ b/staff/lucas-orts/ponies/api/index.js @@ -1,285 +1,62 @@ +import 'dotenv/config' import express from 'express' -import logic from 'cor/logic/index.js' +import { mongoose } from '../cor/index.js' -const api = express() +import { cors, jsonBodyParser, jwtVerifier, errorHandler } from './middlewares/index.js' +import { + registerUserHandler, + authenticateUserHandler, + getAllFollowingUserPostsHandler, + getUserNameHandler, + getAllPostsHandler, + getAllFavPostsHandler, + createPostHandler, + deletePostHandler, + toggleLikePostHandler, + toggleFavPostHandler, + toggleFollowUserHandler, + updatePostCaptionHandler +} from './handlers/index.js' -api.use((req, res, next) => { - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Access-Control-Allow-Headers', '*') - res.setHeader('Access-Control-Allow-Methods', '*') +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.info(`API connected to ${process.env.MONGODB_URI}`) - next() -}) + const api = express() -api.get('/', (req, res) => { - res.send('Hello, World!') -}) + api.use(cors) -api.post('/users', (req, res) => { - req.setEncoding('utf-8') - - req.on('data', json => { - const { name, surname, email, username, password, passwordRepeat } = JSON.parse(json) - - try { - logic.registerUser(name, surname, email, username, password, passwordRepeat, error => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - - return - } - - res.status(201).send() - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } - }) -}) - -api.post('/users/auth', (req, res) => { - req.setEncoding('utf-8') - - req.on('data', json => { - const { username, password } = JSON.parse(json) - - try { - logic.authenticateUser(username, password, error => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - - return - } - - res.send() - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } - }) -}) - -api.post('/posts', (req, res) => { - const { authorization } = req.headers - - const username = authorization.slice(6) - - req.on('data', data => { - const { image, caption } = JSON.parse(data) - - try { - logic.createPost(username, image, caption, error => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - - return - } - - res.status(201).send() - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } - }) -}) - -api.get('/users/:targetUsername/name', (req, res) => { - const { authorization } = req.headers - - const username = authorization.slice(6) - - const { targetUsername } = req.params - - try { - const name = logic.getUserName(username, targetUsername, (error, name) => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - - return - } - - res.json(name) - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } -}) - -api.get('/posts', (req, res) => { - const { authorization } = req.headers - - const username = authorization.slice(6) - - try { - const posts = logic.getAllPosts(username, (error, posts) => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - - return - } - - res.json(posts) + api.get('/', (req, res) => { + res.send('Hello, World!') }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } -}) - -api.get('/posts/follows', (req, res) => { - const { authorization } = req.headers - const username = authorization.slice(6) + api.post('/users', jsonBodyParser, registerUserHandler) - try { - const posts = logic.getAllFollowingUserPosts(username, (error, posts) => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) + api.post('/users/auth', jsonBodyParser, authenticateUserHandler) - return - } + api.get('/users/:targetUsername/name', jwtVerifier, getUserNameHandler) - res.json(posts) - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } -}) + api.get('/posts', jwtVerifier, getAllPostsHandler) -api.get('/posts/favs', (req, res) => { - const { authorization } = req.headers + api.get('/posts/follows', jwtVerifier, getAllFollowingUserPostsHandler) - const username = authorization.slice(6) + api.get('/posts/favs', jwtVerifier, getAllFavPostsHandler) - try { - const posts = logic.getAllFavPosts(username, (error, posts) => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) + api.post('/posts', jwtVerifier, jsonBodyParser, createPostHandler) - return - } + api.delete('/posts/:postId', jwtVerifier, deletePostHandler) - res.json(posts) - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } -}) + api.patch('/posts/:postId/likes', jwtVerifier, toggleLikePostHandler) -api.delete('/posts/:postId', (req, res) => { - const { authorization } = req.headers + api.patch('/posts/:postId/favs', jwtVerifier, toggleFavPostHandler) - const username = authorization.slice(6) + api.patch('/users/:targetUsername/follows', jwtVerifier, toggleFollowUserHandler) - const { postId } = req.params + api.patch('/posts/:postId/caption', jwtVerifier, jsonBodyParser, updatePostCaptionHandler) - try { - logic.deletePost(username, postId, error => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) + api.use(errorHandler) - return - } - - res.status(204).send() - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } -}) - -api.patch('/posts/:postId/likes', (req, res) => { - const { authorization } = req.headers - - const username = authorization.slice(6) - - const { postId } = req.params - - try { - logic.toggleLikePost(username, postId, error => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - - return - } - - res.status(204).send() - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } -}) - -api.patch('/posts/:postId/favs', (req, res) => { - const { authorization } = req.headers - - const username = authorization.slice(6) - - const { postId } = req.params - - try { - logic.toggleFavPost(username, postId, error => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - - return - } - - res.status(204).send() - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } -}) - -api.patch('/users/:targetUsername/follows', (req, res) => { - const { authorization } = req.headers - - const username = authorization.slice(6) - - const { targetUsername } = req.params - - try { - logic.toggleFollowUser(username, targetUsername, error => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - - return - } - - res.status(204).send() - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } -}) - -api.patch('/posts/:postId/caption', (req, res) => { - const { authorization } = req.headers - - const username = authorization.slice(6) - - const { postId } = req.params - - req.setEncoding('utf-8') - - req.on('data', json => { - const { caption } = JSON.parse(json) - - try { - logic.updatePostCaption(username, postId, caption, error => { - if (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - - return - } - - res.status(204).send() - }) - } catch (error) { - res.status(500).json({ error: error.constructor.name, message: error.message }) - } + api.listen(process.env.PORT, () => console.info(`API listening on PORT ${process.env.PORT}`)) }) -}) - -api.listen(8080, () => console.log('API is up')) \ No newline at end of file + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/index1.js b/staff/lucas-orts/ponies/api/index1.js new file mode 100644 index 000000000..b98c8e142 --- /dev/null +++ b/staff/lucas-orts/ponies/api/index1.js @@ -0,0 +1,532 @@ +import 'dotenv/config' +import express from 'express' +import jwt from 'jsonwebtoken' + +import { mongoose, logic } from '../cor/index.js' + +import { errors } from 'com' + +const { ValidationError, DuplicityError, NotFoundError, CredentialsError, OwnershipError, SessionError } = errors + +mongoose.connect(process.env.MONGODB_URI) + + .then(() => { + console.info(`API connected to ${process.env.MONGODB_URI}`) + + const api = express() + + api.use((req, res, next) => { + res.setHeader('Access-Control-Allow-Origin', '*') + res.setHeader('Access-Control-Allow-Headers', '*') + res.setHeader('Access-Control-Allow-Methods', '*') + + next() + }) + + api.get('/', (req, res) => { + res.send('Hello, World!') + }) + + api.post('/users', (req, res) => { + req.setEncoding('utf-8') + + req.on('data', json => { + const { name, surname, email, username, password, passwordRepeat } = JSON.parse(json) + + try { + logic.registerUser(name, surname, email, username, password, passwordRepeat, error => { + if (error) { + let status = 500 + if (error instanceof DuplicityError) + status = 409 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(201).send() + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + api.post('/users/auth', (req, res) => { + req.setEncoding('utf-8') + + req.on('data', json => { + const { username, password } = JSON.parse(json) + + try { + logic.authenticateUser(username, password, error => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + else if (error instanceof CredentialsError) + status = 401 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + jwt.sign({ sub: username }, process.env.JWT_SECRET, (error, token) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + res.json(token) + }) + }) + } catch (error) { + let status = 500 + if (error instanceof ValidationError) + status = 400 + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + api.post('/posts', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + req.setEncoding('utf-8') + + req.on('data', json => { + const { image, caption } = JSON.parse(json) + + try { + logic.createPost(username, image, caption, error => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(201).send() + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + }) + + api.get('/users/:targetUsername/name', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + const { targetUsername } = req.params + + try { + logic.getUserName(username, targetUsername, (error, name) => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.json(name) + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + api.get('/posts', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + try { + logic.getAllPosts(username, (error, posts) => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.json(posts) + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + api.get('/posts/follows', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + try { + logic.getAllFollowingUserPosts(username, (error, posts) => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.json(posts) + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + api.get('/posts/favs', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + try { + logic.getAllFavPosts(username, (error, posts) => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.json(posts) + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + + api.delete('/posts/:postId', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + const { postId } = req.params + + try { + logic.deletePost(username, postId, error => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + else if (error instanceof OwnershipError) + status = 403 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(204).send() + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + + api.patch('/posts/:postId/likes', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + const { postId } = req.params + + try { + logic.toggleLikePost(username, postId, error => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(204).send() + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + api.patch('/posts/:postId/favs', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + const { postId } = req.params + + try { + logic.toggleFavPost(username, postId, error => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(204).send() + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + api.patch('/users/:targetUsername/follows', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + const { targetUsername } = req.params + + try { + logic.toggleFollowUser(username, targetUsername, error => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(204).send() + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + + api.patch('/posts/:postId/caption', (req, res) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + const { postId } = req.params + + req.setEncoding('utf-8') + + req.on('data', json => { + const { caption } = JSON.parse(json) + + try { + logic.updatePostCaption(username, postId, caption, error => { + if (error) { + let status = 500 + + if (error instanceof NotFoundError) + status = 404 + else if (error instanceof OwnershipError) + status = 403 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + + return + } + + res.status(204).send() + }) + } catch (error) { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + + res.status(status).json({ error: error.constructor.name, message: error.message }) + } + }) + }) + }) + api.listen(process.env.PORT, () => console.info(`API listening on PORT ${process.env.PORT}`)) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/middlewares/cors.js b/staff/lucas-orts/ponies/api/middlewares/cors.js new file mode 100644 index 000000000..88a3b4c3d --- /dev/null +++ b/staff/lucas-orts/ponies/api/middlewares/cors.js @@ -0,0 +1,7 @@ +export default (req, res, next) => { + res.setHeader('Access-Control-Allow-Origin', '*') + res.setHeader('Access-Control-Allow-Headers', '*') + res.setHeader('Access-Control-Allow-Methods', '*') + + next() +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/middlewares/errorHandler.js b/staff/lucas-orts/ponies/api/middlewares/errorHandler.js new file mode 100644 index 000000000..11f25aeda --- /dev/null +++ b/staff/lucas-orts/ponies/api/middlewares/errorHandler.js @@ -0,0 +1,22 @@ +import { errors } from 'com' + +const { NotFoundError, CredentialsError, OwnershipError, DuplicityError, ValidationError, SessionError } = errors + +export default (error, req, res, next) => { + let status = 500 + + if (error instanceof ValidationError) + status = 400 + else if (error instanceof CredentialsError) + status = 401 + else if (error instanceof OwnershipError) + status = 403 + else if (error instanceof NotFoundError) + status = 404 + else if (error instanceof DuplicityError) + status = 409 + else if (error instanceof SessionError) + status = 498 + + res.status(status).json({ error: error.constructor.name, message: error.message }) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/middlewares/index.js b/staff/lucas-orts/ponies/api/middlewares/index.js new file mode 100644 index 000000000..0b031c23d --- /dev/null +++ b/staff/lucas-orts/ponies/api/middlewares/index.js @@ -0,0 +1,11 @@ +import cors from './cors.js' +import jsonBodyParser from './jsonBodyParser.js' +import jwtVerifier from './jwtVerifier.js' +import errorHandler from './errorHandler.js' + +export { + cors, + jsonBodyParser, + jwtVerifier, + errorHandler +} diff --git a/staff/lucas-orts/ponies/api/middlewares/jsonBodyParser.js b/staff/lucas-orts/ponies/api/middlewares/jsonBodyParser.js new file mode 100644 index 000000000..e515e98ee --- /dev/null +++ b/staff/lucas-orts/ponies/api/middlewares/jsonBodyParser.js @@ -0,0 +1,11 @@ +export default (req, res, next) => { + req.setEncoding('utf-8') + + req.on('data', json => { + const body = JSON.parse(json) + + req.body = body + + next() + }) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/middlewares/jwtVerifier.js b/staff/lucas-orts/ponies/api/middlewares/jwtVerifier.js new file mode 100644 index 000000000..38e9e14e3 --- /dev/null +++ b/staff/lucas-orts/ponies/api/middlewares/jwtVerifier.js @@ -0,0 +1,25 @@ +import jwt from 'jsonwebtoken' + +import { errors } from 'com' + +const { SessionError } = errors + +export default (req, res, next) => { + const { authorization } = req.headers + + const token = authorization.slice(7) + + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { + if (error) { + res.status(498).json({ error: SessionError.name, message: error.message }) + + return + } + + const { sub: username } = payload + + req.username = username + + next() + }) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/package-lock.json b/staff/lucas-orts/ponies/api/package-lock.json index 3f4b50829..b6829686a 100644 --- a/staff/lucas-orts/ponies/api/package-lock.json +++ b/staff/lucas-orts/ponies/api/package-lock.json @@ -9,14 +9,54 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "com": "file:../com", "cor": "file:../cor", - "express": "^4.19.2" + "express": "^4.19.2", + "jsonwebtoken": "^9.0.2", + "mongodb": "^6.8.0" + }, + "devDependencies": { + "dotenv": "^16.4.5" } }, - "../cor": { + "../com": { "version": "1.0.0", "license": "ISC" }, + "../cor": { + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "com": "file:../com", + "mongoose": "^8.5.1" + }, + "devDependencies": { + "chai": "^5.1.1", + "dotenv": "^16.4.5", + "mocha": "^10.7.0" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", + "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -60,6 +100,19 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/bson": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", + "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -88,6 +141,10 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/com": { + "resolved": "../com", + "link": true + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -173,6 +230,26 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -433,6 +510,86 @@ "node": ">= 0.10" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -442,6 +599,11 @@ "node": ">= 0.6" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -490,6 +652,60 @@ "node": ">= 0.6" } }, + "node_modules/mongodb": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.8.0.tgz", + "integrity": "sha512-HGQ9NWDle5WvwMnrvUxsFYPd3JEbqD3RgABHBQRuoCEND0qzhsd0iH5ypHsf1eJ+sXmvmyKpP+FLOKY8Il7jMw==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -557,6 +773,14 @@ "node": ">= 0.10" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -622,6 +846,17 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "0.18.0", "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", @@ -708,6 +943,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -726,6 +969,17 @@ "node": ">=0.6" } }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -765,6 +1019,26 @@ "engines": { "node": ">= 0.8" } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } } } -} +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/package.json b/staff/lucas-orts/ponies/api/package.json index 1171ee9e3..611dd67e0 100644 --- a/staff/lucas-orts/ponies/api/package.json +++ b/staff/lucas-orts/ponies/api/package.json @@ -13,7 +13,13 @@ "author": "", "license": "ISC", "dependencies": { + "com": "file:../com", "cor": "file:../cor", - "express": "^4.19.2" + "express": "^4.19.2", + "jsonwebtoken": "^9.0.2", + "mongodb": "^6.8.0" + }, + "devDependencies": { + "dotenv": "^16.4.5" } -} \ No newline at end of file +} diff --git a/staff/lucas-orts/ponies/api/test/authenticate-user.sh b/staff/lucas-orts/ponies/api/test/authenticate-user.sh index e75970fd8..a93edb9c5 100644 --- a/staff/lucas-orts/ponies/api/test/authenticate-user.sh +++ b/staff/lucas-orts/ponies/api/test/authenticate-user.sh @@ -1 +1 @@ -curl -v http://localhost:8080/users/auth -X POST -d '{"username":"Petazeta","password":"123123123"}' \ No newline at end of file +curl -v http://localhost:8080/users/auth -X POST -d '{"username":"estercolero","password":"87654321"}' \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/test/create-post.sh b/staff/lucas-orts/ponies/api/test/create-post.sh index 4f5afba12..5e5a28c8a 100644 --- a/staff/lucas-orts/ponies/api/test/create-post.sh +++ b/staff/lucas-orts/ponies/api/test/create-post.sh @@ -1 +1 @@ -jcurl -v http://localhost:8080/posts -X POST -d '{"image":"https://media.giphy.com/media/Ty9Sg8oHghPWg/giphy.gif?cid=790b7611k1isspkgnlxqcvk07kqq26fe137qherkek4mavvf&ep=v1_gifs_trending&rid=giphy.gif&ct=g","caption":"ok"}' -H "Authorization: Basic Tomasturbao" \ No newline at end of file +curl -v http://localhost:8080/posts -X POST -d '{"image":"https://media.giphy.com/media/Ty9Sg8oHghPWg/giphy.gif?cid=790b7611k1isspkgnlxqcvk07kqq26fe137qherkek4mavvf&ep=v1_gifs_trending&rid=giphy.gif&ct=g","caption":"ok"}' -H "Authorization: Basic Estercolero" \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/test/register-user.sh b/staff/lucas-orts/ponies/api/test/register-user.sh index 49e78a69d..2b69afa1a 100644 --- a/staff/lucas-orts/ponies/api/test/register-user.sh +++ b/staff/lucas-orts/ponies/api/test/register-user.sh @@ -1 +1 @@ -curl -v http://localhost:8080/register/user -X POST -d '{"name":"Ester","surname":"Colero","email":"ester@colero.com","username":"estercolero","password":"87654321","passwordRepeat":"87654321"}' \ No newline at end of file +curl -v http://localhost:8080/users -X POST -d '{"name":"Ester","surname":"Colero","email":"ester@colero.com","username":"estercolero","password":"87654321","passwordRepeat":"87654321"}' -H "Content-Type: application/json" \ No newline at end of file diff --git a/staff/lucas-orts/ponies/api/test/toggle-fav-post.sh b/staff/lucas-orts/ponies/api/test/toggle-fav-post.sh index 19b9b510f..fdd5dbba0 100644 --- a/staff/lucas-orts/ponies/api/test/toggle-fav-post.sh +++ b/staff/lucas-orts/ponies/api/test/toggle-fav-post.sh @@ -1 +1 @@ -curl -v http://localhost:8080/posts/4zcpc42md1w/favs -X PATCH -H "Authorization: Basic Tomasturbao" \ No newline at end of file +curl -v http://localhost:8080/posts/66a212db45efecc54ca44e1f/favs -X PATCH -H "Authorization: Basic Estercolero" \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/createPost.js b/staff/lucas-orts/ponies/app/logic/createPost.js index c2d46011c..7849f166e 100644 --- a/staff/lucas-orts/ponies/app/logic/createPost.js +++ b/staff/lucas-orts/ponies/app/logic/createPost.js @@ -1,6 +1,6 @@ -import validate from '../../cor/validate.js' +import { validate, errors } from 'com' -const createPost = (image, caption, callback) => { +export default (image, caption, callback) => { validate.image(image) validate.string(caption, 'caption') validate.callback(callback) @@ -16,18 +16,16 @@ const createPost = (image, caption, callback) => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('POST', 'http://localhost:8080/posts') - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('POST', `${import.meta.env.VITE_API_URL}/posts`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.setRequestHeader('Content-Type', 'application/json') xhr.send(JSON.stringify({ image, caption })) -} - -export default createPost \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/deletePost.js b/staff/lucas-orts/ponies/app/logic/deletePost.js index b75f4bc32..54ae0cba9 100644 --- a/staff/lucas-orts/ponies/app/logic/deletePost.js +++ b/staff/lucas-orts/ponies/app/logic/deletePost.js @@ -1,6 +1,6 @@ -import validate from '../../cor/validate.js' +import { validate, errors } from 'com' -const deletePost = (postId, callabck) => { +export default (postId, callabck) => { validate.postId(postId) validate.callback(callabck) @@ -15,17 +15,15 @@ const deletePost = (postId, callabck) => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callabck(new constructor(message)) } xhr.onerror = () => callabck(new Error('network error')) - xhr.open('DELETE', `http://localhost:8080/posts/${postId}`) - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('DELETE', `${import.meta.env.VITE_API_URL}/posts/${postId}`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.send() -} - -export default deletePost \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/getAllFavPosts.js b/staff/lucas-orts/ponies/app/logic/getAllFavPosts.js index 9afc7755e..44629c1bc 100644 --- a/staff/lucas-orts/ponies/app/logic/getAllFavPosts.js +++ b/staff/lucas-orts/ponies/app/logic/getAllFavPosts.js @@ -1,6 +1,6 @@ -import validate from "../../cor/validate.js" +import { validate, errors } from 'com' -const getAllFavPosts = callback => { +export default callback => { validate.callback(callback) const xhr = new XMLHttpRequest @@ -16,15 +16,14 @@ const getAllFavPosts = callback => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('GET', 'http://localhost:8080/posts/favs') - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('GET', `${import.meta.env.VITE_API_URL}/posts/favs`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.send() -} -export default getAllFavPosts \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/getAllFollowingUserPosts.js b/staff/lucas-orts/ponies/app/logic/getAllFollowingUserPosts.js index 84d51079c..5a6043aee 100644 --- a/staff/lucas-orts/ponies/app/logic/getAllFollowingUserPosts.js +++ b/staff/lucas-orts/ponies/app/logic/getAllFollowingUserPosts.js @@ -1,6 +1,6 @@ -import validate from "../../cor/validate.js" +import { validate, errors } from 'com' -const getAllFollowingUserPosts = callabck => { +export default callabck => { validate.callback(callabck) const xhr = new XMLHttpRequest @@ -16,16 +16,14 @@ const getAllFollowingUserPosts = callabck => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callabck(new constructor(message)) } xhr.onerror = () => callabck(new Error('network error')) - xhr.open('GET', 'http://localhost:8080/posts/follows') - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('GET', `${import.meta.env.VITE_API_URL}/posts/follows`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.send() -} - -export default getAllFollowingUserPosts \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/getAllPosts.js b/staff/lucas-orts/ponies/app/logic/getAllPosts.js index d8142bc24..c119ea878 100644 --- a/staff/lucas-orts/ponies/app/logic/getAllPosts.js +++ b/staff/lucas-orts/ponies/app/logic/getAllPosts.js @@ -1,6 +1,6 @@ -import validate from "../../cor/validate.js" +import { validate, errors } from 'com' -const getAllPosts = callback => { +export default callback => { validate.callback(callback) const xhr = new XMLHttpRequest @@ -16,16 +16,14 @@ const getAllPosts = callback => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('GET', 'http://localhost:8080/posts') - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('GET', `${import.meta.env.VITE_API_URL}/posts`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.send() -} - -export default getAllPosts \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/getUser.js b/staff/lucas-orts/ponies/app/logic/getUser.js index 6cedda101..7ee5b079f 100644 --- a/staff/lucas-orts/ponies/app/logic/getUser.js +++ b/staff/lucas-orts/ponies/app/logic/getUser.js @@ -1,6 +1,6 @@ -import validate from '../validate' +import { validate, errors } from 'com' -const getUser = (callback) => { +export default (callback) => { validate.callback(callback) const xhr = new XMLHttpRequest @@ -16,16 +16,14 @@ const getUser = (callback) => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('GET', `http://localhost:8080/users/${sessionStorage.username}/settings`) - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('GET', `${import.meta.env.VITE_API_URL}/users/${sessionStorage.username}/settings`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.send() -} - -export default getUser \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/getUserName.js b/staff/lucas-orts/ponies/app/logic/getUserName.js index 99a1d985e..f68b901df 100644 --- a/staff/lucas-orts/ponies/app/logic/getUserName.js +++ b/staff/lucas-orts/ponies/app/logic/getUserName.js @@ -1,6 +1,7 @@ -import validate from "../../cor/validate.js" +import { validate, errors } from 'com' +import extractPayloadFromToken from "../util/extractPayloadFromToken" -const getUserName = callback => { +export default callback => { validate.callback(callback) const xhr = new XMLHttpRequest @@ -16,15 +17,16 @@ const getUserName = callback => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('GET', `http://localhost:8080/users/${sessionStorage.username}/name`) - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + const { sub: username } = extractPayloadFromToken(sessionStorage.token) + + xhr.open('GET', `${import.meta.env.VITE_API_URL}/users/${username}/name`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.send() -} -export default getUserName \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/getUserUsername.js b/staff/lucas-orts/ponies/app/logic/getUserUsername.js index 69e37f836..d8f13feb3 100644 --- a/staff/lucas-orts/ponies/app/logic/getUserUsername.js +++ b/staff/lucas-orts/ponies/app/logic/getUserUsername.js @@ -1,3 +1,7 @@ -const getUserUsername = () => sessionStorage.username +import extractPayloadFromToken from '../util/extractPayloadFromToken' -export default getUserUsername \ No newline at end of file +export default () => { + const { sub: username } = extractPayloadFromToken(sessionStorage.token) + + return username +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/isUserLoggedIn.js b/staff/lucas-orts/ponies/app/logic/isUserLoggedIn.js index 099f27484..cb96caf2a 100644 --- a/staff/lucas-orts/ponies/app/logic/isUserLoggedIn.js +++ b/staff/lucas-orts/ponies/app/logic/isUserLoggedIn.js @@ -1,11 +1 @@ -function isUserLoggedIn() { - //if (sessionStorage.username) return true - //return false - - //return sessionStorage.username ? true :false - - return !!sessionStorage.username - -} - -export default isUserLoggedIn \ No newline at end of file +export default () => !!sessionStorage.token diff --git a/staff/lucas-orts/ponies/app/logic/loginUser.js b/staff/lucas-orts/ponies/app/logic/loginUser.js index 3b6439027..e11e72b0d 100644 --- a/staff/lucas-orts/ponies/app/logic/loginUser.js +++ b/staff/lucas-orts/ponies/app/logic/loginUser.js @@ -1,6 +1,6 @@ -import validate from '../../cor/validate.js' +import { validate, errors } from 'com' -const loginUser = (username, password, callback) => { +export default (username, password, callback) => { validate.username(username) validate.password(password) validate.callback(callback) @@ -9,7 +9,8 @@ const loginUser = (username, password, callback) => { xhr.onload = () => { if (xhr.status === 200) { - sessionStorage.username = username + const token = JSON.parse(xhr.response) + sessionStorage.token = token callback(null) @@ -18,17 +19,15 @@ const loginUser = (username, password, callback) => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('POST', 'http://localhost:8080/users/auth') + xhr.open('POST', `${import.meta.env.VITE_API_URL}/users/auth`) xhr.setRequestHeader('Content-Type', 'application/json') xhr.send(JSON.stringify({ username, password })) -} - -export default loginUser \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/logoutUser.js b/staff/lucas-orts/ponies/app/logic/logoutUser.js index cce13cc28..7e33c1d52 100644 --- a/staff/lucas-orts/ponies/app/logic/logoutUser.js +++ b/staff/lucas-orts/ponies/app/logic/logoutUser.js @@ -1,3 +1 @@ -const logoutUser = () => delete sessionStorage.username - -export default logoutUser \ No newline at end of file +export default () => delete sessionStorage.username \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/registerUser.js b/staff/lucas-orts/ponies/app/logic/registerUser.js index 3ade8a99a..d6f25c21f 100644 --- a/staff/lucas-orts/ponies/app/logic/registerUser.js +++ b/staff/lucas-orts/ponies/app/logic/registerUser.js @@ -1,6 +1,6 @@ -import validate from '../../cor/validate.js' +import { validate, errors } from 'com' -const registerUser = (name, surname, email, username, password, passwordRepeat, callback) => { +export default (name, surname, email, username, password, passwordRepeat, callback) => { validate.name(name) validate.name(surname, 'surname') validate.email(email) @@ -19,16 +19,15 @@ const registerUser = (name, surname, email, username, password, passwordRepeat, const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('POST', 'http://localhost:8080/users') + xhr.open('POST', `${import.meta.env.VITE_API_URL}/users`) xhr.setRequestHeader('Content-Type', 'application/json') xhr.send(JSON.stringify({ name, surname, email, username, password, passwordRepeat })) } -export default registerUser \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/toggleFavPost.js b/staff/lucas-orts/ponies/app/logic/toggleFavPost.js index 3b9a3f3fe..f185cc25a 100644 --- a/staff/lucas-orts/ponies/app/logic/toggleFavPost.js +++ b/staff/lucas-orts/ponies/app/logic/toggleFavPost.js @@ -1,6 +1,6 @@ -import validate from '../../cor/validate.js' +import { validate, errors } from 'com' -const toggleFavPost = (postId, callback) => { +export default (postId, callback) => { validate.postId(postId) validate.callback(callback) @@ -15,17 +15,15 @@ const toggleFavPost = (postId, callback) => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('PATCH', `http://localhost:8080/posts/${postId}/favs`) - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('PATCH', `${import.meta.env.VITE_API_URL}/posts/${postId}/favs`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.send() -} - -export default toggleFavPost \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/toggleFollowUser.js b/staff/lucas-orts/ponies/app/logic/toggleFollowUser.js index b0ca0bf08..2a0dd0c9a 100644 --- a/staff/lucas-orts/ponies/app/logic/toggleFollowUser.js +++ b/staff/lucas-orts/ponies/app/logic/toggleFollowUser.js @@ -1,6 +1,6 @@ -import validate from '../../cor/validate.js' +import { validate, errors } from 'com' -const toggleFollowUser = (username, callback) => { +export default (username, callback) => { validate.username(username) validate.callback(callback) @@ -15,17 +15,15 @@ const toggleFollowUser = (username, callback) => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('PATCH', `http://localhost:8080/users/${username}/follows`) - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('PATCH', `${import.meta.env.VITE_API_URL}/users/${username}/follows`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.send() -} - -export default toggleFollowUser \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/toggleLikePost.js b/staff/lucas-orts/ponies/app/logic/toggleLikePost.js index a49efa9ec..104d4f513 100644 --- a/staff/lucas-orts/ponies/app/logic/toggleLikePost.js +++ b/staff/lucas-orts/ponies/app/logic/toggleLikePost.js @@ -1,6 +1,6 @@ -import validate from '../../cor/validate.js' +import { validate, errors } from 'com' -const toggleLikePost = (postId, callback) => { +export default (postId, callback) => { validate.postId(postId) validate.callback(callback) @@ -15,17 +15,15 @@ const toggleLikePost = (postId, callback) => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('PATCH', `http://localhost:8080/posts/${postId}/likes`) - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('PATCH', `${import.meta.env.VITE_API_URL}/posts/${postId}/likes`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.send() -} - -export default toggleLikePost \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/logic/updatePostCaption.js b/staff/lucas-orts/ponies/app/logic/updatePostCaption.js index 5f4ef4b4a..0ad3c8df8 100644 --- a/staff/lucas-orts/ponies/app/logic/updatePostCaption.js +++ b/staff/lucas-orts/ponies/app/logic/updatePostCaption.js @@ -1,6 +1,6 @@ -import validate from '../../cor/validate.js' +import { validate, errors } from 'com' -const updatePostCaption = (postId, caption, callback) => { +export default (postId, caption, callback) => { validate.postId(postId) validate.string(caption, 'caption') validate.callback(callback) @@ -16,18 +16,16 @@ const updatePostCaption = (postId, caption, callback) => { const { error, message } = JSON.parse(xhr.response) - const constructor = window[error] + const constructor = errors[error] callback(new constructor(message)) } xhr.onerror = () => callback(new Error('network error')) - xhr.open('PATCH', `http://localhost:8080/posts/${postId}/caption`) - xhr.setRequestHeader('Authorization', `Basic ${sessionStorage.username}`) + xhr.open('PATCH', `${import.meta.env.VITE_API_URL}/posts/${postId}/caption`) + xhr.setRequestHeader('Authorization', `Bearer ${sessionStorage.token}`) xhr.setRequestHeader('Content-Type', 'application/json') xhr.send(JSON.stringify({ caption })) -} - -export default updatePostCaption \ No newline at end of file +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/package-lock.json b/staff/lucas-orts/ponies/app/package-lock.json index 7bb602bfc..38f01d70a 100644 --- a/staff/lucas-orts/ponies/app/package-lock.json +++ b/staff/lucas-orts/ponies/app/package-lock.json @@ -1,13 +1,14 @@ { - "name": "vite-project", + "name": "app", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "vite-project", + "name": "app", "version": "0.0.0", "dependencies": { + "com": "file:../com", "react": "^18.3.1", "react-dom": "^18.3.1" }, @@ -23,6 +24,10 @@ "vitest": "^2.0.1" } }, + "../com": { + "version": "1.0.0", + "license": "ISC" + }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", @@ -1830,6 +1835,10 @@ "dev": true, "license": "MIT" }, + "node_modules/com": { + "resolved": "../com", + "link": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", diff --git a/staff/lucas-orts/ponies/app/package.json b/staff/lucas-orts/ponies/app/package.json index 1d4db1486..10be59f1a 100644 --- a/staff/lucas-orts/ponies/app/package.json +++ b/staff/lucas-orts/ponies/app/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "com": "file:../com", "react": "^18.3.1", "react-dom": "^18.3.1" }, @@ -24,4 +25,4 @@ "vite": "^5.3.1", "vitest": "^2.0.1" } -} \ No newline at end of file +} diff --git a/staff/lucas-orts/ponies/app/util/extractPayloadFromToken.js b/staff/lucas-orts/ponies/app/util/extractPayloadFromToken.js new file mode 100644 index 000000000..044aeb436 --- /dev/null +++ b/staff/lucas-orts/ponies/app/util/extractPayloadFromToken.js @@ -0,0 +1,9 @@ +export default token => { + const payloadB64 = token.slice(token.indexOf('.') + 1, token.lastIndexOf('.')) + + const payloadJSON = atob(payloadB64) + + const payload = JSON.parse(payloadJSON) + + return payload +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/app/views/Login.jsx b/staff/lucas-orts/ponies/app/views/Login.jsx index 3d7d80351..26b3c1e10 100644 --- a/staff/lucas-orts/ponies/app/views/Login.jsx +++ b/staff/lucas-orts/ponies/app/views/Login.jsx @@ -8,6 +8,10 @@ import Link from './components/Link' import Button from './components/Button' import Container from "./components/Container" +import { errors } from 'com' + +const { NotFoundError, CredentialsError } = errors + function Login({ onLogin, onRegisterClick }) { @@ -27,6 +31,11 @@ function Login({ onLogin, onRegisterClick }) { if (error) { console.error(error) + let message = error.message + + if (error instanceof NotFoundError || error instanceof CredentialsError) + message = 'incorrect username and/or password' + alert(error.message) return diff --git a/staff/lucas-orts/ponies/app/views/Register.jsx b/staff/lucas-orts/ponies/app/views/Register.jsx index 87e98ca64..4aa643f49 100644 --- a/staff/lucas-orts/ponies/app/views/Register.jsx +++ b/staff/lucas-orts/ponies/app/views/Register.jsx @@ -31,11 +31,19 @@ function Register({ onRegister, onLoginClick }) { const passwordRepeat = passwordRepeatInput.value try { - logic.registerUser(name, surname, email, username, password, passwordRepeat) + logic.registerUser(name, surname, email, username, password, passwordRepeat, error => { + if (error) { + console.error(error) - alert('user successfully registered') + alert(error.message) - onRegister() + return + } + + alert('user successfully registered') + + onRegister() + }) } catch (error) { console.error(error) diff --git a/staff/lucas-orts/ponies/com/errors.js b/staff/lucas-orts/ponies/com/errors.js new file mode 100644 index 000000000..6df424ec6 --- /dev/null +++ b/staff/lucas-orts/ponies/com/errors.js @@ -0,0 +1,67 @@ +class ValidationError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class NotFoundError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class DuplicityError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class SystemError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class CredentialsError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class OwnershipError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +class SessionError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + +const errors = { + ValidationError, + NotFoundError, + DuplicityError, + SystemError, + CredentialsError, + OwnershipError, + SessionError +} + +export default errors \ No newline at end of file diff --git a/staff/lucas-orts/ponies/com/index.js b/staff/lucas-orts/ponies/com/index.js new file mode 100644 index 000000000..63289cb6d --- /dev/null +++ b/staff/lucas-orts/ponies/com/index.js @@ -0,0 +1,7 @@ +import validate from './validate.js' +import errors from './errors.js' + +export { + errors, + validate +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/com/package.json b/staff/lucas-orts/ponies/com/package.json new file mode 100644 index 000000000..419029cd0 --- /dev/null +++ b/staff/lucas-orts/ponies/com/package.json @@ -0,0 +1,13 @@ +{ + "name": "com", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "" +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/com/validate.js b/staff/lucas-orts/ponies/com/validate.js new file mode 100644 index 000000000..703f9ea86 --- /dev/null +++ b/staff/lucas-orts/ponies/com/validate.js @@ -0,0 +1,72 @@ +import errors from './errors.js' + +const { ValidationError } = errors + +const USERNAME_REGEX = /^(?!.*\s{2})[a-zA-Z0-9._-]{4,16}$/ +const NAME_REGEX = /^(?!.*\s{2})[a-zA-Z ]{3,16}$/ +const EMAIL_REGEX = /^[a-z0-9._]+@[a-z0-9.-]{3,63}\.[a-z]{2,10}$/ + +function validateString(value, explain = 'value') { + if (typeof value !== 'string') throw new ValidationError(`${explain} is not a string`) +} + +function validateCallback(callback, explain = 'callback') { + if (typeof callback !== 'function') throw new ValidationError(`${explain} is not a function`) +} + +function validateObject(object, explain = 'object') { + if (object === null || typeof object !== 'object' || object.constructor !== Object) throw new ValidationError(`${explain} is not an object`) +} + +function validateUsername(username, explain = 'username') { + validateString(username, explain) + if (!USERNAME_REGEX.test(username)) throw new ValidationError(`invalid ${explain}`) +} + +function validatePassword(password) { + validateString(password, 'password') + if (password.trim().length < 8) throw new ValidationError('password length is lower than 8 characters') + if (password.includes(' ')) throw new ValidationError('password has empty spaces') +} + +function validateName(name, explain = 'name') { + validateString(name, explain) + if (!NAME_REGEX.test(name)) throw new ValidationError(`invalid ${explain}`) +} + +function validateEmail(email) { + validateString(email, 'email') + if (!EMAIL_REGEX.test(email)) throw new ValidationError('invalid email') +} + +function validatePostId(postId) { + validateString(postId, 'postId') + if (postId.trim().length === 0) throw new ValidationError('invalid postId') +} + +function validateImage(image, explain = 'image') { + validateString(image, 'image') + if (!image.startsWith('http')) throw new ValidationError(`invalid ${explain}`) +} + +function validateDate(date) { + validateString(date, 'date') + if (date.trim().length === 0) throw new ValidationError('invalid date') +} + + +const validate = { + callback: validateCallback, + string: validateString, + object: validateObject, + username: validateUsername, + password: validatePassword, + name: validateName, + email: validateEmail, + postId: validatePostId, + image: validateImage, + postId: validatePostId, + date: validateDate +} + +export default validate \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/deletePost.js b/staff/lucas-orts/ponies/cor/data/deletePost.js deleted file mode 100644 index a65a9abe5..000000000 --- a/staff/lucas-orts/ponies/cor/data/deletePost.js +++ /dev/null @@ -1,44 +0,0 @@ -import fs from 'fs' -import path from 'path'; -import { fileURLToPath } from 'url' - -import validate from '../validate.js' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - - -function deletePost(condition, callback) { - validate.callback(condition, 'condition') - validate.callback(callback) - - fs.readFile(`${__dirname}/posts.json`, 'utf8', (error, json) => { - if (error) { - callback(new Error(error.message)) - - return - } - - const posts = json ? JSON.parse(json) : [] - - const postIndex = posts.findIndex(condition) - - if (postIndex > -1) { - posts.splice(postIndex, 1) - - json = JSON.stringify(posts) - - fs.writeFile(`${__dirname}/posts.json`, json, error => { - if (error) { - callback(new Error(error.message)) - - return - } - - callback(null) - }) - } - }) -} - -export default deletePost \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/deletePost.test.js b/staff/lucas-orts/ponies/cor/data/deletePost.test.js deleted file mode 100644 index bfbeefed3..000000000 --- a/staff/lucas-orts/ponies/cor/data/deletePost.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import deletePost from "./deletePost.js" - -deletePost(post => post.id === '2gouhl5uyg06721376432123', error => { - if (error) { - console.error(error) - - return - } - - console.log('post deleted') -}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/findPost.js b/staff/lucas-orts/ponies/cor/data/findPost.js deleted file mode 100644 index 43baa2220..000000000 --- a/staff/lucas-orts/ponies/cor/data/findPost.js +++ /dev/null @@ -1,29 +0,0 @@ -import fs from 'fs' -import path from 'path'; -import { fileURLToPath } from 'url' - -import validate from '../validate.js' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const findPost = (condition, callback) => { - validate.callback(condition, 'condition') - validate.callback(callback) - - fs.readFile(`${__dirname}/posts.json`, 'utf8', (error, json) => { - if (error) { - callback(new Error(error.message)) - - return - } - - const posts = json ? JSON.parse(json) : [] - - const post = posts.find(condition) - - callback(null, post || null) - }) -} - -export default findPost \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/findPost.test.js b/staff/lucas-orts/ponies/cor/data/findPost.test.js deleted file mode 100644 index c9d14681a..000000000 --- a/staff/lucas-orts/ponies/cor/data/findPost.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import findPost from "./findPost.js" - -const postFound = findPost(post => post.id === '2gouhl5uylg0', (error, post) => { - if (error) { - console.error(error) - - return - } - - console.log('Post found', post) -}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/findPosts.js b/staff/lucas-orts/ponies/cor/data/findPosts.js deleted file mode 100644 index 3ebde0d95..000000000 --- a/staff/lucas-orts/ponies/cor/data/findPosts.js +++ /dev/null @@ -1,29 +0,0 @@ -import fs from 'fs' -import path from 'path'; -import { fileURLToPath } from 'url' - -import validate from '../validate.js' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const findPosts = (condition, callback) => { - validate.callback(condition, 'condition') - validate.callback(callback) - - fs.readFile(`${__dirname}/posts.json`, 'utf8', (error, json) => { - if (error) { - callback(new Error(error.message)) - - return - } - - const posts = json ? JSON.parse(json) : [] - - const foundPosts = posts.filter(condition) - - callback(null, foundPosts) - }) -} - -export default findPosts \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/findPosts.test.js b/staff/lucas-orts/ponies/cor/data/findPosts.test.js deleted file mode 100644 index 54f2d5ec4..000000000 --- a/staff/lucas-orts/ponies/cor/data/findPosts.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import findPosts from "./findPosts.js" - -const postsFound = findPosts(post => post.author = 'Petazeta', (error, posts) => { - if (error) { - console.error(error) - - return - } - console.log(postsFound) -}) - diff --git a/staff/lucas-orts/ponies/cor/data/findUser.js b/staff/lucas-orts/ponies/cor/data/findUser.js deleted file mode 100644 index 8eb6b134a..000000000 --- a/staff/lucas-orts/ponies/cor/data/findUser.js +++ /dev/null @@ -1,29 +0,0 @@ -import fs from 'fs' -import path from 'path'; -import { fileURLToPath } from 'url' - -import validate from '../validate.js' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const findUser = (condition, callback) => { - validate.callback(condition, 'condition') - validate.callback(callback) - - fs.readFile(`${__dirname}/users.json`, 'utf8', (error, json) => { - if (error) { - callback(new Error(error.message)) - - return - } - - const users = json ? JSON.parse(json) : [] - - const user = users.find(condition) - - callback(null, user || null) - }) -} - -export default findUser \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/findUser.test.js b/staff/lucas-orts/ponies/cor/data/findUser.test.js deleted file mode 100644 index 7000bec97..000000000 --- a/staff/lucas-orts/ponies/cor/data/findUser.test.js +++ /dev/null @@ -1,12 +0,0 @@ -import findUser from "./findUser.js"; - -const userfound = findUser(user => user.name === 'Caca', (error, user) => { - if (error) { - console.error(error) - - return - } - - console.log('User found', user) -}) - diff --git a/staff/lucas-orts/ponies/cor/data/index.js b/staff/lucas-orts/ponies/cor/data/index.js index 680f75aaf..47c26b640 100644 --- a/staff/lucas-orts/ponies/cor/data/index.js +++ b/staff/lucas-orts/ponies/cor/data/index.js @@ -1,21 +1,3 @@ -import deletePost from './deletePost.js' -import findPost from './findPost.js' -import findPosts from './findPosts.js' -import findUser from './findUser.js' -import insertPost from './insertPost.js' -import insertUser from './insertUser.js' -import updatePost from './updatePost.js' -import updateUser from './updateUser.js' - -const data = { - deletePost, - findPost, - findPosts, - findUser, - insertPost, - insertUser, - updatePost, - updateUser -} +const data = {} export default data \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/insertPost.js b/staff/lucas-orts/ponies/cor/data/insertPost.js deleted file mode 100644 index e1395cfbe..000000000 --- a/staff/lucas-orts/ponies/cor/data/insertPost.js +++ /dev/null @@ -1,39 +0,0 @@ -import fs from 'fs' -import path from 'path'; -import { fileURLToPath } from 'url' - -import validate from '../validate.js' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const insertPost = (post, callback) => { - validate.object(post, 'post') - validate.callback(callback) - - fs.readFile(`${__dirname}/posts.json`, 'utf8', (error, json) => { - if (error) { - callback(new Error(error.message)) - - return - } - - const posts = json ? JSON.parse(json) : [] - - posts.push(post) - - json = JSON.stringify(posts) - - fs.writeFile(`${__dirname}/posts.json`, json, error => { - if (error) { - callback(new Error(error.message)) - - return - } - - callback(null) - }) - }) -} - -export default insertPost \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/insertPost.test.js b/staff/lucas-orts/ponies/cor/data/insertPost.test.js deleted file mode 100644 index f491d0c3e..000000000 --- a/staff/lucas-orts/ponies/cor/data/insertPost.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import insertPost from './insertPost.js' - -const post = { id: '2gouhl5uylg0', image: 'https://images.wikidexcdn.net/mwuploads/wikidex/thumb/4/4e/latest/20191009134017/Ponyta_de_Galar.png/1200px-Ponyta_de_Galar.png', caption: 'ponyta Galar', author: 'Petazeta', date: "2024-07-10T13:36:38.857Z", likes: [] } - -insertPost(post, error => { - if (error) { - console.error(error) - - return - } - - console.log('Post inserted') -}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/insertUser.js b/staff/lucas-orts/ponies/cor/data/insertUser.js deleted file mode 100644 index 7cd585254..000000000 --- a/staff/lucas-orts/ponies/cor/data/insertUser.js +++ /dev/null @@ -1,39 +0,0 @@ -import fs from 'fs' -import path from 'path'; -import { fileURLToPath } from 'url' - -import validate from '../validate.js' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const insertUser = (user, callback) => { - validate.object(user, 'user') - validate.callback(callback) - - fs.readFile(`${__dirname}/users.json`, 'utf8', (error, json) => { - if (error) { - callback(new Error(error.message)) - - return - } - - const users = json ? JSON.parse(json) : [] - - users.push(user) - - json = JSON.stringify(users) - - fs.writeFile(`${__dirname}/users.json`, json, error => { - if (error) { - callback(new Error(error.message)) - - return - } - - callback(null) - }) - }) -} - -export default insertUser \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/insertUser.test.js b/staff/lucas-orts/ponies/cor/data/insertUser.test.js deleted file mode 100644 index dbf873a22..000000000 --- a/staff/lucas-orts/ponies/cor/data/insertUser.test.js +++ /dev/null @@ -1,19 +0,0 @@ -import insertUser from "./insertUser.js"; - -const user = { - name: 'Caca', - surname: 'Huete', - email: 'caca@huete.com', - username: 'cacahuete', - password: '123123123' -} - -insertUser(user, error => { - if (error) { - console.error(error) - - return - } - - console.log('User inserted') -}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/models.js b/staff/lucas-orts/ponies/cor/data/models.js new file mode 100644 index 000000000..6b3fef098 --- /dev/null +++ b/staff/lucas-orts/ponies/cor/data/models.js @@ -0,0 +1,67 @@ +import { Schema, model, ObjectId, trusted } from 'mongoose' + +const user = new Schema({ + name: { + type: String, + required: true + }, + surname: { + type: String, + required: true + }, + email: { + type: String, + required: true, + unique: true + }, + username: { + type: String, + required: true, + unique: true + }, + password: { + type: String, + required: true + }, + avatar: { + type: String, + required: true, + default: 'https://c8.alamy.com/comp/2EDB67T/cute-horse-avatar-cute-farm-animal-hand-drawn-illustration-isolated-vector-illustration-2EDB67T.jpg' + }, + following: { + type: [String] + }, + favs: { + type: [ObjectId] + } +}) + +const post = new Schema({ + author: { + type: String, + required: true + }, + image: { + type: String, + required: true + }, + caption: { + type: String + }, + date: { + type: Date, + required: true, + default: Date.now + }, + likes: { + type: [String] + } +}) + +const User = model('User', user) +const Post = model('Post', post) + +export { + User, + Post +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/posts.json b/staff/lucas-orts/ponies/cor/data/posts.json deleted file mode 100644 index d65ed1b24..000000000 --- a/staff/lucas-orts/ponies/cor/data/posts.json +++ /dev/null @@ -1 +0,0 @@ -[{"id":"4zcpc42md1w","image":"https//nlknvliver","caption":"Soy Cacatua","author":"Cacatua","date":"2024-07-12T09:23:12.495Z","likes":["Tomasturbao","Petazeta"]},{"id":"bt4j66gl0lc","image":"https://images.wikidexcdn.net/mwuploads/wikidex/thumb/4/4e/latest/20191009134017/Ponyta_de_Galar.png/1200px-Ponyta_de_Galar.png","caption":"ponies","author":"Petazeta","date":"2024-07-24T13:53:01.395Z","likes":[]}] \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/updatePost.js b/staff/lucas-orts/ponies/cor/data/updatePost.js deleted file mode 100644 index 2d880663b..000000000 --- a/staff/lucas-orts/ponies/cor/data/updatePost.js +++ /dev/null @@ -1,48 +0,0 @@ -import fs from 'fs' -import path from 'path'; -import { fileURLToPath } from 'url' - -import validate from '../validate.js' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const updatePost = (condition, post, callback) => { - validate.callback(condition, 'condition') - validate.object(post, 'post') - validate.callback(callback) - - fs.readFile(`${__dirname}/posts.json`, 'utf8', (error, json) => { - if (error) { - callback(new Error(error.message)) - - return - } - - const posts = json ? JSON.parse(json) : [] - - const postIndex = posts.findIndex(condition) - - if (postIndex > -1) { - posts.splice(postIndex, 1, post) - - json = JSON.stringify(posts) - - fs.writeFile(`${__dirname}/posts.json`, json, error => { - if (error) { - callback(new Error(error.message)) - - return - } - - callback(null) - }) - - return - } - - callback(null) - }) -} - -export default updatePost \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/updatePost.test.js b/staff/lucas-orts/ponies/cor/data/updatePost.test.js deleted file mode 100644 index 1bc300ec6..000000000 --- a/staff/lucas-orts/ponies/cor/data/updatePost.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import updatePost from "./updatePost.js" - -const post = { - id: "2gouhl5uylg0", - image: "https://images.wikidexcdn.net/mwuploads/wikidex/thumb/4/4e/latest/20191009134017/Ponyta_de_Galar.png/1200px-Ponyta_de_Galar.png", - caption: "perrito faldero", - author: "postulento", - date: "2024-07-10T13:36:38.857Z", - likes: [] -} - -updatePost(post => post.id === '2gouhl5uylg0', post, error => { - if (error) { - console.error(error) - - return - } - - console.log('Post updated') -}) - diff --git a/staff/lucas-orts/ponies/cor/data/updateUser.js b/staff/lucas-orts/ponies/cor/data/updateUser.js deleted file mode 100644 index 58ad9aa23..000000000 --- a/staff/lucas-orts/ponies/cor/data/updateUser.js +++ /dev/null @@ -1,48 +0,0 @@ -import fs from 'fs' -import path from 'path'; -import { fileURLToPath } from 'url' - -import validate from '../validate.js' - -const __filename = fileURLToPath(import.meta.url) -const __dirname = path.dirname(__filename) - -const updateUser = (condition, user, callback) => { - validate.callback(condition, 'condition') - validate.object(user, 'user') - validate.callback(callback) - - fs.readFile(`${__dirname}/users.json`, 'utf8', (error, json) => { - if (error) { - callback(new Error(error.message)) - - return - } - - const users = json ? JSON.parse(json) : [] - - const index = users.findIndex(condition) - - if (index > -1) { - users.splice(index, 1, user) - - json = JSON.stringify(users) - - fs.writeFile(`${__dirname}/users.json`, json, error => { - if (error) { - callback(new Error(error.message)) - - return - } - - callback(null) - }) - - return - } - - callback(null) - }) -} - -export default updateUser \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/updateUser.test.js b/staff/lucas-orts/ponies/cor/data/updateUser.test.js deleted file mode 100644 index 72ac1db4f..000000000 --- a/staff/lucas-orts/ponies/cor/data/updateUser.test.js +++ /dev/null @@ -1,19 +0,0 @@ -import updateUser from "./updateUser.js" - -const user = { - name: "Peta", - surname: "Zeta", - email: "peta@zeta.com", - username: "petazeta", - password: "123123123" -} - -updateUser(user => user.surname === 'Huete', user, error => { - if (error) { - console.error(error) - - return - } - - console.log('User updated') -}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/data/users.json b/staff/lucas-orts/ponies/cor/data/users.json deleted file mode 100644 index 0d0d4bec1..000000000 --- a/staff/lucas-orts/ponies/cor/data/users.json +++ /dev/null @@ -1 +0,0 @@ -[{"name":"Caca","surname":"Tua","email":"caca@tua.com","username":"Cacatua","password":"123123123","favs":[],"following":[],"avatar":"https://c8.alamy.com/comp/2EDB67T/cute-horse-avatar-cute-farm-animal-hand-drawn-illustration-isolated-vector-illustration-2EDB67T.jpg"},{"name":"Pon","surname":"Un","email":"pon@un.com","username":"Ponun","password":"123123123","favs":[],"following":[],"avatar":"https://c8.alamy.com/comp/2EDB67T/cute-horse-avatar-cute-farm-animal-hand-drawn-illustration-isolated-vector-illustration-2EDB67T.jpg"},{"name":"Ta","surname":"Guapo","email":"ta@guapo.com","username":"Taguapo","password":"123123123","favs":["2gouhl5uylg"],"following":["Cacatua"],"avatar":"https://c8.alamy.com/comp/2EDB67T/cute-horse-avatar-cute-farm-animal-hand-drawn-illustration-isolated-vector-illustration-2EDB67T.jpg"},{"name":"Peta","surname":"Zeta","email":"peta@zeta.com","username":"Petazeta","password":"123123123","favs":["4zcpc42md1w"],"following":[],"avatar":"https://c8.alamy.com/comp/2EDB67T/cute-horse-avatar-cute-farm-animal-hand-drawn-illustration-isolated-vector-illustration-2EDB67T.jpg"},{"name":"Jhony","surname":"Melavo","email":"jhony@melavo.com","username":"Jhonymelavo","password":"123123123","favs":["2gouhl5uylg"],"following":["Cacatua"],"avatar":"https://c8.alamy.com/comp/2EDB67T/cute-horse-avatar-cute-farm-animal-hand-drawn-illustration-isolated-vector-illustration-2EDB67T.jpg"},{"name":"Tomas","surname":"Turbao","email":"tomas@turbao.com","username":"Tomasturbao","password":"123123123","favs":["4zcpc42md1w"],"following":["Petazeta"],"avatar":"https://c8.alamy.com/comp/2EDB67T/cute-horse-avatar-cute-farm-animal-hand-drawn-illustration-isolated-vector-illustration-2EDB67T.jpg"},{"name":"Ester","surname":"Colero","email":"ester@colero.com","username":"estercolero","password":"87654321","favs":[],"following":[],"avatar":"https://c8.alamy.com/comp/2EDB67T/cute-horse-avatar-cute-farm-animal-hand-drawn-illustration-isolated-vector-illustration-2EDB67T.jpg"}] \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/index.js b/staff/lucas-orts/ponies/cor/index.js index e69de29bb..d56396d4a 100644 --- a/staff/lucas-orts/ponies/cor/index.js +++ b/staff/lucas-orts/ponies/cor/index.js @@ -0,0 +1,9 @@ +import mongoose from 'mongoose' +import data from './data/index.js' +import logic from './logic/index.js' + +export { + mongoose, + data, + logic +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/authenticateUser.js b/staff/lucas-orts/ponies/cor/logic/authenticateUser.js index 83b38d0f5..1ee4bab4a 100644 --- a/staff/lucas-orts/ponies/cor/logic/authenticateUser.js +++ b/staff/lucas-orts/ponies/cor/logic/authenticateUser.js @@ -1,32 +1,35 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import bcrypt from 'bcryptjs' -const authenticateUser = (username, password, callback) => { - validate.username(username) - validate.password(password) - validate.callback(callback) +import { User } from '../data/models.js' - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) +import { validate, errors } from 'com' - return - } +const { SystemError, NotFoundError, CredentialsError } = errors - if (user === null) { - callback(new Error('user not found')) +export default (username, password, callback) => { + validate.username(username) + validate.password(password) + validate.callback(callback) - return - } + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) - if (user.password !== password) { - callback(new Error('wrong password')) + return + } - return - } + bcrypt.compare(password, user.password) + .then(match => { + if (!match) { + callback(new CredentialsError('wrong password')) - callback(null) - }) -} + return + } -export default authenticateUser \ No newline at end of file + callback(null) + }) + .catch(error => callback(new SystemError(error.message))) + }) + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/authenticateUser.spec.js b/staff/lucas-orts/ponies/cor/logic/authenticateUser.spec.js new file mode 100644 index 000000000..30d900249 --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/authenticateUser.spec.js @@ -0,0 +1,148 @@ +import 'dotenv/config' +import mongoose from 'mongoose' +import { expect } from 'chai' + +import { errors } from 'com' + +import authenticateUser from './authenticateUser.js' +import { User } from '../data/models.js' + +const { ValidationError, CredentialsError, NotFoundError } = errors + +describe('authenticateUser', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + it('succeeds on authenticate user', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'Tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + authenticateUser('Tomasturbao', '123123123', error => { + expect(error).to.equal(null) + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on non-existing user', done => { + authenticateUser('Tomasturbao', '123123123', error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + + it('fails on passwords do not match', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'Tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + authenticateUser(user.username, '123123124', error => { + expect(error).to.be.instanceOf(CredentialsError) + expect(error.message).to.equal('wrong password') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on non-string username', () => { + let error + + try { + authenticateUser(123, '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + authenticateUser('', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string password', () => { + let error + + try { + authenticateUser('Tomasturbao', 123123123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('password is not a string') + } + }) + + it('fails on password too short', () => { + let error + + try { + authenticateUser('Tomasturbao', '123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('password length is lower than 8 characters') + } + }) + + it('fails on password with spaces', () => { + let error + + try { + authenticateUser('Tomasturbao', '123123 123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('password has empty spaces') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + authenticateUser('Tomasturbao', '123123123', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + afterEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/authenticateUser.test.js b/staff/lucas-orts/ponies/cor/logic/authenticateUser.test.js index 765db784c..92a1e5c5d 100644 --- a/staff/lucas-orts/ponies/cor/logic/authenticateUser.test.js +++ b/staff/lucas-orts/ponies/cor/logic/authenticateUser.test.js @@ -1,11 +1,21 @@ +import 'dotenv/config' import authenticateUser from './authenticateUser.js' -authenticateUser("Cacatua", "123123123", error => { - if (error) { - console.error(error) +import mongoose from "mongoose" - return - } +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') - console.log('user authenticated') -}) \ No newline at end of file + authenticateUser("Cacatua", "123123123", error => { + if (error) { + console.error(error) + + return + } + console.log('user authenticated') + + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/createPost.js b/staff/lucas-orts/ponies/cor/logic/createPost.js index 8747ef200..28c5e03c3 100644 --- a/staff/lucas-orts/ponies/cor/logic/createPost.js +++ b/staff/lucas-orts/ponies/cor/logic/createPost.js @@ -1,46 +1,30 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User, Post } from '../data/models.js' -import generateId from '../util/generateId.js' +import { validate, errors } from "com" -const createPost = (username, image, caption, callback) => { +const { NotFoundError, SystemError } = errors + +export default (username, image, caption, callback) => { validate.username(username) validate.image(image) validate.string(caption, 'caption') validate.callback(callback) - data.findUser(user => user.username == username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (user === null) { - callback(new Error('user not found')) - - return - } - - const post = { - id: generateId(), - image, - caption, - author: username, - date: new Date().toISOString(), - likes: [] - } - - data.insertPost(post, error => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - callback(null) + Post.create({ + image, + caption, + author: username + }) + .then(() => callback(null)) + .catch(error => callback(new SystemError(error.message))) }) - }) -} - -export default createPost \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/createPost.spec.js b/staff/lucas-orts/ponies/cor/logic/createPost.spec.js new file mode 100644 index 000000000..339e96d54 --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/createPost.spec.js @@ -0,0 +1,148 @@ +import 'dotenv/config' +import mongoose from 'mongoose' +import { expect } from 'chai' + +import { errors } from 'com' + +import createPost from './createPost.js' +import { User, Post } from '../data/models.js' + +const { NotFoundError, ValidationError } = errors + +describe('createPost', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + it('succeeds on new post', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + createPost(user.username, 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', 'hola', error => { + if (error) { + done(error) + + return + } + + Post.findOne({ author: user.username }) + .then(post => { + expect(post.author).to.equal('Tomasturbao') + expect(post.image).to.equal('https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g') + expect(post.caption).to.equal('hola') + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + + it('fails on non-existing user', done => { + createPost('Tomasturbao', 'http://text', 'hola', error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + + it('fails on non-string username', () => { + let error + + try { + createPost(123, 'http://text', 'hola', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + createPost('', 'http://text', 'hola', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string image', () => { + let error + + try { + createPost('Tomasturbao', 123, 'hola', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('image is not a string') + } + }) + + it('fails on invalid image', () => { + let error + + try { + createPost('Tomasturbao', 'htpp://text', 'hola', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid image') + } + }) + + it('fails on non-string caption', () => { + let error + + try { + createPost('Tomasturbao', 'http://', 123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('caption is not a string') + } + }) + + + it('fails on non-function callback', () => { + let error + + try { + createPost('Tomasturbao', 'http://text', 'hola', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + afterEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/createPost.test.js b/staff/lucas-orts/ponies/cor/logic/createPost.test.js index d15458f8f..22378a7cc 100644 --- a/staff/lucas-orts/ponies/cor/logic/createPost.test.js +++ b/staff/lucas-orts/ponies/cor/logic/createPost.test.js @@ -1,11 +1,22 @@ +import 'dotenv/config' import createPost from './createPost.js' -createPost("Cacatua", "https//nlknvliver", "Soy yo", error => { - if (error) { - console.error(error) +import mongoose from 'mongoose' - return - } +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') - console.log('post created') -}) \ No newline at end of file + createPost('Cacatua', 'https://media.giphy.com/media/l0IybQ6l8nfKjxQv6/giphy.gif?cid=82a1493bal4s8pqx9wahx4spwzaqj7fohoyfahtrmh0yvry4&ep=v1_gifs_trending&rid=giphy.gif&ct=g', 'caca tuya', error => { + if (error) { + console.error(error) + + return + } + + console.log('post created') + + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) diff --git a/staff/lucas-orts/ponies/cor/logic/deletePost.js b/staff/lucas-orts/ponies/cor/logic/deletePost.js index c875edc30..46233ef9f 100644 --- a/staff/lucas-orts/ponies/cor/logic/deletePost.js +++ b/staff/lucas-orts/ponies/cor/logic/deletePost.js @@ -1,48 +1,41 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User, Post } from '../data/models.js' -const deletePost = (username, postId, callback) => { +import { validate, errors } from 'com' + +const { NotFoundError, OwnershipError, SystemError } = errors + +export default (username, postId, callback) => { validate.username(username) validate.postId(postId) validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (user === null) { - callback(new Error('user not found')) - - return - } - - data.findPost(post => post.id === postId, (error, post) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - if (post === null) { - callback(new Error('post not found')) + Post.findById(postId).lean() + .then(post => { + if (!post) { + callback(new NotFoundError('post not found')) - return - } + return + } - data.deletePost(post => post.id === postId, error => { - if (error) { - callback(new Error(error.message)) + if (post.author !== username) { + callback(new OwnershipError('post does not belong to user')) - return - } + return + } - callback(null) - }) + Post.deleteOne({ _id: postId }) + .then(() => callback(null)) + .catch(error => callback(new SystemError(error.message))) + }) + .catch(error => callback(new SystemError(error.message))) }) - }) -} - -export default deletePost \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/deletePost.spec.js b/staff/lucas-orts/ponies/cor/logic/deletePost.spec.js new file mode 100644 index 000000000..88872bb83 --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/deletePost.spec.js @@ -0,0 +1,189 @@ +import 'dotenv/config' +import deletePost from './deletePost.js' +import mongoose, { Types } from 'mongoose' + +import { errors } from 'com' + +const { ObjectId } = Types + +import { expect } from 'chai' +import { User, Post } from '../data/models.js' + +const { ValidationError, NotFoundError, OwnershipError } = errors + +describe('deletePost', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on delete post', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post1 => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post2 => { + deletePost(user.username, post1.id, error => { + if (error) { + console.error(error) + + return + } + + Post.find({}).lean() + .then(posts => { + expect(posts[0].author).to.equal(post2.author) + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('fails on non-existing user', done => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post => { + deletePost('Tomasturbao', post.id, error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on existing user but non-existing post', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(() => { + deletePost('Tomasturbao', new ObjectId().toString(), error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('post not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on existing user and post but post does not belog to user', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + User.create({ name: 'Ester', surname: 'Colero', email: 'ester@colero.com', username: 'Estercolero', password: '123123123' }) + .then(() => { + Post.create({ author: 'Estercolero', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post => { + deletePost(user.username, post.id, error => { + expect(error).to.be.instanceOf(OwnershipError) + expect(error.message).to.equal('post does not belong to user') + + done() + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('fails on non-string username', () => { + let error + + try { + deletePost(123, new ObjectId().toString(), error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + deletePost('', new ObjectId().toString(), error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string postId', () => { + let error + + try { + deletePost('Tomasturbao', 123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('postId is not a string') + } + }) + + it('fails on invalid postId', () => { + let error + + try { + deletePost('Tomasturbao', '', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid postId') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + deletePost('Tomasturbao', new ObjectId().toString(), 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + + afterEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/deletePost.test.js b/staff/lucas-orts/ponies/cor/logic/deletePost.test.js index d9b9b2ba2..db4cea114 100644 --- a/staff/lucas-orts/ponies/cor/logic/deletePost.test.js +++ b/staff/lucas-orts/ponies/cor/logic/deletePost.test.js @@ -1,11 +1,22 @@ +import 'dotenv/config' import deletePost from './deletePost.js' -deletePost("Petazeta", "2gouhl5uylg0", error => { - if (error) { - console.error(error) +import mongoose from 'mongoose' +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') - return - } + deletePost('Cacatua', '66a3907d93b0b4f3739d6ece', error => { + if (error) { + console.error(error) - console.log('post deleted') -}) \ No newline at end of file + return + } + + console.log('post deleted') + + mongoose.disconnect() + }) + + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.js b/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.js index ca770ea05..87be31815 100644 --- a/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.js +++ b/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.js @@ -1,60 +1,61 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User, Post } from '../data/models.js' +import { validate, errors } from 'com' -const getAllFavPosts = (username, callback) => { +const { NotFoundError, SystemError } = errors + +export default (username, callback) => { validate.username(username) validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (user === null) { - callback(new Error('user not found')) - - return - } - - data.findPosts(post => user.favs.includes(post.id), (error, posts) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - if (posts.length) { - let count = 0 + Post.find({ _id: { $in: user.favs } }).sort({ date: -1 }).lean() + .then(posts => { + if (posts.length) { + let count = 0 + + posts.forEach(post => { + post.fav = user.favs.some(postObjectId => postObjectId.toString() === post._id.toString()) + post.like = post.likes.includes(username) - posts.forEach(post => { - post.fav = user.favs.includes(post.id) - post.like = post.likes.includes(username) + User.findOne({ username: post.author }).lean() + .then(author => { - data.findUser(user => user.username === post.author, (error, author) => { - if (error) { - callback(new Error(error.message)) + if (!author) { + callback(new NotFoundError('author not found')) - return - } + return + } + post.author = { + username: author.username, + avatar: author.avatar, + following: user.following.includes(author.username) + } - post.author = { - username: author.username, - avatar: author.avatar, - following: user.following.includes(author.username) - } + count++ + if (count === posts.length) { + posts.forEach(post => { + post.id = post._id.toString() - count++ + delete post._id + }) - if (count === posts.length) - callback(null, posts.reverse()) - }) + callback(null, posts) + } + + }) + .catch(error => callback(new SystemError(error.message))) + }) + } else callback(null, []) }) - } else callback(null, []) + .catch(error => callback(new SystemError(error.message))) }) - }) -} - -export default getAllFavPosts \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.spec.js b/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.spec.js new file mode 100644 index 000000000..4791659e1 --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.spec.js @@ -0,0 +1,121 @@ +import 'dotenv/config' +import getAllFavPosts from './getAllFavPosts.js' +import mongoose from 'mongoose' + +import { errors } from 'com' + +import { expect } from 'chai' +import { User, Post } from '../data/models.js' + +const { ValidationError, NotFoundError } = errors + +describe('getAllFavPosts', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on existing user listing all fav posts', done => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123', favs: [post.id] }) + .then(user => { + getAllFavPosts(user.username, (error, posts) => { + if (error) { + console.error(error) + + return + } + + User.findOne({ username: 'Tomasturbao' }) + .then(user => { + expect(user.favs).to.include(post.id) + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('fails on non-existing user', done => { + getAllFavPosts('Estercolero', (error, posts) => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + + + + it('fails on non-string username', () => { + let error + + try { + getAllFavPosts(123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + getAllFavPosts('', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + getAllFavPosts('Tomasturbao', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + + afterEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.test.js b/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.test.js index 71ab2ab59..5e29cfd9f 100644 --- a/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.test.js +++ b/staff/lucas-orts/ponies/cor/logic/getAllFavPosts.test.js @@ -1,11 +1,21 @@ +import 'dotenv/config' import getAllFavPosts from './getAllFavPosts.js' -getAllFavPosts("Taguapo", (error, posts) => { - if (error) { - console.error(error) +import mongoose from 'mongoose' +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') - return - } + getAllFavPosts("Cacatua", (error, posts) => { + if (error) { + console.error(error) - console.log(posts) -}) \ No newline at end of file + return + } + + console.log(posts) + + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.js b/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.js index a2d7b08ff..5089df796 100644 --- a/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.js +++ b/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.js @@ -1,59 +1,61 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User, Post } from '../data/models.js' -const getAllFollowingUserPosts = (username, callback) => { - validate.username(username) - validate.callback(callback) - - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) +import { validate, errors } from 'com' - return - } +const { NotFoundError, SystemError } = errors - if (user === null) { - callback(new Error('user not found')) - - return - } +export default (username, callback) => { + validate.username(username) + validate.callback(callback) - data.findPosts(post => user.following.includes(post.author), (error, posts) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - if (posts.length) { - let count = 0 + Post.find({ author: { $in: user.following } }).sort({ date: -1 }).lean() + .then(posts => { + if (posts.length) { + let count = 0 + + posts.forEach(post => { + post.fav = user.favs.some(postObjectId => postObjectId.toString() === post._id.toString()) + post.like = post.likes.includes(username) - posts.forEach(post => { - post.fav = user.favs.includes(post.id) - post.like = post.likes.includes(username) + User.findOne({ username: post.author }).lean() + .then(author => { + if (!author) { + callback(new NotFoundError('author not found')) - data.findUser(user => user.username === post.author, (error, author) => { - if (error) { - callback(new Error(error.message)) + return + } + post.author = { + username: author.username, + avatar: author.avatar, + following: user.following.includes(author.username) + } - return - } + count++ - post.author = { - username: author.username, - avatar: author.avatar, - following: user.following.includes(author.username) - } + if (count === posts.length) { + posts.forEach(post => { + post.id = post._id.toString() - count++ + delete post._id + }) - if (count === posts.length) - callback(null, posts.reverse()) - }) + callback(null, posts) + } + + }) + .catch(error => callback(new SystemError(error.message))) + }) + } else callback(null, []) }) - } else callback(null, []) + .catch(error => callback(new SystemError(error.message))) }) - }) + .catch(error => callback(new SystemError(error.message))) } - -export default getAllFollowingUserPosts \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.spec.js b/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.spec.js new file mode 100644 index 000000000..596683ace --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.spec.js @@ -0,0 +1,121 @@ +import 'dotenv/config' +import getAllFollowingUserPosts from './getAllFollowingUserPosts.js' +import mongoose, { Types } from 'mongoose' + +import { errors } from 'com' + +import { expect } from 'chai' +import { User, Post } from '../data/models.js' + +const { NotFoundError, ValidationError } = errors + +describe('getAllFollowingUserPosts', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on existing user listing all following posts', done => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123', following: ['Tomasturbao'] }) + .then(user => { + getAllFollowingUserPosts(user.username, (error, posts) => { + if (error) { + console.error(error) + + return + } + + User.findOne({ username: 'Tomasturbao' }).lean() + .then(user => { + expect(user.following).to.include(post.author) + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('fails on non-existing user', done => { + getAllFollowingUserPosts('Estercolero', (error, posts) => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + + + + it('fails on non-string username', () => { + let error + + try { + getAllFollowingUserPosts(123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + getAllFollowingUserPosts('', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + getAllFollowingUserPosts('Tomasturbao', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + + afterEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.test.js b/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.test.js index c0fc8c391..b64b6cffa 100644 --- a/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.test.js +++ b/staff/lucas-orts/ponies/cor/logic/getAllFollowingUserPosts.test.js @@ -1,11 +1,22 @@ +import 'dotenv/config' import getAllFollowingUserPosts from './getAllFollowingUserPosts.js' -getAllFollowingUserPosts("Taguapo", (error, posts) => { - if (error) { - console.error(error) +import mongoose from 'mongoose' - return - } +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') - console.log(posts) -}) \ No newline at end of file + getAllFollowingUserPosts('Petazeta', (error, posts) => { + if (error) { + console.error(error) + + return + } + + console.log(posts) + mongoose.disconnect() + + }) + }) + .catch(error => console.error(error)) diff --git a/staff/lucas-orts/ponies/cor/logic/getAllPosts.js b/staff/lucas-orts/ponies/cor/logic/getAllPosts.js index fdc4cf646..dbea92594 100644 --- a/staff/lucas-orts/ponies/cor/logic/getAllPosts.js +++ b/staff/lucas-orts/ponies/cor/logic/getAllPosts.js @@ -1,58 +1,59 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User, Post } from '../data/models.js' +import { validate, errors } from 'com' -const getAllPosts = (username, callback) => { +const { NotFoundError, SystemError } = errors + +export default (username, callback) => { validate.username(username) validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (user === null) { - callback(new Error('user not found')) - - return - } - - data.findPosts(() => true, (error, posts) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - if (posts.length) { - let count = 0 - - posts.forEach(post => { - post.fav = user.favs.includes(post.id) - post.like = post.likes.includes(username) - - data.findUser(user => user.username === post.author, (error, author) => { - if (error) { - callback(new Error(error.message)) - - return - } - - post.author = { - username: author.username, - avatar: author.avatar, - following: user.following.includes(author.username) - } - - count++ - - if (count === posts.length) - callback(null, posts.reverse()) - }) + Post.find().sort({ date: -1 }).lean() + .then(posts => { + if (posts.length) { + let count = 0 + + posts.forEach(post => { + post.fav = user.favs.some(postObjectId => postObjectId.toString() === post._id.toString()) + post.like = post.likes.includes(username) + + User.findOne({ username: post.author }).lean() + .then(author => { + if (!author) { + callback(new NotFoundError('author not found')) + + return + } + post.author = { + username: author.username, + avatar: author.avatar, + following: user.following.includes(author.username) + } + + count++ + + if (count === posts.length) { + posts.forEach(post => { + post.id = post._id.toString() + + delete post._id + }) + + callback(null, posts) + } + }) + .catch(error => callback(new SystemError(error.message))) + }) + } else callback(null, []) }) - } else callback(null, []) + .catch(error => callback(new SystemError(error.message))) }) - }) + .catch(error => callback(new SystemError(error.message))) } -export default getAllPosts \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getAllPosts.spec.js b/staff/lucas-orts/ponies/cor/logic/getAllPosts.spec.js new file mode 100644 index 000000000..95840556d --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/getAllPosts.spec.js @@ -0,0 +1,128 @@ +import 'dotenv/config' +import getAllPosts from './getAllPosts.js' +import mongoose, { Types } from 'mongoose' + +import { errors } from 'com' + +import { expect } from 'chai' +import { User, Post } from '../data/models.js' + +const { ValidationError, NotFoundError } = errors + +describe('getAllPosts', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on existing user listing all posts', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123', following: ['Tomasturbao'] }) + .then(user => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post1 => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post2 => { + getAllPosts(user.username, (error, posts) => { + if (error) { + console.error(error) + + return + } + + + Post.find({}).lean() + .then(posts => { + expect(posts[0].author).to.equal(post1.author) + expect(posts[1].author).to.equal(post2.author) + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(erro => done(error)) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + + it('fails on non-existing user', done => { + getAllPosts('Estercolero', (error, posts) => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + + + + it('fails on non-string username', () => { + let error + + try { + getAllPosts(123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + getAllPosts('', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + getAllPosts('Tomasturbao', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + + afterEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getAllPosts.test.js b/staff/lucas-orts/ponies/cor/logic/getAllPosts.test.js index 57ec0257c..533e1dfd5 100644 --- a/staff/lucas-orts/ponies/cor/logic/getAllPosts.test.js +++ b/staff/lucas-orts/ponies/cor/logic/getAllPosts.test.js @@ -1,11 +1,22 @@ +import 'dotenv/config' import getAllPosts from './getAllPosts.js' -getAllPosts("Cacatua", (error, posts) => { - if (error) { - console.error(error) +import mongoose from 'mongoose' - return - } +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') - console.log(posts) -}) \ No newline at end of file + getAllPosts("Cacatua", (error, posts) => { + if (error) { + console.error(error) + + return + } + + console.log(posts) + + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getUser.js b/staff/lucas-orts/ponies/cor/logic/getUser.js index a410fe60c..5ca6a234e 100644 --- a/staff/lucas-orts/ponies/cor/logic/getUser.js +++ b/staff/lucas-orts/ponies/cor/logic/getUser.js @@ -1,43 +1,34 @@ -import data from '../data/index.js' +import { User } from '../data/models.js' -import validate from '../validate.js' +import { validate, errors } from 'com' -const getUser = (username, targetUsername, callback) => { +const { NotFoundError, SystemError } = errors + +export default (username, targetUsername, callback) => { validate.username(username) - validate.targetUsername(targetUsername) + validate.username(targetUsername, 'targetUsername') validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (!user) { - callback(new Error('user not found')) - - return - } - - data.findUser(user => user.username === targetUsername, (error, targetUser) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - if (!targetUser) { - callback(new Error('target user not found')) + User.findOne({ username: targetUsername }).lean() + .then(targetUser => { + if (!targetUser) { + callback(new NotFoundError('target user not found')) - return - } + return + } + delete targetUser.password - delete targetUser.password - - callback(null, targetUser) + callback(null, targetUser) + }) + .catch(error => callback(new SystemError(error.message))) }) - }) -} - -export default getUser \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getUser.spec.js b/staff/lucas-orts/ponies/cor/logic/getUser.spec.js new file mode 100644 index 000000000..0744ed822 --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/getUser.spec.js @@ -0,0 +1,128 @@ +import 'dotenv/config' +import mongoose from 'mongoose' +import { expect } from 'chai' + +import { errors } from 'com' + +import getUser from './getUser.js' +import { User } from '../data/models.js' + +const { NotFoundError, ValidationError } = errors + +describe('getUser', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + it('succeeds on existing user', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + getUser(user.username, user.username, (error, targetUser) => { + expect(targetUser.name).to.equal('Tomas') + expect(targetUser.surname).to.equal('Turbao') + expect(targetUser.email).to.equal('tomas@turbao.com') + expect(targetUser.username).to.equal('Tomasturbao') + + done() + }) + }) + .catch(error => done(error)) + }) + + + it('fails on non-existing user', done => { + getUser('Tomasturbao', 'Tomasturbao', error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + + it('fails on non-string username', () => { + let error + + try { + getUser(123, 'Tomasturbao', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + getUser('', 'Tomasturbao', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string targetUsername', () => { + let error + + try { + getUser('Tomasturbao', 123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('targetUsername is not a string') + } + }) + + it('fails on invalid tagretUsername', () => { + let error + + try { + getUser('Tomasturbao', '', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid targetUsername') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + getUser('Tomasturbao', 'Tomasturbao', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + afterEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) + +}) + diff --git a/staff/lucas-orts/ponies/cor/logic/getUser.test.js b/staff/lucas-orts/ponies/cor/logic/getUser.test.js index 003701228..02ba3765d 100644 --- a/staff/lucas-orts/ponies/cor/logic/getUser.test.js +++ b/staff/lucas-orts/ponies/cor/logic/getUser.test.js @@ -1,11 +1,23 @@ +import 'dotenv/config' + import getUser from './getUser.js' -getUser('Cacatua', 'Cacatua', (error, user) => { - if (error) { - console.error(error.message) +import mongoose from 'mongoose' + +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') + + getUser('Cacatua', 'Cacatua', (error, user) => { + if (error) { + console.error(error.message) + + return + } - return - } + console.log(user) - console.log(user) -}) \ No newline at end of file + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getUserName.js b/staff/lucas-orts/ponies/cor/logic/getUserName.js index cfaaf973c..ed0eb1dff 100644 --- a/staff/lucas-orts/ponies/cor/logic/getUserName.js +++ b/staff/lucas-orts/ponies/cor/logic/getUserName.js @@ -1,39 +1,32 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User } from '../data/models.js' +import { validate, errors } from 'com' -const getUserName = (username, targetUsername, callback) => { +const { NotFoundError, SystemError } = errors + +export default (username, targetUsername, callback) => { validate.username(username) - validate.targetUsername(targetUsername) + validate.username(targetUsername, 'targetUsername') validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (!user) { - callback(new Error('user not found')) - - return - } - - data.findUser(user => user.username === targetUsername, (error, targetUser) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - if (!targetUser) { - callback(new Error('target user not found')) + User.findOne({ username: targetUsername }).lean() + .then(targetUser => { + if (!targetUser) { + callback(new NotFoundError('target user not found')) - return - } + return + } - callback(null, targetUser.name) + callback(null, targetUser.name) + }) + .catch(error => callback(new SystemError(error.message))) }) - }) -} -export default getUserName \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getUserName.spec.js b/staff/lucas-orts/ponies/cor/logic/getUserName.spec.js new file mode 100644 index 000000000..ab23833dd --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/getUserName.spec.js @@ -0,0 +1,142 @@ +import 'dotenv/config' +import mongoose from 'mongoose' +import { expect } from 'chai' + +import { errors } from 'com' + +import getUserName from './getUserName.js' +import { User } from '../data/models.js' + +const { ValidationError, NotFoundError } = errors + +describe('getUserName', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + // it('succeeds on existing user and target user', done => { + // User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + // .then(user => { + // User.create({ name: 'Ester', surname: 'Colero', email: 'ester@colero.com', username: 'Estercolero', password: '123123123' }) + // .then(user1 => { + // getUserName(user.username, user1.username, (error, user1) => { + // expect(user1.name).to.equal('Ester') + + // done() + // }) + // }) + // .catch(error => done(error)) + // }) + // .catch(error => done(error)) + // }) + + + it('fails on non-existing user', done => { + getUserName('Tomasturbao', 'Tomasturbao', error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + + + it('fails on non-existing targetUser', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + getUserName(user.username, 'EsterColero', error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('target user not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on non-string username', () => { + let error + + try { + getUserName(123, 'Tomasturbao', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + getUserName('', 'Tomasturbao', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string targetUsername', () => { + let error + + try { + getUserName('Tomasturbao', 123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('targetUsername is not a string') + } + }) + + it('fails on invalid tagretUsername', () => { + let error + + try { + getUserName('Tomasturbao', '', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid targetUsername') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + getUserName('Tomasturbao', 'Tomasturbao', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + afterEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) + +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/getUserName.test.js b/staff/lucas-orts/ponies/cor/logic/getUserName.test.js index 46fb0300f..6538a05c3 100644 --- a/staff/lucas-orts/ponies/cor/logic/getUserName.test.js +++ b/staff/lucas-orts/ponies/cor/logic/getUserName.test.js @@ -1,13 +1,23 @@ +import 'dotenv/config' import getUserName from './getUserName.js' -const name = getUserName('Cacatua', 'Cacatua', (error, name) => { - if (error) { - console.error(error) +import mongoose, { mongo } from 'mongoose' - return - } +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') - console.log(name) -}) + getUserName('Cacatua', 'Cacatua', (error, name) => { + if (error) { + console.error(error) + + return + } + + console.log(name) + + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) -console.log(name) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/registerUser.js b/staff/lucas-orts/ponies/cor/logic/registerUser.js index 5e837de3e..4d887b61b 100644 --- a/staff/lucas-orts/ponies/cor/logic/registerUser.js +++ b/staff/lucas-orts/ponies/cor/logic/registerUser.js @@ -1,7 +1,11 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import bcrypt from 'bcryptjs' -const registerUser = (name, surname, email, username, password, passwordRepeat, callback) => { +import { User } from '../data/models.js' +import { validate, errors } from 'com' + +const { DuplicityError, SystemError, ValidationError } = errors + +export default (name, surname, email, username, password, passwordRepeat, callback) => { validate.name(name) validate.name(surname, 'surname') validate.email(email) @@ -9,60 +13,39 @@ const registerUser = (name, surname, email, username, password, passwordRepeat, validate.password(password) validate.callback(callback) - if (password !== passwordRepeat) { - callback(new Error('passwords do not match')) - - return - } - - data.findUser(user => user.email === email, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (user !== null) { - callback(new Error('email already exists')) - - return - } - - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) + if (password !== passwordRepeat) + throw new ValidationError('passwords do not match') - return - } - if (user !== null) { - callback(new Error('username already exists')) + User.findOne({ email }).lean() + .then(user => { + if (user) { + callback(new DuplicityError('email already exists')) return } - - const newUser = { - name, - surname, - email, - username, - password, - favs: [], - following: [], - avatar: 'https://svgsilh.com/svg/145535-707070.svg' - } - - data.insertUser(newUser, error => { - if (error) { - callback(new Error(error.message)) - - return - } - - callback(null) - }) + User.findOne({ username }).lean() + .then(user => { + if (user) { + callback(new DuplicityError('user already exists')) + + return + } + bcrypt.hash(password, 8) + .then(hash => { + User.create({ + name, + surname, + email, + username, + password: hash, + }) + .then(() => callback(null)) + .catch(error => callback(new SystemError(error.message))) + }) + .catch(error => callback(new SystemError(error.message))) + }) + .catch(error => callback(new SystemError(error.message))) }) - }) -} - -export default registerUser \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/registerUser.spec.js b/staff/lucas-orts/ponies/cor/logic/registerUser.spec.js new file mode 100644 index 000000000..771d13130 --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/registerUser.spec.js @@ -0,0 +1,254 @@ +import 'dotenv/config' +import mongoose from 'mongoose' +import { expect } from 'chai' + +import { errors } from 'com' + +import registerUser from './registerUser.js' +import { User } from '../data/models.js' + +const { ValidationError, DuplicityError } = errors + +describe('registerUser', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + it('succeeds on new user', done => { + registerUser('Tomas', 'Turbao', 'tomas@turbao.com', 'Tomasturbao', '123123123', '123123123', error => { + if (error) { + done(error) + + return + } + + User.findOne({ username: 'Tomasturbao' }).lean() + .then(user => { + expect(user.name).to.equal('Tomas') + expect(user.surname).to.equal('Turbao') + expect(user.email).to.equal('tomas@turbao.com') + expect(user.password).to.equal('123123123') + + done() + }) + .catch(error => done(error)) + }) + }) + + it('fails on existing user with same email', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(() => { + registerUser('Tomas', 'Turbao', 'tomas@turbao.com', 'Tomasturbao', '123123123', '123123123', error => { + expect(error).to.be.instanceOf(DuplicityError) + expect(error.message).to.equal('email already exists') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on existing user with same username', done => { + + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + + .then(() => { + registerUser('Tomas', 'Turbao', 'tomas@turbao2.com', 'Tomasturbao', '123123123', '123123123', error => { + expect(error).to.be.instanceOf(DuplicityError) + expect(error.message).to.equal('user already exists') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on non-string name', () => { + let error + + try { + registerUser(123, 'Turbao', 'tomas@turbao.com', 'Tomasturbao', '123123123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('name is not a string') + } + }) + + it('fails on invalid name', () => { + let error + + try { + registerUser('', 'Turbao', 'tomas@turbao.com', 'Tomasturbao', '123123123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid name') + } + }) + + it('fails on non-string surame', () => { + let error + + try { + registerUser('Tomas', 123, 'tomas@turbao.com', 'Tomasturbao', '123123123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('surname is not a string') + } + }) + + it('fails on invalid surname', () => { + let error + + try { + registerUser('Tomas', '', 'tomas@turbao.com', 'Tomasturbao', '123123123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid surname') + } + }) + + it('fails on non-string email', () => { + let error + + try { + registerUser('Tomas', 'Turbao', 123, 'Tomasturbao', '123123123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('email is not a string') + } + }) + + it('fails on invalid email', () => { + let error + + try { + registerUser('Tomas', 'Turbao', '', 'Tomasturbao', '123123123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid email') + } + }) + + it('fails on non-string username', () => { + let error + + try { + registerUser('Tomas', 'Turbao', 'tomas@turbao.com', 123, '123123123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + registerUser('Tomas', 'Turbao', 'tomas@turbao.com', '', '123123123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string password', () => { + let error + + try { + registerUser('Tomas', 'Turbao', 'tomas@turbao.com', 'Tomasturbao', 123123123, '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('password is not a string') + } + }) + + it('fails on password too short', () => { + let error + + try { + registerUser('Tomas', 'Turbao', 'tomas@turbao.com', 'Tomasturbao', '123123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('password length is lower than 8 characters') + } + }) + + it('fails on password with spaces', () => { + let error + + try { + registerUser('Tomas', 'Turbao', 'tomas@turbao.com', 'Tomasturbao', '123123 123', '123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('password has empty spaces') + } + }) + + it('fails on non-matching passwords', () => { + let error + + try { + registerUser('Tomas', 'Turbao', 'tomas@turbao.com', 'Tomasturbao', '123123123', '_123123123', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('passwords do not match') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + registerUser('Tomas', 'Turbao', 'tomas@turbao.com', 'Tomasturbao', '123123123', '123123123', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + afterEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/registerUser.test.js b/staff/lucas-orts/ponies/cor/logic/registerUser.test.js index f4dff589a..4070b8184 100644 --- a/staff/lucas-orts/ponies/cor/logic/registerUser.test.js +++ b/staff/lucas-orts/ponies/cor/logic/registerUser.test.js @@ -1,11 +1,22 @@ +import 'dotenv/config' import registerUser from './registerUser.js' -registerUser('Tomas', 'Turbao', 'tomas@turbao.com', 'Tomasturbao', '123123123', '123123123', error => { - if (error) { - console.error(error) +import mongoose from 'mongoose' - return - } +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') - console.log('user registered') -}) \ No newline at end of file + registerUser('Ester', 'Colero', 'ester@colero.com', 'Estercolero', '123123123', '123123123', error => { + if (error) { + console.error(error) + + return + } + + console.log('user registered') + + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) diff --git a/staff/lucas-orts/ponies/cor/logic/toggleFavPost.js b/staff/lucas-orts/ponies/cor/logic/toggleFavPost.js index b6cdadae1..2e962cbc9 100644 --- a/staff/lucas-orts/ponies/cor/logic/toggleFavPost.js +++ b/staff/lucas-orts/ponies/cor/logic/toggleFavPost.js @@ -1,55 +1,42 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User, Post } from '../data/models.js' +import { validate, errors } from 'com' -const toggleFavPost = (username, postId, callback) => { +const { NotFoundError, SystemError } = errors + +export default (username, postId, callback) => { validate.username(username) validate.postId(postId) validate.callback(callback) - data.findUser(user => user.username == username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (user === null) { - callback(new Error('user not found')) - - return - } - - data.findPost(post => post.id === postId, (error, post) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } + Post.findById(postId).lean() + .then(post => { + if (!post) { + callback(new NotFoundError('post not found')) - if (post === null) { - callback(new Error('post not found')) + return + } - return - } - - const index = user.favs.indexOf(postId) + const { favs } = user + const index = favs.findIndex(postObjectId => postObjectId.toString() === postId) - if (index < 0) - user.favs.push(postId) - else - user.favs.splice(index, 1) - data.updateUser(user => user.username === username, user, error => { - if (error) { - callback(new Error(error.message)) + if (index < 0) + favs.push(postId) + else + favs.splice(index, 1) - return - } - - callback(null) - }) + User.updateOne({ username }, { $set: { favs } }) + .then(() => callback(null)) + .catch(error => callback(new SystemError(error.message))) + }) + .catch(error => callback(new SystemError(error.message))) }) - }) -} - -export default toggleFavPost \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/toggleFavPost.spec.js b/staff/lucas-orts/ponies/cor/logic/toggleFavPost.spec.js new file mode 100644 index 000000000..4f0cac260 --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/toggleFavPost.spec.js @@ -0,0 +1,190 @@ +import 'dotenv/config' +import toggleFavPost from './toggleFavPost.js' +import mongoose, { Types } from 'mongoose' + +import { errors } from 'com' + +const { ObjectId } = Types + +import { expect } from 'chai' +import { User, Post } from '../data/models.js' + +const { ValidationError, NotFoundError } = errors + +describe('toggleFavPost', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on existing user and post with no favs', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post => { + toggleFavPost(user.username, post.id, error => { + if (error) { + console.error(error) + + return + } + + User.findOne({ username: 'Tomasturbao' }) + .then(user => { + expect(user.favs).to.include(post.id) + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on existing post and user with favs', done => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola', likes: ['Tomasturbao'] }) + .then(post => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123', favs: [post.id] }) + .then(() => { + toggleFavPost('Tomasturbao', post.id, error => { + if (error) { + console.error(error) + + return + } + + User.findOne({ username: 'Tomasturbao' }).lean() + .then(user => { + expect(user.favs).to.not.include(post.id) + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('fails on non-existing user', done => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post => { + toggleFavPost('Tomasturbao', post.id, error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on existing user but non-existing post', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(() => { + toggleFavPost('Tomasturbao', new ObjectId().toString(), error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('post not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on non-string username', () => { + let error + + try { + toggleFavPost(123, new ObjectId().toString(), error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + toggleFavPost('', new ObjectId().toString(), error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string postId', () => { + let error + + try { + toggleFavPost('Tomasturbao', 123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('postId is not a string') + } + }) + + it('fails on invalid postId', () => { + let error + + try { + toggleFavPost('Tomasturbao', '', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid postId') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + toggleFavPost('Tomasturbao', new ObjectId().toString(), 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + + afterEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/toggleFavPost.test.js b/staff/lucas-orts/ponies/cor/logic/toggleFavPost.test.js index f61be7435..ea82ee359 100644 --- a/staff/lucas-orts/ponies/cor/logic/toggleFavPost.test.js +++ b/staff/lucas-orts/ponies/cor/logic/toggleFavPost.test.js @@ -1,9 +1,23 @@ +import 'dotenv/config' import toggleFavPost from './toggleFavPost.js' -toggleFavPost('Jhonymelavo', '2gouhl5uylg', error => { - if (error) { - console.error(error) - return - } -}) \ No newline at end of file +import mongoose from 'mongoose' + +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') + + toggleFavPost('Cacatua', '66a21404e66ac7c76a9f9bbb', error => { + if (error) { + console.error(error) + + return + } + + console.log('fav post toggled') + + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.js b/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.js index 63c1e448b..540e9f394 100644 --- a/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.js +++ b/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.js @@ -1,55 +1,44 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User } from '../data/models.js' +import { validate, errors } from 'com' -const toggleFollowUser = (username, targetUsername, callback) => { +const { NotFoundError, SystemError } = errors + +export default (username, targetUsername, callback) => { validate.username(username) - validate.username(targetUsername) + validate.username(targetUsername, 'targetUsername') validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (!user) { - callback(new Error('user not found')) - - return - } - - data.findUser(user => user.username === targetUsername, (error, targetUser) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - if (!targetUser) { - callback(new Error('following user not found')) + User.findOne({ username: targetUsername }).lean() + .then(targetUser => { + if (!targetUser) { + callback(new NotFoundError('targetUser not found')) - return - } + return + } - const index = user.following.indexOf(targetUsername) + const { following } = user - if (index < 0) - user.following.push(targetUsername) - else - user.following.splice(index, 1) + const index = following.indexOf(targetUsername) - data.updateUser(user => user.username === username, user, error => { - if (error) { - callback(new Error(error.message)) + if (index < 0) + following.push(targetUsername) + else + following.splice(index, 1) - return - } + User.updateOne({ username }, { $set: { following } }) + .then(() => callback(null)) + .catch(error => callback(new SystemError(error.message))) + }) - callback(null) - }) + .catch(error => callback(new SystemError(error.message))) }) - }) -} - -export default toggleFollowUser \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.spec.js b/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.spec.js new file mode 100644 index 000000000..b4f7a276c --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.spec.js @@ -0,0 +1,182 @@ +import 'dotenv/config' +import toggleFollowUser from './toggleFollowUser.js' +import mongoose from 'mongoose' + +import { errors } from 'com' + +import { expect } from 'chai' +import { User } from '../data/models.js' + +const { NotFoundError, ValidationError } = errors + +describe('toggleFollowUser', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + it('succeeds on existing user and targetUser with no following', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + User.create({ name: 'Ester', surname: 'Colero', email: 'ester@colero.com', username: 'Estercolero', password: '123123123' }) + .then(targetUser => { + toggleFollowUser(user.username, targetUser.username, error => { + if (error) { + console.error(error) + + return + } + + User.findOne({ username: 'Tomasturbao' }).lean() + .then(user => { + expect(user.following).to.include('Estercolero') + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on existing user and targetUser with following', done => { + User.create({ name: 'Ester', surname: 'Colero', email: 'ester@colero.com', username: 'Estercolero', password: '123123123' }) + .then(() => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123', following: ['Estercolero'] }) + .then(() => { + toggleFollowUser('Tomasturbao', 'Estercolero', error => { + if (error) { + console.error(error) + + return + } + + User.findOne({ username: 'Tomasturbao' }).lean() + .then(user => { + expect(user.following).to.not.include({ username: 'Estercolero' }) + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('fails on non-existing user', done => { + User.create({ name: 'Ester', surname: 'Colero', email: 'ester@colero.com', username: 'Estercolero', password: '123123123' }) + .then(user => { + toggleFollowUser('Tomasturbao', user.username, error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on existing user but non-existing targetUser', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(() => { + toggleFollowUser('Tomasturbao', 'Estercolero', error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('targetUser not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on non-string username', () => { + let error + + try { + toggleFollowUser(123, 'Estercolero', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + toggleFollowUser('', 'Estercolero', error => { }) + NotFoundError + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string targetUsername', () => { + let error + + try { + toggleFollowUser('Tomasturbao', 123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('targetUsername is not a string') + } + }) + + it('fails on invalid targetUsername', () => { + let error + + try { + toggleFollowUser('Tomasturbao', '', error => { }) + NotFoundError + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid targetUsername') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + toggleFollowUser('Tomasturbao', 'Estercolero', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + + afterEach(done => { + User.deleteMany({}) + .then(() => done()) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.test.js b/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.test.js index 78aa26acf..6c0229ba1 100644 --- a/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.test.js +++ b/staff/lucas-orts/ponies/cor/logic/toggleFollowUser.test.js @@ -1,9 +1,22 @@ +import 'dotenv/config' import toggleFollowUser from './toggleFollowUser.js' -toggleFollowUser('Jhonymelavo', 'Cacatua', error => { - if (error) { - console.error(error) - return - } -}) \ No newline at end of file +import mongoose from 'mongoose' + +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') + + toggleFollowUser('Petazeta', 'Cacatua', error => { + if (error) { + console.error(error) + + return + } + console.log('follow user toggled') + + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/toggleLikePost.js b/staff/lucas-orts/ponies/cor/logic/toggleLikePost.js index 8a3b5c3ff..4c4e27729 100644 --- a/staff/lucas-orts/ponies/cor/logic/toggleLikePost.js +++ b/staff/lucas-orts/ponies/cor/logic/toggleLikePost.js @@ -1,55 +1,41 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User, Post } from '../data/models.js' +import { validate, errors } from 'com' -const toggleLikePost = (username, postId, callback) => { +const { NotFoundError, SystemError } = errors + +export default (username, postId, callback) => { validate.username(username) validate.postId(postId) validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (!user) { - callback(new Error('user not found')) - - return - } - - data.findPost(post => post.id === postId, (error, post) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - - if (!post) { - callback(new Error('post not found')) - - return - } - - const index = post.likes.indexOf(username) - - if (index < 0) - post.likes.push(username) - else - post.likes.splice(index, 1) - - data.updatePost(post => post.id === postId, post, error => { - if (error) { - callback(new Error(error.message)) - - return - } - - callback(null) - }) + Post.findById(postId).lean() + .then(post => { + if (!post) { + callback(new NotFoundError('post not found')) + + return + } + + const { likes } = post + const index = likes.indexOf(username) + + if (index < 0) + likes.push(username) + else + likes.splice(index, 1) + + Post.updateOne({ _id: postId }, { $set: { likes } }) + .then(() => callback(null)) + .catch(error => callback(new SystemError(error.message))) + }) + .catch(error => callback(new SystemError(error.message))) }) - }) -} - -export default toggleLikePost \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/toggleLikePost.spec.js b/staff/lucas-orts/ponies/cor/logic/toggleLikePost.spec.js new file mode 100644 index 000000000..f3b1b625f --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/toggleLikePost.spec.js @@ -0,0 +1,190 @@ +import 'dotenv/config' +import toggleLikePost from './toggleLikePost.js' +import mongoose, { Types } from 'mongoose' + +import { errors } from 'com' + +const { ObjectId } = Types + +import { expect } from 'chai' +import { User, Post } from '../data/models.js' + +const { NotFoundError, ValidationError } = errors + +describe('toggleLikePost', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on existing user and post with no likes', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(() => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'wtf' }) + .then(post => { + toggleLikePost('Tomasturbao', post.id, error => { + if (error) { + console.error(error) + + return + } + + Post.findById(post.id).lean() + .then(post => { + expect(post.likes).to.include('Tomasturbao') + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on existing user and post with likes', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(() => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'wtf', likes: ['Tomasturbao'] }) + .then(post => { + toggleLikePost('Tomasturbao', post.id, error => { + if (error) { + console.error(error) + + return + } + + Post.findById(post.id).lean() + .then(post => { + expect(post.likes).to.not.include('Tomasturbao') + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('fails on non-existing user', done => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'wtf' }) + .then(post => { + toggleLikePost('Tomasturbao', post.id, error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on existing user but non-existing post', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(() => { + toggleLikePost('Tomasturbao', new ObjectId().toString(), error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('post not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on non-string username', () => { + let error + + try { + toggleLikePost(123, new ObjectId().toString(), error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + toggleLikePost('', new ObjectId().toString(), error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string postId', () => { + let error + + try { + toggleLikePost('Tomasturbao', 123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('postId is not a string') + } + }) + + it('fails on invalid postId', () => { + let error + + try { + toggleLikePost('Tomasturbao', '', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid postId') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + toggleLikePost('Tomasturbao', new ObjectId().toString(), 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + + afterEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/toggleLikePost.test.js b/staff/lucas-orts/ponies/cor/logic/toggleLikePost.test.js index c030e3a4d..2a2b6e052 100644 --- a/staff/lucas-orts/ponies/cor/logic/toggleLikePost.test.js +++ b/staff/lucas-orts/ponies/cor/logic/toggleLikePost.test.js @@ -1,9 +1,20 @@ +import 'dotenv/config' import toggleLikePost from './toggleLikePost.js' -toggleLikePost('Jhonymelavo', '2gouhl5uylg', error => { - if (error) { - console.error(error) +import mongoose from 'mongoose' - return - } -}) \ No newline at end of file +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') + + toggleLikePost('Petazeta', '66a2145ceaa27d4bacad182a', error => { + if (error) { + console.error(error) + + return + } + console.log('like post toggled') + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/updatePostCaption.js b/staff/lucas-orts/ponies/cor/logic/updatePostCaption.js index fcef726c9..8f82d162b 100644 --- a/staff/lucas-orts/ponies/cor/logic/updatePostCaption.js +++ b/staff/lucas-orts/ponies/cor/logic/updatePostCaption.js @@ -1,51 +1,41 @@ -import data from '../data/index.js' -import validate from '../validate.js' +import { User, Post } from '../data/models.js' +import { validate, errors } from 'com' -const updatePostCaption = (username, postId, newCaption, callback) => { +const { NotFoundError, SystemError, OwnershipError } = errors + +export default (username, postId, caption, callback) => { validate.username(username) validate.postId(postId) - validate.string(newCaption) + validate.string(caption, 'caption') validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(new Error(error.message)) - - return - } - - if (user === null) { - callback(new Error('User not found')) - - return - } - - data.findPost(post => post.id === postId, (error, post) => { - if (error) { - callback(new Error(error.message)) + User.findOne({ username }).lean() + .then(user => { + if (!user) { + callback(new NotFoundError('user not found')) return } - if (!post) { - callback(new Error('Post not found')) + Post.findById(postId).lean() + .then(post => { + if (!post) { + callback(new NotFoundError('post not found')) - return - } + return + } + if (post.author !== username) { + callback(new OwnershipError('post does not belong to user')) - post.caption = newCaption + return + } - data.updatePost(post => post.id === postId, post, error => { - if (error) { - callback(new Error(error.message)) + Post.updateOne({ _id: postId }, { $set: { caption } }) + .then(() => callback(null)) + .catch(error => callback(new SystemError(error.message))) - return - } - - callback(null) - }) + }) + .catch(error => callback(new SystemError(error.message))) }) - }) -} - -export default updatePostCaption \ No newline at end of file + .catch(error => callback(new SystemError(error.message))) +} \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/updatePostCaption.spec.js b/staff/lucas-orts/ponies/cor/logic/updatePostCaption.spec.js new file mode 100644 index 000000000..1e521dbfd --- /dev/null +++ b/staff/lucas-orts/ponies/cor/logic/updatePostCaption.spec.js @@ -0,0 +1,198 @@ +import 'dotenv/config' +import updatePostCaption from './updatePostCaption.js' +import mongoose, { Types } from 'mongoose' + +import { errors } from 'com' + +const { ObjectId } = Types + +import { expect } from 'chai' +import { User, Post } from '../data/models.js' + +const { NotFoundError, OwnershipError, ValidationError } = errors + +describe('updatePostCaption', () => { + before(done => { + mongoose.connect(process.env.MONGODB_URI) + .then(() => done()) + .catch(error => done(error)) + }) + + beforeEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('succeeds on existing user and post', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(() => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post => { + updatePostCaption('Tomasturbao', post.id, 'hola soy Tomas', error => { + if (error) { + console.error(error) + + return + } + + Post.findById(post.id).lean() + .then(post => { + expect(post.caption).to.equal('hola soy Tomas') + + done() + }) + .catch(error => done(error)) + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('fails on existing user and post but post does not belog to user', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(user => { + User.create({ name: 'Ester', surname: 'Colero', email: 'ester@colero.com', username: 'Estercolero', password: '123123123' }) + .then(() => { + Post.create({ author: 'Estercolero', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post => { + updatePostCaption(user.username, post.id, 'hola mundo', error => { + expect(error).to.be.instanceOf(OwnershipError) + expect(error.message).to.equal('post does not belong to user') + + done() + }) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + it('fails on non-existing user', done => { + Post.create({ author: 'Tomasturbao', image: 'https://media.giphy.com/media/ji6zzUZwNIuLS/giphy.gif?cid=790b7611qml3yetzjkqcp26cvoxayvif8j713kmqj2yp06oi&ep=v1_gifs_trending&rid=giphy.gif&ct=g', caption: 'hola' }) + .then(post => { + updatePostCaption('Tomasturbao', post.id, 'hola soy Tomas', error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('user not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on existing user but non-existing post', done => { + User.create({ name: 'Tomas', surname: 'Turbao', email: 'tomas@turbao.com', username: 'Tomasturbao', password: '123123123' }) + .then(() => { + updatePostCaption('Tomasturbao', new ObjectId().toString(), 'hola soy Tomas', error => { + expect(error).to.be.instanceOf(NotFoundError) + expect(error.message).to.equal('post not found') + + done() + }) + }) + .catch(error => done(error)) + }) + + it('fails on non-string username', () => { + let error + + try { + updatePostCaption(123, new ObjectId().toString(), 'hola soy Tomas', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('username is not a string') + } + }) + + it('fails on invalid username', () => { + let error + + try { + updatePostCaption('', new ObjectId().toString(), 'hola soy Tomas', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid username') + } + }) + + it('fails on non-string postId', () => { + let error + + try { + updatePostCaption('Tomasturbao', 123, 'hola soy Tomas', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('postId is not a string') + } + }) + + it('fails on invalid postId', () => { + let error + + try { + updatePostCaption('Tomasturbao', '', 'hola soy Tomas', error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('invalid postId') + } + }) + + it('fails on non-string caption', () => { + let error + + try { + updatePostCaption('Tomasturbao', new ObjectId().toString(), 123, error => { }) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('caption is not a string') + } + }) + + it('fails on non-function callback', () => { + let error + + try { + updatePostCaption('Tomasturbao', new ObjectId().toString(), 'hola soy Tomas', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('callback is not a function') + } + }) + + + afterEach(done => { + User.deleteMany({}) + .then(() => { + Post.deleteMany() + .then(() => done()) + .catch(error => done(error)) + }) + .catch(error => done(error)) + }) + + after(done => { + mongoose.disconnect() + .then(() => done()) + .catch(error => done(error)) + }) +}) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/logic/updatePostCaption.test.js b/staff/lucas-orts/ponies/cor/logic/updatePostCaption.test.js index 9f861d942..8a0d005ed 100644 --- a/staff/lucas-orts/ponies/cor/logic/updatePostCaption.test.js +++ b/staff/lucas-orts/ponies/cor/logic/updatePostCaption.test.js @@ -1,9 +1,21 @@ +import 'dotenv/config' import updatePostCaption from './updatePostCaption.js' -updatePostCaption('Petazeta', '2gouhl5uylg', 'ponita marica', error => { - if (error) { - console.error(error) +import mongoose from 'mongoose' - return - } -}) \ No newline at end of file +mongoose.connect(process.env.MONGODB_URI) + .then(() => { + console.log('connected') + + updatePostCaption('Petazeta', '66a2145ceaa27d4bacad182a', 'ponita marica', error => { + if (error) { + console.error(error) + + return + } + console.log('post caption updated') + + mongoose.disconnect() + }) + }) + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/lucas-orts/ponies/cor/package-lock.json b/staff/lucas-orts/ponies/cor/package-lock.json new file mode 100644 index 000000000..fa3bf5ab9 --- /dev/null +++ b/staff/lucas-orts/ponies/cor/package-lock.json @@ -0,0 +1,1151 @@ +{ + "name": "cor", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cor", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "com": "file:../com", + "mongoose": "^8.5.1" + }, + "devDependencies": { + "bcryptjs": "^2.4.3", + "chai": "^5.1.1", + "dotenv": "^16.4.5", + "mocha": "^10.7.0" + } + }, + "../com": { + "version": "1.0.0", + "license": "ISC" + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.8.tgz", + "integrity": "sha512-qKwC/M/nNNaKUBMQ0nuzm47b7ZYWQHN3pcXq4IIcoSBc2hOIrflAxJduIvvqmhoz3gR2TacTAs8vlsCVPkiEdQ==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/bson": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", + "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chai": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", + "dev": true, + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "engines": { + "node": ">= 16" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/com": { + "resolved": "../com", + "link": true + }, + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loupe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", + "dev": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, + "node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.0.tgz", + "integrity": "sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mongodb": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.7.0.tgz", + "integrity": "sha512-TMKyHdtMcO0fYBNORiYdmM25ijsHs+Njs963r4Tro4OQZzqYigAzYQouwWRg4OIaiLRUEGUh/1UAcH5lxdSLIA==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.5.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.5.1.tgz", + "integrity": "sha512-OhVcwVl91A1G6+XpjDcpkGP7l7ikZkxa0DylX7NT/lcEqAjggzSdqDxb48A+xsDxqNAr0ntSJ1yiE3+KJTOd5Q==", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "6.7.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/staff/lucas-orts/ponies/cor/package.json b/staff/lucas-orts/ponies/cor/package.json index 33faa39e7..1a5fbb9f2 100644 --- a/staff/lucas-orts/ponies/cor/package.json +++ b/staff/lucas-orts/ponies/cor/package.json @@ -4,10 +4,21 @@ "main": "index.js", "type": "module", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "npx mocha logic/*.spec.js", + "test-inspect": "npx mocha --inspect-brk logic/*.spec.js" }, "author": "", "license": "ISC", "keywords": [], + "devDependencies": { + "bcryptjs": "^2.4.3", + "chai": "^5.1.1", + "dotenv": "^16.4.5", + "mocha": "^10.7.0" + }, + "dependencies": { + "com": "file:../com", + "mongoose": "^8.5.1" + }, "description": "" -} \ No newline at end of file +} diff --git a/staff/lucas-orts/ponies/cor/validate.js b/staff/lucas-orts/ponies/cor/validate.js deleted file mode 100644 index b1291a8e1..000000000 --- a/staff/lucas-orts/ponies/cor/validate.js +++ /dev/null @@ -1,74 +0,0 @@ -const USERNAME_REGEX = /^(?!.*\s{2})[a-zA-Z0-9._-]{4,16}$/ -const NAME_REGEX = /^(?!.*\s{2})[a-zA-Z ]{3,16}$/ -const EMAIL_REGEX = /^[a-z0-9._]+@[a-z0-9.-]{3,63}\.[a-z]{2,10}$/ - -function validateString(value, explain = 'value') { - if (typeof value !== 'string') throw new TypeError(`${explain} is not a string`) -} - -function validateCallback(callback, explain = 'callback') { - if (typeof callback !== 'function') throw new TypeError(`${explain} is not a function`) -} - -function validateObject(object, explain = 'object') { - if (object === null || typeof object !== 'object' || object.constructor !== Object) throw new TypeError(`${explain} is not an object`) -} - -function validateUsername(username) { - validateString(username, 'username') - if (!USERNAME_REGEX.test(username)) throw new SyntaxError('invalid username') -} - -function validateTargetUsername(username) { - validateString(username, 'username') - if (!USERNAME_REGEX.test(username)) throw new SyntaxError('invalid username') -} - -function validatePassword(password) { - validateString(password, 'password') - if (password.trim().length < 8) throw new SyntaxError('password length is lower than 8 characters ') - if (password.includes(' ')) throw new SyntaxError('paasword has empty sapece') -} - -function validateName(name, explain = 'name') { - validateString(name, explain) - if (!NAME_REGEX.test(name)) throw new SyntaxError(`invalid ${explain}`) -} - -function validateEmail(email) { - validateString(email, 'email') - if (!EMAIL_REGEX.test(email)) throw new SyntaxError('invalid email') -} - - -function validateImage(image, explain = 'image') { - validateString(image, 'image') - if (!image.startsWith('http')) throw new SyntaxError(`invalid ${explain}`) -} - -function validatePostId(postId) { - validateString(postId, 'postId') - if (postId.trim().length === 0) throw new Error('invalid postId') -} - -function validateDate(date) { - validateString(date, 'date') - if (date.trim().length === 0) throw new Error('invalid date') -} - - -const validate = { - callback: validateCallback, - string: validateString, - object: validateObject, - username: validateUsername, - targetUsername: validateTargetUsername, - password: validatePassword, - name: validateName, - email: validateEmail, - image: validateImage, - postId: validatePostId, - date: validateDate -} - -export default validate \ No newline at end of file