Skip to content

Commit

Permalink
Feat: CRUD functionality (#11)
Browse files Browse the repository at this point in the history
Co-authored-by: Daniel Flores Diaz <[email protected]>
  • Loading branch information
Dafloresdiaz and Daniel Flores Diaz authored Mar 4, 2024
1 parent 15339f2 commit f845354
Show file tree
Hide file tree
Showing 14 changed files with 876 additions and 46 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
SECRET_KEY = 'foo';
EXPIRES_IN = '1h';
POSTGRES_USER = 'postgres';
POSTGRES_PASSWORD = 'changethis';
POSTGRES_HOST ='db';
POSTGRES_DB = 'ca_microservices_nodejs';
POSTGRES_PORT = '5432';
26 changes: 21 additions & 5 deletions app/internal/core/controllers/users.controller.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { constants } from 'http2';
import { UserServices } from '../services';
import { UserModel } from '../models';

/**
* Callback function for getting a user by id.
Expand All @@ -8,9 +9,9 @@ import { UserServices } from '../services';
* @param {import('express').Response} res - Express response object.
* @param {import('express').NextFunction} next - Express next middleware function.
*/
const getById = (req, res, next) => {
const getById = async (req, res, next) => {
try {
const message = UserServices.getById();
const message = await UserServices.getById(req.db.client, req.params.id);
res.status(constants.HTTP_STATUS_OK).json(message);
} catch (err) {
next(err);
Expand All @@ -26,7 +27,22 @@ const getById = (req, res, next) => {
*/
const create = async (req, res, next) => {
try {
const message = UserServices.create();
const err = UserModel.validate(req.body)
if (err.error) {
throw new Error(`Request is not correct`);
}
const { first_name, last_name, email } = req.body;
const newUser = new UserModel({
email: email,
first_name: first_name,
last_name: last_name,
});
const message = UserServices.create(
req.db.client,
newUser.first_name,
newUser.last_name,
newUser.email,
);
res.status(constants.HTTP_STATUS_OK).json(message);
} catch (err) {
next(err);
Expand All @@ -42,7 +58,7 @@ const create = async (req, res, next) => {
*/
const update = async (req, res, next) => {
try {
const message = UserServices.update();
const message = await UserServices.update(req.db.client, req.params.id, req.body);
res.status(constants.HTTP_STATUS_OK).json(message);
} catch (err) {
next(err);
Expand All @@ -58,7 +74,7 @@ const update = async (req, res, next) => {
*/
const drop = async (req, res, next) => {
try {
const message = UserServices.deleteByID();
const message = await UserServices.deleteByID(req.db.client, req.params.id);
res.status(constants.HTTP_STATUS_OK).json(message);
} catch (err) {
next(err);
Expand Down
33 changes: 33 additions & 0 deletions app/internal/core/domain/models/responses.models.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,40 @@ const tokenResponse = (token, expiresIn) => ({
tokenType: 'Bearer',
});

/**
* Represents a token response object.
* @typedef {object} UserResponse
* @property {string} id - The user id.
* @property {string} firstName - User first name.
* @property {string} lastName - User last name.
* @property {string} email - User email.
*/

/**
* Creates a token response object.
* @param {string} success - A simple flag.
* @param {string} message - A simple message about the response.
* @param {Map} user - The user information from the database.
* @param {string} id - The id in UUID form of the user.
* @param {string} first_name - The first name of the user.
* @param {string} last_name - The last_name of the user.
* @param {string} email - The email from the user.
* @returns {UserResponse} The user response with the information about the user.
*/

const userResponse = (id, firstName, lastName, email) =>({
success: true,
message: "Success",
user: {
id: id,
first_name: firstName,
last_name: lastName,
email: email,
}
});

export const Responses = {
simple,
tokenResponse,
userResponse,
};
2 changes: 1 addition & 1 deletion app/internal/core/middlewares/auth.middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export const Auth = (req, res, next) => {
req.headers.authorization && req.headers.authorization.split(' ')[1];
if (token !== 'undefined' && tokenPaths.includes(req.path.slice(0, 7))) {
try {
JWT.verifyToken(token, req.defaultConfig.auth.secretKey);
JWT.verifyToken(token, req.config.auth.securityKey);
next();
} catch (err) {
next(err);
Expand Down
40 changes: 40 additions & 0 deletions app/internal/core/middlewares/database.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Auth middlewares
* @module Middlewares/Database
*/

import {Pool} from "pg";

/**
* Express middleware to create the connection to the database.
* @function
* @param {import('express').Request} req - The Express request object.
* @param {import('express').Response} res - The Express response object.
* @param {import('express').NextFunction} next - The Express next function.
* @throws {Error} If authentication fails.
*/
export const DatabaseConnection = (req, res, next) => {
const { user, password, host, port, database } = req.config.db;
try {
const dbConfig = {
user: user,
password: password,
host: host,
port: port,
database: database
};
const client = new Pool(dbConfig);
client.connect((err, client, release) => {
if (err) {
console.error('Error connecting to PostgreSQL database:', err);
return res.status(500).send('Error connecting to database');
}
console.log('Connected to PostgreSQL database');
req.db = { client, release };
next();
});
} catch (ex) {
console.error('Error connecting to PostgreSQL database:', ex);
res.status(500).send();
}
};
1 change: 1 addition & 0 deletions app/internal/core/middlewares/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './logs.middleware';
export * from './error.middleware';
export * from './configurations.middleware';
export * from './auth.middleware';
export * from './database.middleware';
7 changes: 0 additions & 7 deletions app/internal/core/models/users.model.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ import { model, Schema } from 'mongoose';
*/
const createUserSchema = new Schema(
{
Id: {
type: Number,
required: true,
},
email: {
type: String,
required: true,
Expand All @@ -23,9 +19,6 @@ const createUserSchema = new Schema(
type: String,
required: true,
},
company: String,
url: String,
text: String,
},
{ timestamps: true, versionKey: false },
);
Expand Down
7 changes: 7 additions & 0 deletions app/internal/core/providers/config.provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,11 @@ export const defaultConfig = {
securityKey: process.env.SECRET_KEY,
expiresIn: process.env.EXPIRES_IN,
},
db: {
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
host: process.env.POSTGRES_HOST,
port: process.env.POSTGRES_PORT,
database: process.env.POSTGRES_DB
}
};
72 changes: 64 additions & 8 deletions app/internal/core/services/users.services.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @module Services/Users */

import { Domain } from '../domain';
import {v4 as uuidv4} from 'uuid';

const {
Models: { Responses },
Expand All @@ -9,31 +10,86 @@ const {
/**
* Get user by ID.
* @function
* @returns {import('../domain/models/responses.models.js').SimpleResponse} The response for the get by ID request.
* @param {db} db - DB client for the connection to the database.
* @param {string} id - The UUID for the user in the database.
* @returns {import('../domain/models/responses.models.js').UserResponse} The response for the get by ID request.
*/
const getById = () => Responses.simple('This is a response from a GET request');
const getById = async (db, id) => {
try{
const query = 'SELECT id, first_name, last_name, email FROM users WHERE id=$1';
const result = await db.query(query, [id]);
if (result.rows.length === 0) {
return Responses.simple(`User with ${id} is not in the database.`);
} else {
return Responses.userResponse(id,result.rows[0].first_name, result.rows[0].last_name, result.rows[0].email);
}
} catch (error) {
return error;
}
}

/**
* Create a new user.
* @function
* @returns {import('../domain/models/responses.models.js').SimpleResponse} The response for the create request.
* @param {db} db - DB client for the connection to the database.
* @param {string} firstName - The first name of the user.
* @param {string} lastName - The last name of the user.
* @param {string} email - The email from the user.
* @returns {import('../domain/models/responses.models.js').userResponse} The response for the create request.
*/
const create = () => Responses.simple('This is a response from a POST request');
const create = (db, firstName, lastName, email) => {
const randomUuid = uuidv4();
const query = 'INSERT INTO users (id, first_name, last_name, email) VALUES ($1, $2, $3, $4)';
db.query(query, [randomUuid, firstName, lastName, email]);
return Responses.userResponse(randomUuid, firstName, lastName, email);
};

/**
* Update an existing user.
* @function
* @param {db} db - DB client for the connection to the database.
* @param {string} id - The id of the user.
* @param {Map} body - THe fields to be updated.
* @returns {import('../domain/models/responses.models.js').SimpleResponse} The response for the update request.
*/
const update = () =>
Responses.simple('This is a response from an UPDATE request');
const update = async (db, id, body) =>{
try{
const updates = [];
const values = [id];
let index = 2;
for (const value in body) {
updates.push(`${value} = $${index}`);
values.push(body[value]);
index++;
}
const query = `UPDATE users SET ${updates.join(',')} WHERE id = $1`;
db.query(query, values);
return Responses.simple(`User with ${id} has been updated.`)
} catch (error) {
return error;
}
}

/**
* Delete a user by ID.
* @function
* @returns {import('../domain/models/responses.models.js').SimpleResponse} The response for the delete request.
*/
const deleteByID = () =>
Responses.simple('This is a response from a DELETE request');
const deleteByID = async (db, id) =>{
try{
const query = 'SELECT id FROM users WHERE id=$1';
const result = await db.query(query, [id]);
if (result.rows.length === 0) {
return Responses.simple(`User with ${id} is not in the database.`);
} else{
const query = 'DELETE FROM users WHERE id = $1';
db.query(query, [id]);
return Responses.simple(`User with ${id} has been deleted.`);
}
} catch (error) {
return error;
}
}


export const UserServices = { getById, create, update, deleteByID };
4 changes: 4 additions & 0 deletions app/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
HandlerError,
Configurations,
Auth,
DatabaseConnection,
} from '../internal/core/middlewares';

const { Routes } = Adapters;
Expand All @@ -25,6 +26,9 @@ App.use(bodyParser.urlencoded({ extended: false }));
// Apply global configurations middleware
App.use(Configurations);

//Apply database middleware
App.use(DatabaseConnection)

// Apply logger middleware
App.use(Logger);

Expand Down
6 changes: 6 additions & 0 deletions db_migrations/000001.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE TABLE IF NOT EXISTS users (
id TEXT PRIMARY KEY,
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
email TEXT NOT NULL
);
25 changes: 24 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
version: "3.4"
services:
db:
container_name: ca-microservices-app-db
image: postgres:latest
restart: always
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=changethis
- POSTGRES_DB=ca_microservices_nodejs
- POSTGRES_PORT=5432
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./db_migrations/000001.sql:/docker-entrypoint-initdb.d/000001.sql
command: ["docker-entrypoint.sh", "postgres", "-c", "log_statement=all"]
express:
image: ca-microservices-nodejs:latest
container_name: ca-microservices-app
Expand All @@ -11,4 +26,12 @@ services:
- NODE_ENV=development
- PORT=3001
- SECRET_KEY=secret_key_value
- EXPIRES_IN=1h
- EXPIRES_IN=1h
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=changethis
- POSTGRES_HOST=db
- POSTGRES_DB=ca_microservices_nodejs
- POSTGRES_PORT=5432
restart: on-failure
volumes:
postgres_data:
Loading

0 comments on commit f845354

Please sign in to comment.