No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Completando la base de datos

15/29
Recursos

Aportes 39

Preguntas 7

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Reg铆strate o inicia sesi贸n para participar.

La l贸gica del upsert para determinar si llamar al insert o al update no est谩 funcionando.

Tal como est谩 el c贸digo, siempre viene informaci贸n dentro del data.id (si es registro nuevo el data.id lo estamos llenando desde el controller con el id generado por nanoid).

Como desde el controlador para cualquier caso data.id viene lleno, entonces se est谩 entrando directamente al update y nunca en el insert.

Para solventar esto yo agregu茅 un par谩metro en la funci贸n upsert que me dice si el registro es nuevo o no:

function upsert(table, data, isNew){
    if(data && isNew){
        return insert(table, data);
    }else{
        return update(table, data);
    }
}

No s茅 si hay alguna mejor forma de hacerlo.

Tuve un problema al generar el token debido a que el resultado de la query es un rowdatapacket. Lo solucione creando un nuevo objeto simple para que el metodo sign de jwt no me genere problemas.

function query(table, query){
    return new Promise((resolve, reject) => {
        connection.query(`SELECT * FROM ${table} WHERE ?`, query,(error, result)=>{
            if(error) return reject(error)
            
            // Necesario para evitar el rowdatapacket
            let output = {
                id: result[0].id,
                username: result[0].username,
                password: result[0].password
            }
            
            resolve(output, null)
        })
    })
}

Si cambiamos un poco la estructura de nuestra tabla de mysql (haciendo que el username sea 煤nico lo que es lo correcto), podr铆amos s贸lo hacer una funci贸n para el upsert:

const upsert = async (table, payload) => new Promise((resolve, reject) => {
  connection.query(`INSERT INTO ${table} SET ? ON DUPLICATE KEY UPDATE ?`, [payload, payload], (error, data) => {
    console.log('UPDATE DATA: ', data)
    if (error) {
      return reject(error)
    }
    resolve(data)
  })
})

Este fue el c贸digo con el que puede hacer la clase. Espero que les sirva. Saludos

// SE REALIZA LA CONSULTA QUE TRAIGA TODO SOBRE LA TABLA
function list(table) {
    return new Promise( (resolve, reject) => {
        connection.query(`SELECT * FROM ${table}`, (err, data) => {
            if (err) return reject(err);
            resolve(data);
        })
    })
}
// FUNCION PARA TRAER UN USUARIO CON UN ID EN ESPECIFICO
function get(table, id) {
    return new Promise((resolve, reject) => {
        console.log("ID TO BE GET: ", id);
        console.log("IN TABLE TO GET: ", table);
        connection.query(`SELECT * FROM ${table} WHERE id=${id}`, (err, data) => {
            if (err) return reject(err);
            console.log("QUERY DONE: ", data);
            resolve(data);
        })
    })
}
// FUNCION PARA INSERTAR UN USUARIO
function insert(table, data) {
    return new Promise((resolve, reject)=>{
        console.log(`GOING TO MAKE AN INSERT INTO TABLE: ${table} with data: ${data}`)
        connection.query(`INSERT INTO ${table} SET ?`, data, (err, result) => {
            if(err) {
                console.error("### ERR ###: ",err)
                return reject(err)
            } else {
                resolve(result)
            }
        })
    })
}
// FUNCION PARA ACTUALIZAR UN USUARIO
function update(table, data) {
    return new Promise((resolve, reject)=>{
        console.log("DATA TO BE UPDATED: ",data);
        connection.query(`UPDATE ${table} SET ? WHERE id= ?`,[data, data.id],(err,result)=>{
            if(err) {
                console.error("UPDATE CANNOT BE DONE: ",err)
                return reject(err)
            } else {
                console.log("UPDATE DONE: ", result)
                resolve(result)
            }
        })
    })
}

// FUNCION PARA ACTUALIZAR MODIFICAR
// LA FUNCION  upsert VA VA A HACER LA DIFERENCIA QUE ENTRE UN INSERT Y UN UPDATE
// SI LA DATA TIENE IN data.id SERA UN UPDATE POR QUE ASI VIENEN DE
// ROUTER /api/components/user/network.js
const upsert = async (table, payload) =>
  new Promise((resolve, reject) => {
    console.log("DATA TO BE UPSERT: ", payload);
    connection.query(`INSERT INTO ${table} SET ? ON DUPLICATE KEY UPDATE ?`, [payload, payload], (error, data) => {
        console.log("UPSERT DATA: ", data);
        console.log("UPDATE TABLE: ", table);
        if (error) {
            return reject(error);
        }
        resolve(data);
    });
  });

// FUNCION ASINCRONA PARA SABER SI ESTA REGISTRADO EL USUARIO
// SE GUARDA LA LISTA PARA FILTRAR
function query(table, query) {
    return new Promise((resolve, reject) => {
        console.log("DATA TO BE QUERY: ", query);
        console.log("IN TABLE: ", table);
        connection.query(`SELECT * FROM ${table} WHERE ?`, query, (err, res) => {
            console.log('QUERY RESULT: ', res)
            if (err) return reject(err);
            resolve(res[0] || null);
        })
    })
}

module.exports = {
    list,
    get,
    upsert,
    query

};

Estoy bloqueado con este error Expected 鈥減ayload鈥 to be a plain object. veo que es de jwt pero no se de porque.

estes mi auth.js

const jwt = require('jsonwebtoken')
const config = require('../config');

const secret = config.jwt.secret;

function sign(data) {
    data.id = parseInt(data.id.toString()); 
    return jwt.sign(data, secret)
}

function verify(token) {
    return jwt.verify(token, secret);
}
const check = {
    own: function(req, owner) {
       const decoded = decodeHader(req)
       console.log(decoded);    
    }
}
function getToken(auth) {
    if (!auth)
        throw new Error('No viene ningun token')
    
    if ( auth.indexOf('Bearer ') === -1 )
        throw new Error('Formato invalido');
    
    let token = auth.replace('Bearer ', '');
    return token;
}
function decodeHader(req) {
    const authorization = req.headers.authorization || '';
    const token = getToken(authorization)
    const decoded = verify(token);

    req.user = decoded;

    return decoded;
}
module.exports = {
    sign,
    check
}

Chicos hay 2 errores una vez que termina este video. Para que no pasen los mismos dolores de cabeza que yo. Les comparto los errores y su soluci贸n

Error: Expected 鈥減ayload鈥 to be a plain object
Soluci贸n: Propagar el objecto

    const login = async (username, password) => {
        let data = await store.query(TABLE, { username: username })
        
        if(!data){
            data = { password: ''}
        }

        return bcrypt.compare(password, data.password)
        .then((isValid) => {
            if(isValid){
                //TOKEN GENERATE
                **return jwt.sign({ ...data }) **         
            }else{
                throw error('Invalid information')
            }
        }).catch((err) => {
            throw error(err.message, 403)
        })
    }

*Error: ER_PARSE_ERROR: You have an error in your SQL syntax
Soluci贸n: Validar id del objeto contra la DB

const upsert = async (table, data) => {
    const row = await get(table, data.id);
    
    if (row.length === 0) {
      return insert(table, data);
    } else {
      return update(table, data);
    }
}

Llevo mas de dos horas y no encuentro el error en 鈥渂ody鈥: "Informaci贸n Invalida"
tome algunos consejos de mis compa帽eros pero no sigo en lo mismo 馃槮

**Puede ayudarte 馃悶 :
Si alguien tiene un error como el siguiente:

Error: Expected "payload" to be a plain object.
    at validate (D:\dev\project-nodejs\node_modules\jsonwebtoken\sign.js:40:11)
    at validatePayload (D:\dev\project-nodejs\node_modules\jsonwebtoken\sign.js:62:10)
    at Object.module.exports [as sign] (D:\dev\project-nodejs\node_modules\jsonwebtoken\sign.js:114:7)
    at Object.sign (D:\dev\project-nodejs\auth\index.js:9:14)
    at bcrypt.compare.then.sonIguales (D:\dev\project-nodejs\api\components\auth\controller.js:20:23)
    at <anonymous>

鉂 Esto se encuentra en el siguiente archivo: /auth/index.js en la funci贸n sign.

function sign(data) {
  return jwt.sign(data, secret);
}

鉁旓笍 Y se soluciona de la siguiente forma:

function sign(data) {
  let jsonData = JSON.parse(JSON.stringify(data));
  return jwt.sign(jsonData, secret);
}

Esto se debe a que el valor del par谩metro 鈥渄ata鈥 de la funci贸n no es serializable. A veces uno no sabe si el dato proviene de diferentes fuentes y no sabe si ser谩 un objeto simple o no, en ese caso puede resolverlo parseando los datos.

Con respecto al error que mencionaba @nancygtec:
鈥淟a l贸gica del upsert para determinar si llamar al insert o al update no est谩 funcionando鈥, debido a que el id para ambos casos siempre va ha existir.

Les presento mi soluci贸n, espero les sea de ayuda:

async function upsert(table, data) {
    let lista = await list(table);
    let data_id = [];
    for (let key in lista) {
        if (data.id === lista[key].id) {
            data_id.push(lista[key].id)
        }
    }
    // console.log(data_id.length)
    if (data_id.length > 0) {
        return update(table, data);
    } else {
        return insert(table, data);
    }
}

Pueden usar db4free o freemysqlhosting como sustituto a remotemysql

Yo tuve un error 500 porque faltaba reemplazar en el archivo auth/controller.js la referencia a la db dummy

module.exports = function (injectedStore) {
    let store = injectedStore;
    if (!store) {
        store = require('../../../store/mysql');
    }

Nota: si el id buscado para hacer update es un 鈥渧archar鈥 la query debe ser la siguiente:
.
UPDATE ${table} SET ? WHERE id = '${id}'
.
(El id buscado debe ir entre comillas 鈥榠d鈥)

Luego de terminar el v铆deo con los 煤ltimos cambios, dejo de funcionar el insert.

Problemas con la l贸gica de upsert y el ID de la tabla
Hice una peque帽a variaci贸n del c贸digo para resolver inconveniente desde el mismo componente de mysql

function upsert(table, data) {

    get(table, data.id)
        .then((result) => {
            if (result.length===0){
                return insert(table, data);
            } else {
                return update(table, data);
            }
        }).catch((err) => {
            return console.error(err)
        });
}```

Con el tema del UPSERT preferir铆a tener mas lineas de c贸digo pero garantizar la sencillez en el c贸digo. Por lo tanto creo que es mejor separar el UPDATE y el INSERT

Y para el error generado por el RowDataPacket, lo solucion茅 copiando su contenido en otra varaible:

function query(table, param){
  return new Promise((resolve, reject) => {
    connection.query(`SELECT * FROM ${table} WHERE ?`, param['query'], (error, result) => {
      if(error) return reject(error);
      const output = {
        ... result[0]
      }
      resolve(output || null);
    })
  })
}

yo tenia un problema con la promesa por lo que la modifique un poco para que me generara el codigo



async function login(username,password) {
console.log(鈥榠ngres2o鈥)
console.log(username+鈥??鈥+password)
const data= await store.query(TABLA,{username:username})
console.log(data.password)
let retorna = bcrypt.compareSync(password, data.password)
console.log(retorna)
if(retornatrue){
// return auth.sign(data)
return auth.sign(JSON.parse(JSON.stringify(data)));
}
else{
throw new Error (鈥榠nfo123 invalida鈥)
}
// bcrypt.compareSync(password, data.password)
// .then(igual=>{
// if(igual=true){
// console.log(bcrypt.compareSync(password, data.password))
// // generar token
// return auth.sign(data)
// } else{
// throw new Error (鈥榠nfo123 invalida鈥)
// };
// });

No me queda clara la ventaja de usar un s贸lo m茅todo para insertar y actualizar. Entiendo que los ORM funcionan as铆. Pero en nuestro caso, que no estamos usando ninguno, no deber铆amos cuidar la separaci贸n de responsabilidades de los m茅todos?

yo tenia un problema con la promesa por lo que la modifique un poco para que me generara el codigo

async function login(username,password) {
console.log(鈥榠ngres2o鈥)
console.log(username+鈥??鈥+password)
const data= await store.query(TABLA,{username:username})
console.log(data.password)
let retorna = bcrypt.compareSync(password, data.password)
console.log(retorna)
if(retornatrue){
// return auth.sign(data)
return auth.sign(JSON.parse(JSON.stringify(data)));
}
else{
throw new Error (鈥榠nfo123 invalida鈥)
}
// bcrypt.compareSync(password, data.password)
// .then(igual=>{
// if(igual
=true){
// console.log(bcrypt.compareSync(password, data.password))
// // generar token
// return auth.sign(data)
// } else{
// throw new Error (鈥榠nfo123 invalida鈥)
// };
// });

    // return data
}

tengo el mismo codigo del profesor y me sale este error al hacer login, a alguien mas le pasa?:

TypeError: Cannot read property 鈥榩assword鈥 of null
at Object.login (/Users/xxx/Documents/sites/node/proyecto-backend-node-platzi/api/components/auth/controller.js:15:46)

si sucede el problema de informaci贸n invalida corrigan este archivo

componenst\auth\network.js

const express = require('express');

const response = require('../../../network/response');
const Controller = require('./index');

const router = express.Router();


router.post('/login', function(req, res, next) {
    Controller.login(req.body.username, req.body.password)
        .then(token => {
            response.success(req, res, token, 200);
        })
        .catch(next);
})

module.exports = router;

Esto te ayudar谩 en futuras lecciones
Para los que tengan problemas con,

if (data && data.id) {
return insert(table, data);
} else {
return update(table, data);
}

Siempre estamos pasando un ID ya sea para actualizar o insertar, por ende siempre nos dar谩 true. Para evitar este error hay muchas soluciones la m铆a fue a帽adir un elemento a los datos que se manda de nombre type cuyo valor puede ser insert o update.

Les dejo el c贸digo para la funci贸n de upsert() en el archivo mysql.js

// Guardamos el type
let type = data["type"];
// Lo eliminamos del diccionario
delete data["type"];

// Comprobamos
if (type == "insert") {
// Agregamos un usuario
return insert(table, data);
} else {
// Actualizamos un usuario
    return update(table, data);
} 

Cre茅 una soluci贸n del upsert en una misma funci贸n

 function upsert(table, param) {
  return new Promise((resolve, reject) => {
    if (!param.data.id){
      connection.query(`INSERT INTO ${table} 
                        SET ?`, param.data, (error, result) => {
                        if (error) return reject(error);
                        resolve(result);
                      })
    }
    else{
      connection.query(`UPDATE ${table} 
      SET ? WHERE id=?`, [param.data, param.data.id], (error, result) => {
      if (error) return reject(error);
      resolve(result);
    })
    }
  }) 
 }

Quien me pueda ayudar con este erro.!

Cuando hago el agregar usuario me sale este error

code: 鈥楨R_BAD_FIELD_ERROR鈥,
errno: 1054,
sqlMessage: 鈥淯nknown column 鈥榩assword鈥 in 鈥榝ield list鈥欌,
sqlState: 鈥42S22鈥,
index: 0,
sql: 鈥淚NSERT INTO users SET id = 鈥楧Ker99SDm6Rxl2of1-vKW鈥, name = 鈥榡uan鈥, username = 鈥楯uanTeix鈥, password = 鈥1234鈥欌

que informacion mandan en el login de postman? me regresa informacion invalida

Para solucionar el error lo que hice fue tomar el objeto creado en el middleware de secure (req.user) pasarlo por par谩metro a la funci贸n upsert del controlador

si depronto el codigo de: raparisg que esta abajo no les sirve

async function upsert(table, data) {
console.log(table)
console.log(data.id)

const row = await get(table, data.id);
console.log(row)
if (row.length === 0) {
  return insert(table, data);
} else {
  return update(table, data);
}

}

y les sale un error de sql es por que tienen que poner en el get la consulta entre coomillas

function get(table, id) {
return new Promise((resolve, reject) => {
connection.query(SELECT * FROM ${table} WHERE id="${id}", (err, data) => {
if (err) return reject(err);
resolve(data);
})
})
}```

Esta clase si estuvo fuerte, casi no la logro. Pero ya se pudo.

Hola Compa帽eros, comentando algo que me paso es que el remotemysql no me funcionaba correctamente, les recomiendo hacer la practica en modo local con xampp, o si tienen alg煤n servidor seria mejor.

Amigos alguien me puede ayudar con este error

[error] TypeError: Cannot read property 鈥榩assword鈥 of null
at Object.login (/home/gerry/Projects/Practico_node/api/components/auth/controller.js:19:26)
at processTicksAndRejection

Grupo de aprendizaje para temas relacionados a Node js: https://chat.whatsapp.com/CrXPoxll2VMA4Jo7dJyP1w

Si cuando hacen el put les sale un error. Aseg煤rence de primero hacer una petici贸n put con exactamente los mismos datos con los que hacen el login (id, name, username y password). Ya que hicieron eso, ahora si pueden modificar.

Tenia un problema con el return en el Query me marcaba undefinend, la t茅cnica que utilice para solucionarlo fue destructuring mediante el paso a otro objeto let user = {鈥es[0]};

function query(table, query) {
  return new Promise((resolve, reject) => {
    connection.query(`SELECT * FROM ${table} WHERE ?`, query, (err, res) => {
        let user = {...res[0]};
        if (err) return reject(err);
        resolve(user || null);
    })
  })
}```

Convertir RowData a Objeto

function query(table, query) {
  return new Promise((resolve, reject) => {
      connection.query(`SELECT * FROM ${table} WHERE ?`, query, (err, res) => {
          if (err) return reject(err);
          var responseObject = res.map((result, index) => {
            return Object.assign({}, result);
          });
          resolve(responseObject[0] || null);
      })
  })
}```

https://remotemysql.com/ esta caido. que podemos hacer?

Compa帽eras y compa帽eros, cuidado con lo siguiente:

Al parecer, cuando se le hace la query a la base de datos, me pas贸 que me devolvi贸 la data del usuario envuelta en un objeto de tipo RowDataPacket y ese tipo de objetos no son recibidos por la funci贸n sign de jsonwebtoken. 驴C贸mo se soluciona? Muy f谩cil, en deben utilizar el Spread Operator de JavaScript, con lo que en la parte de la funci贸n login del componente auth que hace la query queda as铆:

const userRaw = await store.query(TABLE, {username: username})
 userData = {...userRaw}

En este link queda una explicaci贸n mejor del spread operator.

Espero haber sido de ayuda.

Tuve problemas con el insert y update, y lo correg铆 de la siguiente forma para que no sea necesario enviar todos los campos a la hora de actualizar.

Network.js User

router.post("/", (req, res, next) => {
  Controller.upsert(req.body,true)
    .then((user) => {
      response.success(req, res, user, 201);
    })
    .catch(next);
});
router.put("/", secure("update"), (req, res,next) => {
  Controller.upsert(req.body,false)
    .then((user) => {
      response.success(req, res, user, 201);
    })
    .catch(next);
});

controller.js User

async function upsert(body, isNew) {
    const authUser = {};
    const user = {};
    if (isNew) {
      user.id = uuid.v4();
    } else {
      user.id = body.id;
      authUser.id= body.id;
    }

    if (body.username) {
      user.username = body.username;
      authUser.username = body.username;
    }
    if (body.name) {
      user.name = body.name;
      authUser.name = body.name;
    }
    if (body.password) {
      authUser.password = body.password;
    }

    //crear o editar datos de tabla auth
    await auth.upsert(authUser, isNew);
    //crear o editar datos de tabla user
    return store.upsert(TABLA, user, isNew);
  }

constroller.js Auth

 async function upsert(data,isNew) {
    const authData = {
      id: data.id,
    };
   
    if (data.username) {
      authData.username = data.username;
    }
    if (data.password) {
      authData.password = await bcrypt.hash(data.password,8 );
    }
    return store.upsert(TABLA, authData, isNew);
  }

mysql.js Store

async function upsert(table, data, isNew) {
 
  if (data && isNew) {
    return insert(table, data);
  } else {
    return update(table, data);
  }
}

Tuve el problema de que al cambiar a la base de datos de mysql no me estaba generando el token, y eso es por que la funcion query() retorna un objeto con una firma

RowDataPacket {
  id: 'HmaZav2Flmfk9GWV56oCx',
  username: 'username',
  passwd: '$2b$08$aum2gRBawvQD5N2coHLrx.xKpPpMwst1OhftAKaKfl96Py/mhIu.C'
}

y eso evita que webtoken pueda generar el token, basta con ir al index.js de auth, en la funcion sing queda as铆:

function sing(data){
	return jwt.sign(JSON.stringify(data), config.auth.palabraSecreta)
}

En el mundo profesional las operaciones de base de datos van dentro de transacciones