From aacbb11898216ae114a2ae0c23b19c5db0bb4f8d Mon Sep 17 00:00:00 2001 From: Marti Herms Date: Mon, 12 Aug 2024 12:12:29 +0200 Subject: [PATCH] implement getUserUsername logic and tests; implement first home react iteration #84 --- .../api/handlers/getUserUsernameHandler.js | 6 +- .../project/V-HUB/api/handlers/index.js | 4 +- staff/marti-herms/project/V-HUB/api/index.js | 2 +- .../V-HUB/api/middleware/errorHandler.js | 4 +- .../V-HUB/api/middleware/jwtVerifier.js | 2 +- .../project/V-HUB/api/test/get-username.sh | 1 + .../marti-herms/project/V-HUB/app/src/App.jsx | 1 + .../project/V-HUB/app/src/home/Footer.jsx | 16 +++ .../project/V-HUB/app/src/home/Header.jsx | 32 ++++++ .../project/V-HUB/app/src/home/Library.jsx | 11 ++ .../project/V-HUB/app/src/home/index.jsx | 33 +++--- .../project/V-HUB/app/src/library/Button.jsx | 2 +- .../V-HUB/app/src/library/Checkbox.jsx | 8 +- .../project/V-HUB/app/src/library/Form.jsx | 2 +- .../project/V-HUB/app/src/library/Logo.jsx | 6 + .../V-HUB/app/src/library/VSeparator.jsx | 3 - .../project/V-HUB/app/src/login/index.jsx | 14 +-- .../project/V-HUB/app/src/register/index.jsx | 15 +-- .../V-HUB/app/util/extractPayloadFromToken.js | 2 +- staff/marti-herms/project/V-HUB/com/errors.js | 11 +- .../project/V-HUB/core/data/models.js | 1 - .../V-HUB/core/logic/getUserUsername.js | 24 ++++ .../V-HUB/core/logic/getUserUsername.spec.js | 104 ++++++++++++++++++ .../V-HUB/core/logic/getUserUsername.test.js | 16 +++ .../project/V-HUB/core/logic/index.js | 4 +- 25 files changed, 270 insertions(+), 54 deletions(-) create mode 100644 staff/marti-herms/project/V-HUB/api/test/get-username.sh create mode 100644 staff/marti-herms/project/V-HUB/app/src/home/Footer.jsx create mode 100644 staff/marti-herms/project/V-HUB/app/src/home/Header.jsx create mode 100644 staff/marti-herms/project/V-HUB/app/src/home/Library.jsx create mode 100644 staff/marti-herms/project/V-HUB/app/src/library/Logo.jsx delete mode 100644 staff/marti-herms/project/V-HUB/app/src/library/VSeparator.jsx create mode 100644 staff/marti-herms/project/V-HUB/core/logic/getUserUsername.js create mode 100644 staff/marti-herms/project/V-HUB/core/logic/getUserUsername.spec.js create mode 100644 staff/marti-herms/project/V-HUB/core/logic/getUserUsername.test.js diff --git a/staff/marti-herms/project/V-HUB/api/handlers/getUserUsernameHandler.js b/staff/marti-herms/project/V-HUB/api/handlers/getUserUsernameHandler.js index 119d5b81a..5bacc64bc 100644 --- a/staff/marti-herms/project/V-HUB/api/handlers/getUserUsernameHandler.js +++ b/staff/marti-herms/project/V-HUB/api/handlers/getUserUsernameHandler.js @@ -1,10 +1,12 @@ import { logic } from 'core' export default (req, res, next) => { - const { userId } = req.params + const { userId } = req + + const { targetUserId } = req.params try { - logic.getUserUsername(userId) + logic.getUserUsername(userId, targetUserId) .then(username => res.json(username)) .catch(error => next(error)) } catch (error) { diff --git a/staff/marti-herms/project/V-HUB/api/handlers/index.js b/staff/marti-herms/project/V-HUB/api/handlers/index.js index 5cba2fd31..bada52a5e 100644 --- a/staff/marti-herms/project/V-HUB/api/handlers/index.js +++ b/staff/marti-herms/project/V-HUB/api/handlers/index.js @@ -1,9 +1,11 @@ import authenticateUserHandler from './authenticateUserHandler.js' import registerUserHandler from './registerUserHandler.js' +import getUserUsernameHandler from './getUserUsernameHandler.js' const handle = { authenticateUser: authenticateUserHandler, - registerUser: registerUserHandler + registerUser: registerUserHandler, + getUserUsername: getUserUsernameHandler } export default handle \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/api/index.js b/staff/marti-herms/project/V-HUB/api/index.js index bbbe34803..4db0992c6 100644 --- a/staff/marti-herms/project/V-HUB/api/index.js +++ b/staff/marti-herms/project/V-HUB/api/index.js @@ -19,7 +19,7 @@ mongoose.connect(process.env.MONGODB_URI) api.post('/users/auth', jsonBodyParser, handle.authenticateUser) - api.get('/users/:userId/username', jsonBodyParser, handle.getUserUsername) + api.get('/users/:targetUserId/username', jwtVerifier, handle.getUserUsername) api.use(errorHandler) diff --git a/staff/marti-herms/project/V-HUB/api/middleware/errorHandler.js b/staff/marti-herms/project/V-HUB/api/middleware/errorHandler.js index a63c41e2c..1415b7bce 100644 --- a/staff/marti-herms/project/V-HUB/api/middleware/errorHandler.js +++ b/staff/marti-herms/project/V-HUB/api/middleware/errorHandler.js @@ -1,6 +1,6 @@ import { errors } from 'com' -const { NotFoundError, CredentialsError, DuplicityError, SessionError, ValidationError } = errors +const { NotFoundError, CredentialsError, DuplicityError, SessionError, ValidationError, OwnershipError } = errors export default (error, req, res, next) => { let status = 500 @@ -9,7 +9,7 @@ export default (error, req, res, next) => { status = 404 else if (error instanceof CredentialsError || error instanceof DuplicityError) - ststus = 409 + status = 409 else if (error instanceof OwnershipError) status = 403 diff --git a/staff/marti-herms/project/V-HUB/api/middleware/jwtVerifier.js b/staff/marti-herms/project/V-HUB/api/middleware/jwtVerifier.js index 114496875..9ecd622f1 100644 --- a/staff/marti-herms/project/V-HUB/api/middleware/jwtVerifier.js +++ b/staff/marti-herms/project/V-HUB/api/middleware/jwtVerifier.js @@ -10,7 +10,7 @@ export default (req, res, next) => { const token = authorization.slice(7) - jwt.verifier(token, process.env.JWT_SECRET, (error, payload) => { + jwt.verify(token, process.env.JWT_SECRET, (error, payload) => { if (error) { res.status(498).json({ error: SessionError.name, message: error.message }) diff --git a/staff/marti-herms/project/V-HUB/api/test/get-username.sh b/staff/marti-herms/project/V-HUB/api/test/get-username.sh new file mode 100644 index 000000000..8b9d0dfe9 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/api/test/get-username.sh @@ -0,0 +1 @@ +curl -v http://localhost:8080/users/66b941110938786955ecf3b5/username -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NmI5NDExMTA5Mzg3ODY5NTVlY2YzYjUiLCJpYXQiOjE3MjM0NDk5MDN9.sE0z2XpU_e6AQlVnebsA9k7gtW8TITWrfEpP73zj3Dw" \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/App.jsx b/staff/marti-herms/project/V-HUB/app/src/App.jsx index c3f85684c..fd4a6d807 100644 --- a/staff/marti-herms/project/V-HUB/app/src/App.jsx +++ b/staff/marti-herms/project/V-HUB/app/src/App.jsx @@ -38,6 +38,7 @@ export default function App() { } const handleLogout = () => { + logic.logoutUser() navigate('/login') } diff --git a/staff/marti-herms/project/V-HUB/app/src/home/Footer.jsx b/staff/marti-herms/project/V-HUB/app/src/home/Footer.jsx new file mode 100644 index 000000000..a4cb21854 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/app/src/home/Footer.jsx @@ -0,0 +1,16 @@ +import logic from '../../logic' + +export default function Footer() { + const handleAddGame = () => { + + } + + const handleRegisterGame = () => { + + } + + return +} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/home/Header.jsx b/staff/marti-herms/project/V-HUB/app/src/home/Header.jsx new file mode 100644 index 000000000..d59643626 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/app/src/home/Header.jsx @@ -0,0 +1,32 @@ +import { useState, useEffect } from 'react' + +import useContext from '../context' + +import logic from '../../logic' + +import Paragraph from '../library/Paragraph' + +export default function Header({ onLogoutClick }) { + const [username, setUsername] = useState(null) + + useEffect(() => { + try { + logic.getUserUsername() + .then(username => setUsername(username)) + .catch(error => { + console.error(error) + + alert(error.message) + }) + } catch (error) { + console.error(error) + + alert(error.message) + } + }, []) + + return
+ {username} + +
+} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/home/Library.jsx b/staff/marti-herms/project/V-HUB/app/src/home/Library.jsx new file mode 100644 index 000000000..eda91f056 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/app/src/home/Library.jsx @@ -0,0 +1,11 @@ +import { useState, useEffect } from 'react' + + + +export default function Library() { + const [games, setGames] = useState(null) + + return
+ {games.map()} +
+} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/home/index.jsx b/staff/marti-herms/project/V-HUB/app/src/home/index.jsx index ccb809cd8..1d5057539 100644 --- a/staff/marti-herms/project/V-HUB/app/src/home/index.jsx +++ b/staff/marti-herms/project/V-HUB/app/src/home/index.jsx @@ -1,25 +1,24 @@ -import { useState, useEffect } from "react" +import { useState, useEffect } from 'react' import useContext from '../context' -export default function Home() { - const [username, setUsername] = useState(null) +import logic from '../../logic' + +import Header from './Header' +import Library from './Library' +import Footer from './Footer' + +export default function Home({ onLogout }) { useEffect(() => { - try { - logic.getUserUsername() - .then(username => setName(username)) - .catch(error => { - console.error(error) - - alert(error.message) - }) - } catch (error) { - console.error(error) - - alert(error.message) - } + }) - return

Hello, {username}

+ return
+
+ + + +
+
} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/library/Button.jsx b/staff/marti-herms/project/V-HUB/app/src/library/Button.jsx index 05298e262..48079cd71 100644 --- a/staff/marti-herms/project/V-HUB/app/src/library/Button.jsx +++ b/staff/marti-herms/project/V-HUB/app/src/library/Button.jsx @@ -1,3 +1,3 @@ export default function Button({ className = '', children, ...nextProps }) { - return + return } \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/library/Checkbox.jsx b/staff/marti-herms/project/V-HUB/app/src/library/Checkbox.jsx index a7318ca87..890de17a0 100644 --- a/staff/marti-herms/project/V-HUB/app/src/library/Checkbox.jsx +++ b/staff/marti-herms/project/V-HUB/app/src/library/Checkbox.jsx @@ -1,3 +1,7 @@ -export default function Checkbox({ className = '', ...nextProps }) { - return +export default function Checkbox({ className = '', id, ...nextProps }) { + return <> + + } \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/library/Form.jsx b/staff/marti-herms/project/V-HUB/app/src/library/Form.jsx index e900df18f..a103a6a7e 100644 --- a/staff/marti-herms/project/V-HUB/app/src/library/Form.jsx +++ b/staff/marti-herms/project/V-HUB/app/src/library/Form.jsx @@ -1,3 +1,3 @@ export default function Form({ className = '', children, onSubmit }) { - return
{children}
+ return
{children}
} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/library/Logo.jsx b/staff/marti-herms/project/V-HUB/app/src/library/Logo.jsx new file mode 100644 index 000000000..18ef13b7f --- /dev/null +++ b/staff/marti-herms/project/V-HUB/app/src/library/Logo.jsx @@ -0,0 +1,6 @@ +export default function Logo() { + return
+ logo +

V-HUB

+
+} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/library/VSeparator.jsx b/staff/marti-herms/project/V-HUB/app/src/library/VSeparator.jsx deleted file mode 100644 index 12888e4d3..000000000 --- a/staff/marti-herms/project/V-HUB/app/src/library/VSeparator.jsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function VSeparator() { - return
-} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/app/src/login/index.jsx b/staff/marti-herms/project/V-HUB/app/src/login/index.jsx index 28b407432..7127da951 100644 --- a/staff/marti-herms/project/V-HUB/app/src/login/index.jsx +++ b/staff/marti-herms/project/V-HUB/app/src/login/index.jsx @@ -1,7 +1,7 @@ import logic from '../../logic' import Input from '../library/Input' -import VSeparator from '../library/VSeparator' +import Logo from '../library/Logo' import Container from '../library/Container' import Form from '../library/Form' import Button from '../library/Button' @@ -47,14 +47,10 @@ export default function Login({ onLogin, onRegisterClick }) { } } - return
- - logo -

V-HUB

-
- - -
+ return
+ + + diff --git a/staff/marti-herms/project/V-HUB/app/src/register/index.jsx b/staff/marti-herms/project/V-HUB/app/src/register/index.jsx index 09c1ffb6e..e1246ebf0 100644 --- a/staff/marti-herms/project/V-HUB/app/src/register/index.jsx +++ b/staff/marti-herms/project/V-HUB/app/src/register/index.jsx @@ -1,7 +1,7 @@ import logic from '../../logic' import Input from '../library/Input' -import VSeparator from '../library/VSeparator' +import Logo from '../library/Logo' import Container from '../library/Container' import Form from '../library/Form' import Button from '../library/Button' @@ -45,20 +45,15 @@ export default function Register({ onLoginClick, onRegister }) { } } - return
- - logo -

V-HUB

-
- - - + return
+ + + - Login diff --git a/staff/marti-herms/project/V-HUB/app/util/extractPayloadFromToken.js b/staff/marti-herms/project/V-HUB/app/util/extractPayloadFromToken.js index 18b891bd9..044aeb436 100644 --- a/staff/marti-herms/project/V-HUB/app/util/extractPayloadFromToken.js +++ b/staff/marti-herms/project/V-HUB/app/util/extractPayloadFromToken.js @@ -1,5 +1,5 @@ export default token => { - const payload64 = token.slice(token.indexOf('.') + 1, token.lastIndexOf('.')) + const payloadB64 = token.slice(token.indexOf('.') + 1, token.lastIndexOf('.')) const payloadJSON = atob(payloadB64) diff --git a/staff/marti-herms/project/V-HUB/com/errors.js b/staff/marti-herms/project/V-HUB/com/errors.js index 6396fc998..0b996f1fd 100644 --- a/staff/marti-herms/project/V-HUB/com/errors.js +++ b/staff/marti-herms/project/V-HUB/com/errors.js @@ -46,13 +46,22 @@ class SessionError extends Error { } } +class OwnershipError extends Error { + constructor(message) { + super(message) + + this.name = this.constructor.name + } +} + const errors = { ValidationError, SystemError, DuplicityError, NotFoundError, CredentialsError, - SessionError + SessionError, + OwnershipError } export default errors \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/core/data/models.js b/staff/marti-herms/project/V-HUB/core/data/models.js index 0f1b3d78d..4a6df4307 100644 --- a/staff/marti-herms/project/V-HUB/core/data/models.js +++ b/staff/marti-herms/project/V-HUB/core/data/models.js @@ -19,7 +19,6 @@ const user = new Schema({ }, avatar: { type: String, - required: true }, role: { type: String, diff --git a/staff/marti-herms/project/V-HUB/core/logic/getUserUsername.js b/staff/marti-herms/project/V-HUB/core/logic/getUserUsername.js new file mode 100644 index 000000000..df4c2a835 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/core/logic/getUserUsername.js @@ -0,0 +1,24 @@ +import { User } from '../data/models.js' + +import { validate, errors } from 'com' + +const { NotFoundError, SystemError } = errors + +export default (userId, targetUserId) => { + validate.string(userId, 'userId') + validate.string(targetUserId, 'targetUserId') + + return User.findById(userId).lean() + .catch(error => { throw new SystemError(error.message) }) + .then(user => { + if (!user) throw new NotFoundError('user not found') + + return User.findById(targetUserId).lean() + .catch(error => { throw new SystemError(error.message) }) + }) + .then(user => { + if (!user) throw new NotFoundError('targetUser not found') + + return user.username + }) +} \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/core/logic/getUserUsername.spec.js b/staff/marti-herms/project/V-HUB/core/logic/getUserUsername.spec.js new file mode 100644 index 000000000..1676f6f6c --- /dev/null +++ b/staff/marti-herms/project/V-HUB/core/logic/getUserUsername.spec.js @@ -0,0 +1,104 @@ +import 'dotenv/config' +import mongoose from 'mongoose' +import { expect } from 'chai' + +import getUserUsername from './getUserUsername.js' +import { User } from '../data/models.js' + +import { errors } from 'com' + +const { NotFoundError, ValidationError } = errors + +describe('getUserUsername', () => { + before(() => mongoose.connect(process.env.MONGODB_URI)) + + beforeEach(() => User.deleteMany()) + + it('succeeds on existing user returning username', () => { + return User.create({ username: 'monoloco', email: 'mono@loco.com', password: '123123123' }) + .then(user => getUserUsername(user.id, user.id)) + .then(name => { + expect(name).to.be.an('string') + expect(name).to.include('monoloco') + }) + }) + + it('fails on non-existing user', () => { + let _error + + return getUserUsername('66b941110938786955ecf3b5', '66b941110938786955ecf3b5') + .catch(error => _error = error) + .finally(() => { + expect(_error).to.be.instanceOf(NotFoundError) + expect(_error.message).to.equal('user not found') + }) + }) + + it('fails on non-existing targetUser', () => { + let _error + + return User.create({ username: 'monoloco', email: 'mono@loco.com', password: '123123123' }) + .then(user => getUserUsername(user.id, '66b941110938786955ecf3b5')) + .catch(error => _error = error) + .finally(() => { + expect(_error).to.be.instanceOf(NotFoundError) + expect(_error.message).to.equal('targetUser not found') + }) + }) + + it('fails on non-string userId', () => { + let error + + try { + getUserUsername(123, 'eden') + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('userId is not a string') + } + }) + + // it('fails on invalid userId', () => { + // let error + + // try { + // getUserUsername('', 'eden') + // } catch (_error) { + // error = _error + // } finally { + // expect(error).to.be.instanceOf(ValidationError) + // expect(error.message).to.equal('invalid userId') + // } + // }) + + it('fails on non-string targetUserId', () => { + let error + + try { + getUserUsername('eden', 123) + } catch (_error) { + error = _error + } finally { + expect(error).to.be.instanceOf(ValidationError) + expect(error.message).to.equal('targetUserId is not a string') + } + }) + + // it('fails on invalid targetUserId', () => { + // let error + + // try { + // getUserUsername('eden', '') + // } catch (_error) { + // error = _error + // } finally { + // expect(error).to.be.instanceOf(ValidationError) + // expect(error.message).to.equal('invalid targetUserId') + // } + // }) + + afterEach(() => User.deleteMany()) + + after(() => mongoose.disconnect()) +}) \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/core/logic/getUserUsername.test.js b/staff/marti-herms/project/V-HUB/core/logic/getUserUsername.test.js new file mode 100644 index 000000000..3687a6ee0 --- /dev/null +++ b/staff/marti-herms/project/V-HUB/core/logic/getUserUsername.test.js @@ -0,0 +1,16 @@ +import 'dotenv/config' +import bcrypt from 'bcryptjs' + +import getUserUsername from './getUserUsername' + +import { User } from '../data/models' + +import mongoose from 'mongoose' + +mongoose.connect(process.env.MONGODB_URI) + .then(() => bcrypt.hash('123123123', 8)) + .then(hash => User.create({ username: 'eden', email: 'marti@herms.com', password: hash })) + .then(user => getUserUsername(user.id, user.id)) + .then(username => console.log('username: ', username)) + .catch(error => console.error(error)) + .finally(() => mongoose.disconnect()) \ No newline at end of file diff --git a/staff/marti-herms/project/V-HUB/core/logic/index.js b/staff/marti-herms/project/V-HUB/core/logic/index.js index a58ac9e8d..a8eef46c8 100644 --- a/staff/marti-herms/project/V-HUB/core/logic/index.js +++ b/staff/marti-herms/project/V-HUB/core/logic/index.js @@ -1,9 +1,11 @@ import authenticateUser from './authenticateUser.js' import registerUser from './registerUser.js' +import getUserUsername from './getUserUsername.js' const logic = { authenticateUser, - registerUser + registerUser, + getUserUsername } export default logic \ No newline at end of file