From 71144887431d968717ea9994e675f7ba3ff96b11 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 1 Jul 2024 20:49:38 +0200 Subject: [PATCH] create like and fix errors #140 --- .../socialcode/api/data/posts.json | 74 --------------- .../socialcode/api/data/users.json | 9 -- staff/adrian-martin/socialcode/api/index.js | 67 +++++++++---- .../socialcode/api/logic/createPost.js | 71 +++++++++----- .../socialcode/api/logic/createPost.test.js | 38 ++++++-- .../socialcode/api/logic/deletePost.js | 94 +++++++++++++------ .../socialcode/api/logic/deletePost.test.js | 38 ++++++-- .../socialcode/api/logic/getAllPosts.js | 59 ++++++++---- .../socialcode/api/logic/getAllPosts.test.js | 38 ++++++-- .../socialcode/api/logic/getUserName.js | 64 ++++++++----- .../socialcode/api/logic/getUserName.test.js | 36 +++++-- .../socialcode/api/logic/index.js | 6 +- .../socialcode/api/logic/toggleLikePost.js | 43 +++++++++ .../api/logic/toggleLikePost.test.js | 33 +++++++ .../socialcode/api/test/create-post.sh | 1 + .../socialcode/api/test/delete-post.sh | 1 + .../socialcode/api/test/get-all-posts.sh | 1 + .../socialcode/api/test/get-user-name.sh | 1 + .../socialcode/api/test/toggle-like-post.sh | 1 + staff/adrian-martin/socialcode/app/.env | 2 +- .../socialcode/app/src/logic/index.js | 6 +- .../app/src/logic/toggleLikePost.js | 31 ++++++ .../app/src/views/components/Post.jsx | 26 ++++- .../app/src/views/components/PostList.jsx | 7 +- .../adrian-martin/socialcode/com/validate.js | 2 +- 25 files changed, 509 insertions(+), 240 deletions(-) delete mode 100644 staff/adrian-martin/socialcode/api/data/posts.json delete mode 100644 staff/adrian-martin/socialcode/api/data/users.json create mode 100644 staff/adrian-martin/socialcode/api/logic/toggleLikePost.js create mode 100644 staff/adrian-martin/socialcode/api/logic/toggleLikePost.test.js create mode 100644 staff/adrian-martin/socialcode/api/test/create-post.sh create mode 100644 staff/adrian-martin/socialcode/api/test/delete-post.sh create mode 100644 staff/adrian-martin/socialcode/api/test/get-all-posts.sh create mode 100644 staff/adrian-martin/socialcode/api/test/get-user-name.sh create mode 100644 staff/adrian-martin/socialcode/api/test/toggle-like-post.sh create mode 100644 staff/adrian-martin/socialcode/app/src/logic/toggleLikePost.js diff --git a/staff/adrian-martin/socialcode/api/data/posts.json b/staff/adrian-martin/socialcode/api/data/posts.json deleted file mode 100644 index 94f967922..000000000 --- a/staff/adrian-martin/socialcode/api/data/posts.json +++ /dev/null @@ -1,74 +0,0 @@ -[ - { - "author": "jameshook", - "title": "smile1", - "image": "https://m.media-amazon.com/images/I/41xsPjrM-pL._AC_UF350,350_QL50_.jpg", - "description": "hi 2", - "date": "2024-05-27T14:44:14.236Z", - "id": "8448820043690848-1716821054237" - }, - { - "author": "jameshook", - "title": "smile2", - "image": "https://m.media-amazon.com/images/I/41xsPjrM-pL._AC_UF350,350_QL50_.jpg", - "description": "hi 2", - "date": "2024-05-27T14:44:21.737Z", - "id": "05670034285101688-1716821061738" - }, - { - "author": "jameshook", - "title": "smile3", - "image": "https://m.media-amazon.com/images/I/41xsPjrM-pL._AC_UF350,350_QL50_.jpg", - "description": "hi 2", - "date": "2024-05-27T14:44:23.250Z", - "id": "549161270915079-1716821063251" - }, - { - "author": "Brutalito", - "title": "JSON Momoa", - "image": "https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExYTgxa2Y4OWl6YnJ6c2I4dndwZDQ0dHJsaXZ1N3B6ZnhsY2Q2eW5wcCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/QOxoMjSfRFATCNLDDT/giphy.gif", - "description": "I Love JSON MOMOA", - "date": "2024-05-30T21:37:32.119Z", - "id": "3243047678592019-1717105052119" - }, - { - "author": "Pollete", - "title": "SAD POLLETE", - "image": "https://media3.giphy.com/media/Zg7clvqHE3CdW/200.webp?cid=ecf05e47uppvz2sbk9azhbxn6vhvtj8s4a8g6y7h487bwy6m&ep=v1_gifs_search&rid=200.webp&ct=g", - "description": "Triste pajarillo sin alas, susurra en el eter", - "date": "2024-05-30T21:41:20.710Z", - "id": "6840503078560916-1717105280710" - }, - { - "author": "AdrianGon", - "title": "SAD POLLETE", - "image": "https://media3.giphy.com/media/Zg7clvqHE3CdW/200.webp?cid=ecf05e47uppvz2sbk9azhbxn6vhvtj8s4a8g6y7h487bwy6m&ep=v1_gifs_search&rid=200.webp&ct=g", - "description": "Triste pajarillo sin alas, susurra en el eter", - "date": "2024-05-30T21:41:20.710Z", - "id": "6840503078560916-1717105280713" - }, - { - "author": "AdrianGon", - "title": "SAD POLLETE", - "image": "https://media3.giphy.com/media/Zg7clvqHE3CdW/200.webp?cid=ecf05e47uppvz2sbk9azhbxn6vhvtj8s4a8g6y7h487bwy6m&ep=v1_gifs_search&rid=200.webp&ct=g", - "description": "Triste pajarillo sin alas, susurra en el eter", - "date": "2024-05-30T21:41:20.710Z", - "id": "6840503078560916-1717105280720" - }, - { - "author": "AdrianGon", - "title": "veranito", - "image": "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExam40MjY0Y2FtamsyZ3VrcXB4bDk4dWZvaml1Y2hvZTg0M2twNHJvNyZlcD12MV9naWZzX3RyZW5kaW5nJmN0PWc/e7VHjuy09leJPMJ2c2/giphy.gif", - "description": "empieza el veranito", - "date": "2024-06-20T09:02:47.280Z", - "id": "9445498011651718-1718874167280" - }, - { - "author": "AdrianGon", - "title": "smile4", - "image": "https://imgs.search.brave.com/rY4vd7ChrTffot87xezWVyJZcsjp10UPNHx2EQMRCfs/rs:fit:860:0:0/g:ce/aHR0cHM6Ly9pLnBp/bmltZy5jb20vb3Jp/Z2luYWxzLzFiL2Qx/L2I2LzFiZDFiNjE1/ZTZkYTcwZGQ3MWRj/ODRmZDJmNDdjODBk/LmpwZw", - "description": "hi 2", - "date": "2024-06-20T09:08:58.858Z", - "id": "3583688471061328-1718874538859" - } -] \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/data/users.json b/staff/adrian-martin/socialcode/api/data/users.json deleted file mode 100644 index 118e8f166..000000000 --- a/staff/adrian-martin/socialcode/api/data/users.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - { - "name": "Adrian", - "surname": "Martin", - "email": "adrian@martin.com", - "username": "AdrianGon", - "password": "$2a$08$972kMqo2qd.hAhYpxqfype6EkIqenVPM5EOGtIyWAwNh8z.761Y0a" - } -] \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/index.js b/staff/adrian-martin/socialcode/api/index.js index b2c34ef7e..8c96b4275 100644 --- a/staff/adrian-martin/socialcode/api/index.js +++ b/staff/adrian-martin/socialcode/api/index.js @@ -15,9 +15,11 @@ client.connect() .then(connection => { const db = connection.db('test') - const users =db.collection('users') + const users = db.collection('users') + const posts = db.collection('posts') data.users = users + data.posts = posts const { JsonWebTokenError, TokenExpiredError } = jwt @@ -83,6 +85,10 @@ client.connect() jwt.verify(token, JWT_SECRET, (error, payload) => { if (error) { + if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) + res.status(500).json({ error: SystemError.name, message: error.message }) + else + res.status(500).json({ error: error.constructor.name, message: error.message }) return @@ -102,10 +108,7 @@ client.connect() }) } catch (error) { - if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) - res.status(500).json({ error: SystemError.name, message: error.message }) - else - res.status(500).json({ error: error.constructor.name, message: error.message }) + res.status(500).json({ error: error.constructor.name, message: error.message }) } }) @@ -117,6 +120,9 @@ client.connect() jwt.verify(token, JWT_SECRET, (error, payload) => { if (error) { + if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) + res.status(500).json({ error: SystemError.name, message: error.message }) + else res.status(500).json({ error: error.constructor.name, message: error.message }) return @@ -134,9 +140,6 @@ client.connect() }) } catch (error) { - if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) - res.status(500).json({ error: SystemError.name, message: error.message }) - else res.status(500).json({ error: error.constructor.name, message: error.message }) } }) @@ -149,6 +152,9 @@ client.connect() jwt.verify(token, JWT_SECRET, (error, payload) => { if (error) { + if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) + res.status(500).json({ error: SystemError.name, message: error.message }) + else res.status(500).json({ error: error.constructor.name, message: error.message }) return @@ -168,9 +174,6 @@ client.connect() }) }) } catch (error) { - if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) - res.status(500).json({ error: SystemError.name, message: error.message }) - else res.status(500).json({ error: error.constructor.name, message: error.message }) } @@ -183,6 +186,9 @@ client.connect() jwt.verify(token, JWT_SECRET, (error, payload) => { if (error) { + if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) + res.status(500).json({ error: SystemError.name, message: error.message }) + else res.status(500).json({ error: error.constructor.name, message: error.message }) return @@ -202,14 +208,43 @@ client.connect() }) }) } catch (error) { - if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) - res.status(500).json({ error: SystemError.name, message: error.message }) - else - res.status(500).json({ error: error.constructor.name, message: error.message }) + res.status(500).json({ error: error.constructor.name, message: error.message }) + } + }) + + api.patch('/posts/:postId/likes', (req, res) => { + try { + const token = req.headers.authorization.slice(7) + + jwt.verify(token, JWT_SECRET, (error, payload) => { + if (error) { + if (error instanceof JsonWebTokenError || error instanceof TokenExpiredError) + res.status(500).json({ error: SystemError.name, message: error.message }) + else + res.status(500).json({ error: error.constructor.name, message: error.message }) + + return + } + const { sub: username } = payload + + const { postId } = req.params + + 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.listen(PORT, () => console.log('api is up')) + api.listen(PORT, () => console.log(`API running on PORT ${PORT}`)) }) .catch(error => console.error(error)) diff --git a/staff/adrian-martin/socialcode/api/logic/createPost.js b/staff/adrian-martin/socialcode/api/logic/createPost.js index 75c42cbaf..794d33229 100644 --- a/staff/adrian-martin/socialcode/api/logic/createPost.js +++ b/staff/adrian-martin/socialcode/api/logic/createPost.js @@ -11,37 +11,60 @@ const createPost = (username, title, image, description, callback) => { validate.text(description, 'description', 200) validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(error) + data.users.findOne({ username }) + .then(user => { + if (!user) { + callback(new MatchError('user not found')) - return - } + return + } - if (!user) { - callback(new MatchError('user not found')) + const post = { + author: username, + title, + image, + description, + date: new Date, + likes: [] + } - return - } + data.posts.insertOne(post) + .then(() => callback(null)) + .catch(error => callback(error)) + }) + .catch(error => console.error(error)) - const post = { - author: username, - title, - image, - description, - date: new Date().toISOString() - } + // data.findUser(user => user.username === username, (error, user) => { + // if (error) { + // callback(error) - data.insertPost(post, error => { - if (error) { - callback(error) + // return + // } - return - } + // if (!user) { + // callback(new MatchError('user not found')) - callback(null) - }) - }) + // return + // } + + // const post = { + // author: username, + // title, + // image, + // description, + // date: new Date().toISOString() + // } + + // data.insertPost(post, error => { + // if (error) { + // callback(error) + + // return + // } + + // callback(null) + // }) + // }) } diff --git a/staff/adrian-martin/socialcode/api/logic/createPost.test.js b/staff/adrian-martin/socialcode/api/logic/createPost.test.js index 81ef7b771..7ac8beb0d 100644 --- a/staff/adrian-martin/socialcode/api/logic/createPost.test.js +++ b/staff/adrian-martin/socialcode/api/logic/createPost.test.js @@ -1,15 +1,33 @@ +import 'dotenv/config' import logic from './index.js' +import { MongoClient } from 'mongodb' +import data from '../data/index.js' -try { - logic.createPost('AdrianGon', 'smile4', 'https://imgs.search.brave.com/rY4vd7ChrTffot87xezWVyJZcsjp10UPNHx2EQMRCfs/rs:fit:860:0:0/g:ce/aHR0cHM6Ly9pLnBp/bmltZy5jb20vb3Jp/Z2luYWxzLzFiL2Qx/L2I2LzFiZDFiNjE1/ZTZkYTcwZGQ3MWRj/ODRmZDJmNDdjODBk/LmpwZw', 'hi 2', error => { - if (error) { - console.error(error) +const { MONGODB_URL } = process.env +const client = new MongoClient(MONGODB_URL) - return - } +client.connect() + .then(connection => { + const db = connection.db('test') + + const users = db.collection('users') + const posts = db.collection('posts') + + data.users = users + data.posts = posts - console.log('posts created') + try { + logic.createPost('AdrianGon', 'smile4', 'https://imgs.search.brave.com/rY4vd7ChrTffot87xezWVyJZcsjp10UPNHx2EQMRCfs/rs:fit:860:0:0/g:ce/aHR0cHM6Ly9pLnBp/bmltZy5jb20vb3Jp/Z2luYWxzLzFiL2Qx/L2I2LzFiZDFiNjE1/ZTZkYTcwZGQ3MWRj/ODRmZDJmNDdjODBk/LmpwZw', 'hi 2', error => { + if (error) { + console.error(error) + + return + } + + console.log('posts created') + }) + } catch (error) { + console.error(error) + } }) -} catch (error) { - console.error(error) -} \ No newline at end of file + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/logic/deletePost.js b/staff/adrian-martin/socialcode/api/logic/deletePost.js index 1e18b023d..7e84c86f1 100644 --- a/staff/adrian-martin/socialcode/api/logic/deletePost.js +++ b/staff/adrian-martin/socialcode/api/logic/deletePost.js @@ -1,55 +1,87 @@ import validate from 'com/validate.js' import data from '../data/index.js' import { MatchError } from 'com/error.js' +import { ObjectId } from 'mongodb' const deletePost = (username, postId, callback) => { validate.username(username) validate.id(postId, 'postId') validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(error) + data.users.findOne({ username }) + .then(user => { + if (!user) { + callback(new MatchError('user not found')) - return - } + return + } - if (!user) { - callback(new MatchError('user not found')) + data.posts.findOne({ _id: new ObjectId(postId) }) + .then(post => { + if (!post) { + callback(new MatchError('posts not found')) + + return + } + + if (post.author !== username) { + callback(new MatchError('post author does not match user')) + + return + } - return - } + data.posts.deleteOne({_id: new ObjectId(postId)}) + .then(() => callback(null)) + .catch(error => callback(error)) + }) + .catch(error => callback(error)) + }) + .catch(error => console.error(error)) - data.findPost(post => post.id === postId, (error, post) => { - if (error) { - callback(error) - return - } + // data.findUser(user => user.username === username, (error, user) => { + // if (error) { + // callback(error) - if (!post) { - callback(new MatchError('posts not found')) + // return + // } - return - } + // if (!user) { + // callback(new MatchError('user not found')) - if (post.author !== username) { - callback(new MatchError('post author does not match user')) + // return + // } - return - } + // data.findPost(post => post.id === postId, (error, post) => { + // if (error) { + // callback(error) - data.deletePost(post => post.id === postId, error => { - if (error) { - callback(error) + // return + // } - return - } + // if (!post) { + // callback(new MatchError('posts not found')) - callback(null) - }) - }) - }) + // return + // } + + // if (post.author !== username) { + // callback(new MatchError('post author does not match user')) + + // return + // } + + // data.deletePost(post => post.id === postId, error => { + // if (error) { + // callback(error) + + // return + // } + + // callback(null) + // }) + // }) + // }) } export default deletePost \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/logic/deletePost.test.js b/staff/adrian-martin/socialcode/api/logic/deletePost.test.js index f4d27aaf0..122b100e5 100644 --- a/staff/adrian-martin/socialcode/api/logic/deletePost.test.js +++ b/staff/adrian-martin/socialcode/api/logic/deletePost.test.js @@ -1,15 +1,33 @@ +import 'dotenv/config' import logic from './index.js' +import { MongoClient } from 'mongodb' +import data from '../data/index.js' -try { - logic.deletePost("AdrianGon", "9445498011651718-1718874167280", error => { - if (error) { - console.error(error) +const { MONGODB_URL } = process.env +const client = new MongoClient(MONGODB_URL) - return - } +client.connect() + .then(connection => { + const db = connection.db('test') + + const users = db.collection('users') + const posts = db.collection('posts') + + data.users = users + data.posts = posts - console.log('posts deleted') + try { + logic.deletePost("AdrianGon", '66827cfdcb6735f4325c126a', error => { + if (error) { + console.error(error) + + return + } + + console.log('posts deleted') + }) + } catch (error) { + console.error(error) + } }) -} catch (error) { - console.error(error) -} \ No newline at end of file + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/logic/getAllPosts.js b/staff/adrian-martin/socialcode/api/logic/getAllPosts.js index cf2fe3656..153a92820 100644 --- a/staff/adrian-martin/socialcode/api/logic/getAllPosts.js +++ b/staff/adrian-martin/socialcode/api/logic/getAllPosts.js @@ -1,35 +1,58 @@ import data from '../data/index.js' -import { MatchError } from 'com/error.js' +import { MatchError, SystemError } from 'com/error.js' import validate from 'com/validate.js' + const getAllPosts = (username, callback) => { // page, limit validate.username(username) validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(error) + data.users.findOne({ username }) + .then(user => { + if (!user) { + callback(new MatchError('user not found')) - return - } + return + } - if (!user) { - callback(new MatchError('user not found')) + data.posts.find({}).toArray() + .then(posts => { + posts.forEach(post => { + post.id = post._id.toString() - return - } + delete post._id + }) - data.findPosts(() => true, (error, posts) => { + callback(null, posts) + }) + .catch(error => callback(new SystemError(error.message))) + }) + .catch(error => callback(new SystemError(error.message))) - if (error) { - callback(error) + // data.findUser(user => user.username === username, (error, user) => { + // if (error) { + // callback(error) - return - } - callback(null, posts.reverse()) - }) - }) + // return + // } + + // if (!user) { + // callback(new MatchError('user not found')) + + // return + // } + + // data.findPosts(() => true, (error, posts) => { + + // if (error) { + // callback(error) + + // return + // } + // callback(null, posts.reverse()) + // }) + // }) } export default getAllPosts \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/logic/getAllPosts.test.js b/staff/adrian-martin/socialcode/api/logic/getAllPosts.test.js index d7397845f..2b6e3f910 100644 --- a/staff/adrian-martin/socialcode/api/logic/getAllPosts.test.js +++ b/staff/adrian-martin/socialcode/api/logic/getAllPosts.test.js @@ -1,15 +1,33 @@ +import 'dotenv/config' import logic from './index.js' +import { MongoClient } from 'mongodb' +import data from '../data/index.js' -try { - logic.getAllPosts("AdrianGon", (error, posts) => { - if (error) { - console.error(error) +const { MONGODB_URL } = process.env +const client = new MongoClient(MONGODB_URL) - return - } +client.connect() + .then(connection => { + const db = connection.db('test') + + const users = db.collection('users') + const posts = db.collection('posts') + + data.users = users + data.posts = posts - console.log('posts retrived', posts) + try { + logic.getAllPosts("AdrianGon", (error, posts) => { + if (error) { + console.error(error) + + return + } + + console.log('posts retrived', posts) + }) + } catch (error) { + console.error(error) + } }) -} catch (error) { - console.error(error) -} \ No newline at end of file + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/logic/getUserName.js b/staff/adrian-martin/socialcode/api/logic/getUserName.js index 08aed4496..5bab70d38 100644 --- a/staff/adrian-martin/socialcode/api/logic/getUserName.js +++ b/staff/adrian-martin/socialcode/api/logic/getUserName.js @@ -8,35 +8,57 @@ const getUserName = (username, targetUsername, callback) => { validate.username(targetUsername, 'targetUsername') validate.callback(callback) - data.findUser(user => user.username === username, (error, user) => { - if (error) { - callback(error) + data.users.findOne({ username }) + .then(user => { + if (!user) { + callback(new MatchError('user not found')) - return - } + return + } - if (!user) { - callback(new MatchError('user not found')) + data.users.findOne({ username: targetUsername }) + .then(user => { + if (!user) { + callback(new MatchError('targetUsername not found')) - return - } + return + } + callback(null, user.name) - data.findUser(user => user.username === targetUsername, (error, targetUsername) => { - if (error) { - callback(error) + }) + .catch(error => callback(error)) + }) + .catch(error => callback(error)) - return - } + // data.findUser(user => user.username === username, (error, user) => { + // if (error) { + // callback(error) - if (!targetUsername) { - callback(new MatchError('targetUsername not found')) + // return + // } - return - } + // if (!user) { + // callback(new MatchError('user not found')) - callback(null, targetUsername.name) - }) - }) + // return + // } + + // data.findUser(user => user.username === targetUsername, (error, targetUsername) => { + // if (error) { + // callback(error) + + // return + // } + + // if (!targetUsername) { + // callback(new MatchError('targetUsername not found')) + + // return + // } + + // callback(null, targetUsername.name) + // }) + // }) } export default getUserName \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/logic/getUserName.test.js b/staff/adrian-martin/socialcode/api/logic/getUserName.test.js index cb532e120..f6183cf85 100644 --- a/staff/adrian-martin/socialcode/api/logic/getUserName.test.js +++ b/staff/adrian-martin/socialcode/api/logic/getUserName.test.js @@ -1,15 +1,31 @@ +import 'dotenv/config' import logic from './index.js' +import { MongoClient } from 'mongodb' +import data from '../data/index.js' -try { - logic.getUserName('AdrianGon', 'AdrianGon', (error, name) => { - if (error) { - console.error(error) +const { MONGODB_URL } = process.env +const client = new MongoClient(MONGODB_URL) - return - } +client.connect() + .then(connection => { + const db = connection.db('test') + + const users = db.collection('users') + + data.users = users - console.log('username retrived', name) + try { + logic.getUserName('AdrianGon', 'AdrianGon', (error, name) => { + if (error) { + console.error(error) + + return + } + + console.log('username retrived', name) + }) + } catch (error) { + console.error(error) + } }) -} catch (error) { - console.error(error) -} \ No newline at end of file + .catch(error => console.error(error)) \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/logic/index.js b/staff/adrian-martin/socialcode/api/logic/index.js index 2e1129545..a24255acf 100644 --- a/staff/adrian-martin/socialcode/api/logic/index.js +++ b/staff/adrian-martin/socialcode/api/logic/index.js @@ -5,6 +5,8 @@ import getAllPosts from './getAllPosts.js' import getUserName from './getUserName.js' import registerUser from './registerUser.js'; +import toggleLikePost from './toggleLikePost.js' + const logic = { registerUser, authenticateUser, @@ -12,7 +14,9 @@ const logic = { getAllPosts, createPost, - deletePost + deletePost, + + toggleLikePost } export default logic \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/logic/toggleLikePost.js b/staff/adrian-martin/socialcode/api/logic/toggleLikePost.js new file mode 100644 index 000000000..3064d1d35 --- /dev/null +++ b/staff/adrian-martin/socialcode/api/logic/toggleLikePost.js @@ -0,0 +1,43 @@ +import data from '../data/index.js' +import validate from 'com/validate.js' +import { MatchError, SystemError } from 'com/error.js' +import { ObjectId } from 'mongodb' + +function toggleLikePost(username, postId, callback) { + validate.username(username) + validate.id(postId, 'postId') + validate.callback(callback) + + data.users.findOne({ username }) + .then(user => { + if (!user) { + callback(new MatchError('user not found')) + + return + } + + data.posts.findOne({ _id: new ObjectId(postId) }) + .then(post => { + if (!post) { + callback(new MatchError('posts not found')) + + return + } + + const index = post.likes.indexOf(username) + + if(index < 0) + post.likes.push(username) + else + post.likes.splice(index, 1) + + data.posts.updateOne({_id: new ObjectId(postId)}, { $set: post }) + .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 toggleLikePost \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/logic/toggleLikePost.test.js b/staff/adrian-martin/socialcode/api/logic/toggleLikePost.test.js new file mode 100644 index 000000000..f8e26d91f --- /dev/null +++ b/staff/adrian-martin/socialcode/api/logic/toggleLikePost.test.js @@ -0,0 +1,33 @@ +import 'dotenv/config' +import logic from './index.js' +import { MongoClient } from 'mongodb' +import data from '../data/index.js' + +const { MONGODB_URL } = process.env +const client = new MongoClient(MONGODB_URL) + +client.connect() + .then(connection => { + const db = connection.db('test') + + const users = db.collection('users') + const posts = db.collection('posts') + + data.users = users + data.posts = posts + + try { + logic.toggleLikePost('AdrianGon', '668293beb5a0a194d7be21f5', error => { + if (error) { + console.error(error) + + return + } + + console.log('user toggled like') + }) + } catch (error) { + console.error(error) + } + }) + .catch(() => console.error(error)) diff --git a/staff/adrian-martin/socialcode/api/test/create-post.sh b/staff/adrian-martin/socialcode/api/test/create-post.sh new file mode 100644 index 000000000..9e6dfc325 --- /dev/null +++ b/staff/adrian-martin/socialcode/api/test/create-post.sh @@ -0,0 +1 @@ +curl -X POST http://localhost:9010/posts -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJBZHJpYW5Hb24iLCJpYXQiOjE3MTk4MjQ5MzEsImV4cCI6MTcxOTgyODUzMX0.pUVP4ZF3MDKTugsDqvOqcTcmSHsCsi9UVHg_nKNNt_g" -H "Content-Type: application/json" -d '{"title":"Buenos Dias","image":"https://imgs.search.brave.com/J8imP8bduv7lIhsfCsnGwaeKNgdHqp2g5dCpK8aVkYA/rs:fit:860:0:0/g:ce/aHR0cHM6Ly90My5m/dGNkbi5uZXQvanBn/LzAxLzAzLzM2LzA0/LzM2MF9GXzEwMzM2/MDQyNV9IekJxZEkx/d1dOTjBWU3Z2ZXkw/R3RRTlRvaDJLenhy/Ny5qcGc","description":"blah blah"}' -v \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/test/delete-post.sh b/staff/adrian-martin/socialcode/api/test/delete-post.sh new file mode 100644 index 000000000..2a9427579 --- /dev/null +++ b/staff/adrian-martin/socialcode/api/test/delete-post.sh @@ -0,0 +1 @@ +curl -X DELETE http://localhost:9010/posts/66827bafec6b2758945088fe -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJBZHJpYW5Hb24iLCJpYXQiOjE3MTk4MjQ5MzEsImV4cCI6MTcxOTgyODUzMX0.pUVP4ZF3MDKTugsDqvOqcTcmSHsCsi9UVHg_nKNNt_g" -v \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/test/get-all-posts.sh b/staff/adrian-martin/socialcode/api/test/get-all-posts.sh new file mode 100644 index 000000000..d9bdb564b --- /dev/null +++ b/staff/adrian-martin/socialcode/api/test/get-all-posts.sh @@ -0,0 +1 @@ +curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJBZHJpYW5Hb24iLCJpYXQiOjE3MTk4MjQ5MzEsImV4cCI6MTcxOTgyODUzMX0.pUVP4ZF3MDKTugsDqvOqcTcmSHsCsi9UVHg_nKNNt_g" http://localhost:9010/posts -v \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/test/get-user-name.sh b/staff/adrian-martin/socialcode/api/test/get-user-name.sh new file mode 100644 index 000000000..fbe482a0c --- /dev/null +++ b/staff/adrian-martin/socialcode/api/test/get-user-name.sh @@ -0,0 +1 @@ +curl http://localhost:9010/users/AdrianGon -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJBZHJpYW5Hb24iLCJpYXQiOjE3MTk4MjQ5MzEsImV4cCI6MTcxOTgyODUzMX0.pUVP4ZF3MDKTugsDqvOqcTcmSHsCsi9UVHg_nKNNt_g" -v \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/api/test/toggle-like-post.sh b/staff/adrian-martin/socialcode/api/test/toggle-like-post.sh new file mode 100644 index 000000000..a35dfb88c --- /dev/null +++ b/staff/adrian-martin/socialcode/api/test/toggle-like-post.sh @@ -0,0 +1 @@ +curl -X PATCH http://localhost:9010/posts/667db530b88331f3b8d43901/likes -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJwZXBpdG9ncmlsbG8iLCJpYXQiOjE3MTk1MTU0MDIsImV4cCI6MTcxOTUxOTAwMn0.ytbXnwi2tmu9m_DqkEqlMWU2VdSMYrXjd8O9ozvB8w0" -v \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/app/.env b/staff/adrian-martin/socialcode/app/.env index 59c1c1813..9efeddeb3 100644 --- a/staff/adrian-martin/socialcode/app/.env +++ b/staff/adrian-martin/socialcode/app/.env @@ -1 +1 @@ -VITE_API_URL = http://localhost:8080 \ No newline at end of file +VITE_API_URL = http://localhost:9010 \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/app/src/logic/index.js b/staff/adrian-martin/socialcode/app/src/logic/index.js index d1450a51d..45f28b002 100644 --- a/staff/adrian-martin/socialcode/app/src/logic/index.js +++ b/staff/adrian-martin/socialcode/app/src/logic/index.js @@ -10,6 +10,8 @@ import getAllPosts from './getAllPosts' import createPost from './createPost' import deletePost from './deletePost' +import toggleLikePost from './toggleLikePost' + const logic = { registerUser, loginUser, @@ -21,7 +23,9 @@ const logic = { getAllPosts, createPost, - deletePost + deletePost, + + toggleLikePost } export default logic \ No newline at end of file diff --git a/staff/adrian-martin/socialcode/app/src/logic/toggleLikePost.js b/staff/adrian-martin/socialcode/app/src/logic/toggleLikePost.js new file mode 100644 index 000000000..d5ac55e74 --- /dev/null +++ b/staff/adrian-martin/socialcode/app/src/logic/toggleLikePost.js @@ -0,0 +1,31 @@ +import errors from "com/error" +import validate from "com/validate" + +const toggleLikePost = (postId, callback) => { + validate.id(postId, 'postId') + validate.callback(callback) + + const xhr = new XMLHttpRequest + + xhr.onload = () => { + if (xhr.status === 204) { + callback(null) + + return + } + + const { error, message } = JSON.parse(xhr.response) + + const constructor = errors[error] + + callback(new constructor(message)) + } + + 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 diff --git a/staff/adrian-martin/socialcode/app/src/views/components/Post.jsx b/staff/adrian-martin/socialcode/app/src/views/components/Post.jsx index fea28bd01..22a956a15 100644 --- a/staff/adrian-martin/socialcode/app/src/views/components/Post.jsx +++ b/staff/adrian-martin/socialcode/app/src/views/components/Post.jsx @@ -12,7 +12,7 @@ import logic from '../../logic' -function Post({ post, onPostDeleted }) { +function Post({ post, onPostDeleted, onPostLikeToggled }) { const handleDeletePost = () => { if (confirm('Delete post?')) try { @@ -34,6 +34,26 @@ function Post({ post, onPostDeleted }) { } } + const handleToggleLikePost = () => { + try { + logic.toggleLikePost(post.id, error => { + if (error) { + console.error(error) + + alert(error.message) + + return + } + + onPostLikeToggled() + }) + } catch (error) { + console.error(error) + + alert(error.message) + } + } + return @@ -45,6 +65,10 @@ function Post({ post, onPostDeleted }) { {post.description} + + + + diff --git a/staff/adrian-martin/socialcode/app/src/views/components/PostList.jsx b/staff/adrian-martin/socialcode/app/src/views/components/PostList.jsx index 4217c2edd..2cf25ff7a 100644 --- a/staff/adrian-martin/socialcode/app/src/views/components/PostList.jsx +++ b/staff/adrian-martin/socialcode/app/src/views/components/PostList.jsx @@ -37,13 +37,16 @@ function PostList({ refreshStamp }) { } } + const handlePostLikeToggled = () => loadPost() + const handleDeletePost = () => loadPost() return {posts.map(post => + key={post.id} post={post} onPostDeleted={handleDeletePost} onPostLikeToggled={handlePostLikeToggled}/> )} } -export default PostList \ No newline at end of file +export default PostList + diff --git a/staff/adrian-martin/socialcode/com/validate.js b/staff/adrian-martin/socialcode/com/validate.js index a19134272..4caa2b68c 100644 --- a/staff/adrian-martin/socialcode/com/validate.js +++ b/staff/adrian-martin/socialcode/com/validate.js @@ -4,7 +4,7 @@ const USERNAME_REGEX = /^[\w-]+$/ const PASSWORD_REGEX = /^[\w-$%&=\[\]\{\}\<\>\(\)]{8,}$/ const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|.(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ const NAME_REGEX = /^[a-zA-Z=\[\]\{\}\<\>\(\)]{1,}$/ -const ID_REGEX = /^[0-9]+-[0-9]+$/ +const ID_REGEX = /^[0-9a-z]+$/ function validateName(name, explain = 'name'){ if (typeof name !== 'string' || !NAME_REGEX.test(name))