Git Product home page Git Product logo

waldohidalgo / banco_solar Goto Github PK

View Code? Open in Web Editor NEW
0.0 1.0 0.0 2.28 MB

Repositorio con el código solución de la prueba Banco Solar con la que se finaliza el módulo 7 Acceso a datos en aplicaciones Node

Home Page: https://banco-solar.onrender.com/

CSS 11.10% SCSS 4.62% JavaScript 52.60% HTML 31.68%
axios bootstrap-icons bootstrap5 desafiolatam express javascript jquery momentjs pg postgresql scss sweetalert2 typewriter-effect bancosolar

banco_solar's Introduction

Solución a la Prueba Banco Solar

Repositorio con el código solución a la prueba Banco Solar con la que se finaliza el módulo 7: Acceso a datos en aplicaciones Node de la beca otorgada por Talento Digital para Chile Desarrollo de aplicaciones Full Stack Javascript Trainee dictada por Desafío Latam.

He ido muchísimo más allá de los requerimientos del proyecto con la intención de autodesafiarme en implementar ideas que considero interesantes para complementar el proyecto. He modificado por completo el front end que se nos ha entregado al inicio, con la intención de personalizarlo en un 100% y extenderlo, tal y como lo he hecho, llegando al punto de ser el front end un "espejo" del back end y lograr manipular sin problemas varias rutas (9 queries a la base de datos y 9 fetch a rutas que manejan dichas queries).

Tabla de Contenidos

Requisitos

Requisitos Hoja 1 y Hoja 2 Requisitos Hoja 3

Librerias Utilizadas

Tecnologías Utilizadas
JQuery
Bootstrap
SCSS
Axios
Moment
Bootstrap-icons
Express
pg
Typewriter-effect
Sweetalert2

Deploy

Mi proyecto es completamente funcional y lo he desplegado en Render.com en la siguiente URL: Banco Solar de Waldo Hidalgo

Proyecto Final 100% Responsivo

Proyecto Final Big Devices

Proyecto Final Big Devices

Proyecto Final Small Devices

En pantallas small, las tablas permiten ser visualizadas por completo ya que el overflow presenta scrollbars:

Proyecto Final Small Devices

Diagrama de Flujo

1. Creación de Usuario

1.1. Alerta de creación de Usuario ya registrado

Se impide la creación de usuarios con mismo nombre:

Alerta de creación de Usuario ya registrado

1.2. Alerta creación de Usuario exitosa

Se procede a crear un usuario de nombre Daniela Perez con saldo igual a 500000:

Alerta creación de Usuario exitosa

1.3 Creación de usuario exitosa en el front end

Creación de usuario exitosa en el front end

1.4. Creación de usuario exitosa en el back end

Creación de usuario exitosa en el back end

2. Edición de Usuario

2.1. UI Edición de Usuario

UI Edición de Usuario

2.2. Alerta Edición exitosa de usuario

Alerta Edición exitosa de usuario

2.3. Edición Exitosa en el front end

Edición Exitosa en el front end

2.4. Edición Exitosa en el back end

Edición Exitosa en el back end

3. Transferencia

3.1. Alerta transferencia entre mismos usuarios

Alerta transferencia entre mismos usuarios

3.2. Error transferencia con saldos insuficientes

Error transferencia con saldos insuficientes

3.3. Alerta de Transferencia exitosa

Se procede a realizar una transferencia desde el usuario Daniela Perez al usuario Waldo Hidalgo por el monto de 500000:

Alerta de Transferencia exitosa

3.4. Transferencia exitosa en el front end

Transferencia exitosa en el front end

3.5. Transferencia exitosa en el back end

Transferencia exitosa en el back end

4. Eliminación de Usuario

Deseo eliminar el usuario Daniela Perez, sin embargo, el usuario presenta transferencias hechas por lo cual se procede a eliminar transferencias realizadas por el usuario y hacia el usuario para posteriormente eliminar al usuario:

4.1. Alerta de imposibilidad de eliminación de usuario con transferencias hechas

Si se desea eliminar un usuario con transferencias hechas se genera un error de violación de la integridad referencial ya que la tabla de hechos presenta claves foraneas con la tabla de dimensiones:

Alerta de imposibilidad de eliminación de usuario con transferencias hechas

4.2. Alerta eliminación exitosa de transferencias con clave foranea al usuario que se desea eliminar

Eliminación de transferencias con clave foranea al usuario que se desea eliminar

4.3. Transferencia eliminada con éxito en el front end

Transferencia eliminada con éxito en el front end

4.4. Transferencia eliminada con éxito en el back end

Transferencia eliminada con éxito en el back end

Una vez eliminadas todas las transferencias asociadas al usuario Daniela Perez es posible eliminar a dicho usuario con éxito:

4.5. Alerta eliminación usuario exitosa

Alerta eliminación usuario exitosa

4.6. Eliminación de usuario exitosa en el front end

Eliminación de usuario exitosa en el front end

4.7. Eliminación de usuario exitosa en el back end

Eliminación de usuario exitosa en el back end

5. Página Error 404

Página Error 404

Soluciones

1.Utilizar el paquete pg para conectarse a PostgreSQL y realizar consultas DML para la gestión y persistencia de datos. (3 Puntos)

Se ha utilizado el paquete pg para realizar 9 queries a la base de datos:

import pool from "../config/db.js";

export async function getUsersQuery() {
  try {
    const response = await pool.query("SELECT * FROM bancosolar_usuarios");

    return response.rows;
  } catch (error) {
    return error;
  }
}

export async function addUserQuery(data) {
  try {
    const response = await pool.query(
      "INSERT INTO bancosolar_usuarios  (nombre, balance) VALUES ($1, $2) RETURNING *",
      data,
    );
    if (response.rowCount === 0) {
      throw new Error("No se pudo agregar el usuario");
    }
    return response.rows[0];
  } catch (error) {
    return error;
  }
}

export async function deleteUserQuery(id) {
  try {
    const response = await pool.query(
      "DELETE FROM bancosolar_usuarios  WHERE id = $1 RETURNING *",
      [id],
    );
    if (response.rowCount === 0) {
      throw new Error("No se pudo eliminar el usuario");
    }
    return response.rows[0];
  } catch (error) {
    return error;
  }
}

export async function editUserQuery(data) {
  try {
    const response = await pool.query(
      "UPDATE bancosolar_usuarios SET nombre = $2, balance = $3 WHERE id = $1 RETURNING *",
      data,
    );
    if (response.rowCount === 0) {
      throw new Error("No se pudo editar el usuario");
    }
    return response.rows[0];
  } catch (error) {
    return error;
  }
}

export async function getTransferenciasQuery() {
  try {
    const response = await pool.query(
      "SELECT * FROM bancosolar_transferencias",
    );

    return response.rows;
  } catch (error) {
    return error;
  }
}

export async function postAddTransferenciaQuery(data) {
  const client = await pool.connect();

  try {
    const { emisor, receptor, monto, fecha } = data;
    await client.query("BEGIN");
    const queryTransferenciaEmisor = {
      text: "update bancosolar_usuarios  set balance = balance - $2 where id = $1 returning *",
      values: [emisor, monto],
    };
    const responseEmisor = await client.query(queryTransferenciaEmisor);
    if (responseEmisor.rowCount === 0) {
      throw new Error("No se pudo realizar la transferencia");
    }

    const queryTransferenciaReceptor = {
      text: "update bancosolar_usuarios  set balance = balance + $2 where id = $1 returning *",
      values: [receptor, monto],
    };

    const responseReceptor = await client.query(queryTransferenciaReceptor);
    if (responseReceptor.rowCount === 0) {
      throw new Error("No se pudo realizar la transferencia");
    }
    const queryTransferencia = {
      text: "INSERT INTO bancosolar_transferencias (emisor, receptor, monto, fecha) VALUES ($1, $2, $3, $4) returning *",
      values: [emisor, receptor, monto, fecha],
    };
    const responseTransferencia = await client.query(queryTransferencia);
    if (responseTransferencia.rowCount === 0) {
      throw new Error("No se pudo realizar la transferencia");
    }

    await client.query("COMMIT");
  } catch (e) {
    console.log(e.message);
    await client.query("ROLLBACK");
    throw e;
  } finally {
    client.release();
  }
}

export async function deleteTransferenciaQuery(id) {
  try {
    const response = await pool.query(
      "DELETE FROM bancosolar_transferencias WHERE id = $1 RETURNING *",
      [id],
    );
    if (response.rowCount === 0) {
      throw new Error("No se pudo eliminar la transferencia");
    }
    return response.rows[0];
  } catch (error) {
    return error;
  }
}

export async function getComisionesQuery() {
  const comision = 300;
  try {
    const query = {
      text: `select j.id,j.nombre,sum(j.monto ) as subtotal_transferencias ,sum(j.subtotal_ganancias) as subtotal_comisiones
      from
      (select u.id,u.nombre,t.monto,
      case when t.monto<1000 then 0 else $1 end AS subtotal_ganancias 
      from bancosolar_transferencias t
      INNER JOIN 
      bancosolar_usuarios u ON t.emisor = u.id) j
        group by j.id,j.nombre;
  `,
      values: [comision],
    };
    const response = await pool.query(query);

    return response.rows;
  } catch (error) {
    return error;
  }
}

export async function resetDataQuery() {
  const client = await pool.connect();
  try {
    await client.query("BEGIN");

    await client.query("delete from bancosolar_transferencias");

    await client.query(
      "ALTER SEQUENCE bancosolar_transferencias_id_seq RESTART WITH 1",
    );

    await client.query("delete from bancosolar_usuarios");

    await client.query(
      "ALTER SEQUENCE bancosolar_usuarios_id_seq RESTART WITH 1",
    );

    await client.query(`insert into 
    bancosolar_usuarios(nombre,balance)
    values ('Waldo Hidalgo',1000000),
    ('John Doe',1200000),
    ('Juan Bravo',800000),
    ('Karina Perez',500000)`);

    await client.query(`insert into 
    bancosolar_transferencias 
    (emisor,receptor,monto,fecha)
    values
    (1,2,400000,'2024-04-14 19:31:53.000'),
    (2,3,50000,'2022-04-14 19:32:29.000')`);

    await client.query("COMMIT");
  } catch (e) {
    console.log(e.message);
    await client.query("ROLLBACK");
    throw e;
  } finally {
    client.release();
  }
}

2.Usar transacciones SQL para realizar el registro de las transferencias. (2 Puntos)

A continuación muestro el código que permite realizar transferencias en el cual utilizo transacciones SQL:

export async function postAddTransferenciaQuery(data) {
  const client = await pool.connect();

  try {
    const { emisor, receptor, monto, fecha } = data;
    await client.query("BEGIN");
    const queryTransferenciaEmisor = {
      text: "update bancosolar_usuarios  set balance = balance - $2 where id = $1 returning *",
      values: [emisor, monto],
    };
    const responseEmisor = await client.query(queryTransferenciaEmisor);
    if (responseEmisor.rowCount === 0) {
      throw new Error("No se pudo realizar la transferencia");
    }

    const queryTransferenciaReceptor = {
      text: "update bancosolar_usuarios  set balance = balance + $2 where id = $1 returning *",
      values: [receptor, monto],
    };

    const responseReceptor = await client.query(queryTransferenciaReceptor);
    if (responseReceptor.rowCount === 0) {
      throw new Error("No se pudo realizar la transferencia");
    }
    const queryTransferencia = {
      text: "INSERT INTO bancosolar_transferencias (emisor, receptor, monto, fecha) VALUES ($1, $2, $3, $4) returning *",
      values: [emisor, receptor, monto, fecha],
    };
    const responseTransferencia = await client.query(queryTransferencia);
    if (responseTransferencia.rowCount === 0) {
      throw new Error("No se pudo realizar la transferencia");
    }

    await client.query("COMMIT");
  } catch (e) {
    console.log(e.message);
    await client.query("ROLLBACK");
    throw e;
  } finally {
    client.release();
  }
}

3.Servir una API RESTful en el servidor con los datos de los usuarios almacenados en PostgreSQL. (3 Puntos)

Se sirve la API REST en la siguiente ruta:

router.get("/usuarios", getUsers);

la cual utiliza la función getUsers siguiente:

export async function getUsers(req, res) {
  try {
    const users = await getUsersQuery();
    res.status(200).send(users);
  } catch (error) {
    res.status(500).send(error.message);
  }
}

La que a su vez utiliza la siguiente función getUsersQuery:

export async function getUsersQuery() {
  try {
    const response = await pool.query("SELECT * FROM bancosolar_usuarios");

    return response.rows;
  } catch (error) {
    return error;
  }
}

A continuación muestro un screenshot de la API sirviendo con éxito:

API sirviendo data con éxito

4.Capturar los posibles errores que puedan ocurrir a través de bloques catch o parámetros de funciones callbacks para condicionar las funciones del servidor. (1 Punto)

A continuación muestro los 9 controllers manejando los errores mediante bloques trycatch:

export async function getUsers(req, res) {
  try {
    const users = await getUsersQuery();
    res.status(200).send(users);
  } catch (error) {
    res.status(500).send(error.message);
  }
}

export async function addUser(req, res) {
  try {
    const data = req.body;

    const result = await addUserQuery([data.nombre, data.balance]);

    res.status(200).send(data);
  } catch (error) {
    res.status(500).send(error.message);
  }
}

export async function deleteUser(req, res) {
  try {
    const { id } = req.query;
    const result = await deleteUserQuery(id);
    res.status(200).send(result);
  } catch (error) {
    res.status(500).send(error.message);
  }
}

export async function editUser(req, res) {
  try {
    const { id } = req.query;
    const data = req.body;

    const result = await editUserQuery([id, data.nombre, data.balance]);
    res.status(200).send(result);
  } catch (error) {
    res.status(500).send(error.message);
  }
}

export async function getTransferencias(req, res) {
  try {
    const transferencias = await getTransferenciasQuery();
    res.status(200).send(transferencias);
  } catch (error) {
    res.status(500).send(error.message);
  }
}

export async function postAddTransferencia(req, res) {
  try {
    const data = req.body;
    const result = await postAddTransferenciaQuery(data);
    res.status(200).send(result);
  } catch (error) {
    res.status(500).send(error);
  }
}

export async function deleteTransferencia(req, res) {
  try {
    const { id } = req.query;
    const result = await deleteTransferenciaQuery(id);
    res.status(200).send(result);
  } catch (error) {
    res.status(500).send(error.message);
  }
}

export async function getComisiones(req, res) {
  try {
    const comisiones = await getComisionesQuery();

    res.status(200).json(comisiones);
  } catch (error) {
    res.status(500).send(error.message);
  }
}

export async function resetData(req, res) {
  try {
    await resetDataQuery();
    res.status(200).send("Data Reseteada");
  } catch (error) {
    res.status(500).send(error.message);
  }
}

5.Devolver correctamente los códigos de estado según las diferentes situaciones. (1 Punto)

Se han manejado todos los posibles errores y como ejemplo a continuación muestro el manejo del error 23503 el cual se origina cuando se desea eliminar un usuario con transferencias hechas (error generado por una FOREIGN KEY VIOLATION). Dicho error se maneja mostrando una alerta la cual he mostrado en la sección 4.1. Alerta de imposibilidad de eliminación de usuario con transferencias hechas:

if (response.data.code === "23503") {
  Swal.fire({
    title: "Advertencia",
    text: "No se puede eliminar un usuario que tenga transferencias hechas",
    icon: "warning",
  });
  return;
}

¡Extra!: Reseteo de data mediante ruta y mediante intervalo de 30 minutos

Disponibilizo la ruta reset que permite eliminar toda la data de las tablas utilizadas en la base de datos y crear una data por defecto:

router.get("/reset", resetData);

La cual hace uso de la función resetData:

export async function resetData(req, res) {
  try {
    await resetDataQuery();
    res.status(200).send("Data Reseteada");
  } catch (error) {
    res.status(500).send(error.message);
  }
}

La cual hace uso del siguiente script SQL:

export async function resetDataQuery() {
  const client = await pool.connect();
  try {
    await client.query("BEGIN");

    await client.query("delete from bancosolar_transferencias");

    await client.query(
      "ALTER SEQUENCE bancosolar_transferencias_id_seq RESTART WITH 1",
    );

    await client.query("delete from bancosolar_usuarios");

    await client.query(
      "ALTER SEQUENCE bancosolar_usuarios_id_seq RESTART WITH 1",
    );

    await client.query(`insert into 
    bancosolar_usuarios(nombre,balance)
    values ('Waldo Hidalgo',1000000),
    ('John Doe',1200000),
    ('Juan Bravo',800000),
    ('Karina Perez',500000)`);

    await client.query(`insert into 
    bancosolar_transferencias 
    (emisor,receptor,monto,fecha)
    values
    (1,2,400000,'2024-04-14 19:31:53.000'),
    (2,3,50000,'2022-04-14 19:32:29.000')`);

    await client.query("COMMIT");
    return "exito";
  } catch (e) {
    console.log(e.message);
    await client.query("ROLLBACK");
    throw e;
  } finally {
    client.release();
  }
}

También he creado el siguiente intervalo el cual permite el reseteo de data y creación de data por defecto cada 30 minutos:

setInterval(async () => {
  try {
    const response = await resetDataQuery();

    if (response === "exito") {
      console.log("Se reinicio el servidor exitosamente");
      return;
    } else {
      throw new Error("No se pudo reiniciar el servidor");
    }
  } catch (error) {
    console.error("Error al llamar a la ruta:", error);
  }
}, 1800000);

banco_solar's People

Contributors

waldohidalgo avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.