Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/fullstack #265

Open
wants to merge 24 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
85de01b
create fullstack folder; bring app from frontend and api from backend
juditta99 Jan 7, 2024
ea55248
bring in my version of backend api folder
juditta99 Jan 16, 2024
fbcf9dd
branch reset
juditta99 Jan 16, 2024
e0d4b60
bring latest versions of app and api, take 2
juditta99 Jan 16, 2024
df68bf4
integrate app and api #263
juditta99 Jan 17, 2024
ba70dd5
add retrieve posts logic and endpoint, and integration with app #263
juditta99 Jan 18, 2024
75eab0b
refactor app logic into separate files and context; implement toggle …
juditta99 Jan 21, 2024
ff619db
clean up api folder #268
juditta99 Jan 22, 2024
7bc97c7
integrate new api mongo-connected and add retrieve and create post lo…
juditta99 Jan 25, 2024
2a4e515
add toggle like post logic in api with mongoose #269
juditta99 Jan 31, 2024
2ab2267
implement toggle fav posts and retrieve fav posts in api and integrat…
juditta99 Jan 31, 2024
6987694
refactor api handlers, validators; move settings to file in api; refa…
juditta99 Feb 2, 2024
b5a47f6
add auomated test in logic (api) for authenticate user #322
juditta99 Feb 4, 2024
ea36e90
move api to esm; repair authenticate user spec #268
juditta99 Feb 6, 2024
9645775
update specs in api for authenticate and register user logics #322
juditta99 Feb 9, 2024
af5cd3e
refactor authenticate user, register user, retrieve user and create p…
juditta99 Feb 12, 2024
1fab92c
implement automated tests for create post, retrieve posts, toggle fav…
juditta99 Feb 14, 2024
78eba30
add jwt protection in api #364
juditta99 Feb 15, 2024
aef4e32
finish adding jwt to retrieve user endpoint ##364
juditta99 Feb 15, 2024
c741ec1
add customised error handling in app logic #374
juditta99 Feb 17, 2024
51c6924
add Feedback component in App to handle errors on screen #374
juditta99 Feb 17, 2024
977e831
move validate and errors into their own package #268
juditta99 Feb 19, 2024
8343bd9
implement app routes for login, register and home #395; implement use…
juditta99 Feb 20, 2024
c846ff3
add home sub-routes for profile and posts #395
juditta99 Feb 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions staff/judy-grayland/fullstack/api/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
MONGODB_URL = mongodb://127.0.0.1:27017/test
MONGODB_URL_TEST = mongodb://127.0.0.1:27017/temp
PORT = 8000
JWT_SECRET = no me gusta el cilantro
JWT_EXP = 5s
1 change: 1 addition & 0 deletions staff/judy-grayland/fullstack/api/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!.env
31 changes: 31 additions & 0 deletions staff/judy-grayland/fullstack/api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# API

## Register user

- Request: POST /users "Content-Type: application/json" { name, email, password }
- Response: 201
- Response (error): 400|409|500 "Content-Type: application/json" { error, message }

## Authenticate user

- Request: POST /users/auth "Content-Type: application/json" { email, password }
- Response: 200 "Content-Type: application/json" userId
- Response (error): 400 "Content-Type: application/json" { error, message }

## Retrieve user

- Request: GET /users "Authorization: Bearer userId"
- Response: 200 "Content-Type: application/json" { name }
- Response (error): 400 "Content-Type: application/json" { error, message }

## Create post

- Request: POST /posts "Authorization: Bearer userId" "Content-Type: application/json" { image, text }
- Response: 201
- Response (error): 400 "Content-Type: application/json" { error, message }

## Toggle like post

- Request: PATCH /posts/postId/likes "Authorization: Bearer userId"
- Response: 204
- Response (error): 400|404|406|500 "Content-Type: application/json" { error, message }
69 changes: 69 additions & 0 deletions staff/judy-grayland/fullstack/api/data/demo_mongodriver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
const mongodb = require('mongodb')

// Para conectarnos a la bbdd, nos traemos la clase MongoClient, y lo instanciamos. MongoClient es una clase que está dentro del objeto mongodb. Aquí estamos destructurando. MongoClient nos sirve para crear un cliente como Mongosh. Es decir nos vamos a conectar desde aquí con node, en vez de através de mongosh en la terminal.
// Para hacer un updateOne, necesitamos el ObjectId. Como es una función de Mongo, nos la traemos aquí:
const { MongoClient, ObjectId } = mongodb

// le pasamos la ruta de la conexión: 1) usamos el protocolo de mongo (en vez de http)). 2)la dirección de nuestra máquina. Localhost no funciona siempre desde VSC, así que mejor poner la dirección ip: 127.0.0.1, y 3)El puerto en que suele abrirse Mongo es: 27017
const client = new MongoClient('mongodb://127.0.0.1:27017')

// nos conectamos así con client.connect y un try catch. Si nos conectamos succesffuly, en el callback del then recibimos el connector, con el que podemos decir que usemos el test y luego la colección users
client
.connect()
.then((connector) => {
const db = connector.db('test')

const users = db.collection('users')
const posts = db.collection('posts')

// users
// .insertOne({
// name: 'Pata Tus',
// email: '[email protected]',
// password: 'aaa',
// favs: [],
// })
// .then(result => console.log('inserted', result))
// .catch((error) => console.error(error))

// users
// .updateOne(
// { _id: new ObjectId('65acf7e1f0bad8bad5bcdd9e') },
// { $set: { name: 'Pata Tin', email: '[email protected]' } }
// )
// .then(result => console.log('updated', result))
// .catch((error) => console.error(error))

// users
// .findOne({ _id: new ObjectId('65acf7e1f0bad8bad5bcdd9e') })
// .then((result) => console.log('found', result))
// .catch((error) => console.error(error))

// users
// .deleteOne({ _id: new ObjectId('659be6460093e6bdc0b0ad0e') })
// .then((result) => console.log('deleted', result))
// .catch((error) => console.error(error))

// users
// .find().toArray()
// .then((result) => console.log('found all', result))
// .catch((error) => console.error(error))

// posts
// .insertOne({
// author: new ObjectId('65acf7e1f0bad8bad5bcdd9e'),
// image:
// 'https://valenciafruits.com/wp-content/uploads/2023/10/Calabacin-01-1024x820.jpg',
// text: 'Time to grow!',
// likes: [],
// })
// .then((result) => console.log('inserted', result))
// .catch((error) => console.error(error))

// posts
// .find({ author: new ObjectId('65acf7e1f0bad8bad5bcdd9e') })
// .toArray()
// .then((result) => console.log('found posts', result))
// .catch((error) => console.error(error))
})
.catch((error) => console.error(error))
53 changes: 53 additions & 0 deletions staff/judy-grayland/fullstack/api/data/demo_mongoose.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import mongoose from 'mongoose'

import { User, Post } from './models.js'
// en la propia dirección podemos indicar la bbdd a la que queremos conectarnos - test
mongoose
.connect('mongodb://127.0.0.1:27017/test')
.then(() => {
// // CREAR USUARIO
// // creamos el usuario en memoria guardándolo como una variable. Para guardarlo en la BBDD hacemos .save() después:
// const ajo = new User({ name: 'A Jo', email: '[email protected]', password: 'aaa' })
// ajo
// .save()
// .then(() => console.log('user created'))
// .catch((error) => console.error(error))

// CREAR POST
// const post = new Post({
// author: '65ad62b7b958109e79915778',
// image:
// 'https://content.fortune.com/wp-content/uploads/2023/08/Barbie-Dancing-MCDBARB_WB048.jpg',
// text: "Let's dance the night away!",
// })

// post
// .save()
// .then(() => console.log('post published'))
// .catch((error) => console.error(error))

// // DARLE LIKE A UN POST:
// Post.findById('65ad69d88ce623d0fdd7cdc6')
// .then((post) => {
// post.likes.push('65acf7e1f0bad8bad5bcdd9e')

// post
// .save()
// .then(() => console.log('post liked'))
// .catch((error) => console.error(error))
// })
// .catch((error) => console.error(error))

// GUARDAR POST COMO FAV
User.findById('65acf7e1f0bad8bad5bcdd9e')
.then((user) => {
user.favs.push('65ad69d88ce623d0fdd7cdc6')

user
.save()
.then(() => console.log('post favd'))
.catch((error) => console.error(error))
})
.catch((error) => console.error(error))
})
.catch((error) => console.error(error))
53 changes: 53 additions & 0 deletions staff/judy-grayland/fullstack/api/data/models.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import mongoose from 'mongoose'

const { Schema, model, ObjectId } = mongoose

const user = new Schema({
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
minlength: 3,
},
favs: [
{
type: ObjectId,
ref: 'Post',
},
],
})

// el ref lo ponemos para decirle a qué colección pertenece. El Id pertenece a los usuarios. Así relacionamos que el autor viene de la colección usuarios.
const post = new Schema({
author: {
type: ObjectId,
required: true,
ref: 'User',
},
image: {
type: String,
required: true,
},
text: {
type: String,
required: true,
},
likes: [
{
type: ObjectId,
ref: 'User',
},
],
})
const User = model('User', user)
const Post = model('Post', post)

export { User, Post }
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import jwt from 'jsonwebtoken'
import logic from '../logic/index.js'

import { errors } from 'com'
const { NotFoundError, CredentialsError, ContentError } = errors

export default (req, res) => {
try {
const { email, password } = req.body

logic
.authenticateUser(email, password)
.then((userId) => {
const token = jwt.sign({ sub: userId }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXP,
})
res.json(token)
})
.catch((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 })
})
} catch (error) {
let status = 500

if (error instanceof ContentError || error instanceof TypeError) {
status = 406
}
res
.status(status)
.json({ error: error.constructor.name, message: error.message })
}
}
47 changes: 47 additions & 0 deletions staff/judy-grayland/fullstack/api/handlers/createPostHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import jwt from 'jsonwebtoken'
const { JsonWebTokenError } = jwt

import logic from '../logic/index.js'

import { errors } from 'com'
const { NotFoundError, TokenError, ContentError } = errors

export default (req, res) => {
try {
const token = req.headers.authorization.substring(7)

const { sub: userId } = jwt.verify(token, process.env.JWT_SECRET)

const { image, text } = req.body

logic
.createPost(userId, image, text)
.then(() => {
res.status(201).send()
})
.catch((error) => {
let status = 500

if (error instanceof NotFoundError) {
status = 404
}

res
.status(status)
.json({ error: error.constructor.name, message: error.message })
})
} catch (error) {
let status = 500

if (error instanceof ContentError || error instanceof TypeError) {
status = 406
} else if (error instanceof JsonWebTokenError) {
status = 401

error = new TokenError(error.message)
}
res
.status(status)
.json({ error: error.constructor.name, message: error.message })
}
}
20 changes: 20 additions & 0 deletions staff/judy-grayland/fullstack/api/handlers/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import registerUserHandler from './registerUserHandler.js'
import authenticateUserHandler from './authenticateUserHandler.js'
import retrieveUserHandler from './retrieveUserHandler.js'
import retrievePostsHandler from './retrievePostsHandler.js'
import retrieveFavPostsHandler from './retrieveFavPostsHandler.js'
import createPostHandler from './createPostHandler.js'
import toggleLikePostHandler from './toggleLikePostHandler.js'
import toggleFavPostHandler from './toggleFavPostHandler.js'

// no estás exportando un objeto, es un scope, un bloque. Esto te permite luego hace destructuring cuando lo importas en las lógicas
export {
registerUserHandler,
authenticateUserHandler,
retrieveUserHandler,
retrievePostsHandler,
retrieveFavPostsHandler,
createPostHandler,
toggleLikePostHandler,
toggleFavPostHandler,
}
35 changes: 35 additions & 0 deletions staff/judy-grayland/fullstack/api/handlers/registerUserHandler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import logic from '../logic/index.js'

import { errors } from 'com'
const { DuplicityError, ContentError } = errors

export default (req, res) => {
try {
const { name, email, password } = req.body

logic
.registerUser(name, email, password)
.then(() => res.status(201).send())
.catch((error) => {
let status = 500

if (error instanceof DuplicityError) {
status = 409
}
res
.status(status)
.json({ error: error.constructor.name, message: error.message })

return
})
} catch (error) {
let status = 500

if (error instanceof ContentError || error instanceof TypeError) {
status = 406
}
res
.status(status)
.json({ error: error.constructor.name, message: error.message })
}
}
Loading