Skip to content
Snippets Groups Projects
Commit 204388ae authored by Ramiro's avatar Ramiro
Browse files

Users api

parent 26b3ebfe
No related branches found
No related tags found
No related merge requests found
Showing
with 9173 additions and 0 deletions
.env 0 → 100644
MYSQL_USERNAME=root
MYSQL_PASSWORD=password
MYSQL_DB=repp_users
\ No newline at end of file
const path = require('path');
module.exports = {
'config': path.resolve('dist', 'config', 'config.js'),
'models-path': path.resolve('dist', 'models'),
'seeders-path': path.resolve('dist', 'seeders'),
'migrations-path': path.resolve('dist', 'migrations')
};
\ No newline at end of file
----Como correr los test ----
Para crear un test para un correspondiente archivo se debe proceder de la siguiente forma:
-Para un archivo ```ejemplo.ts``` crear dentro de la carpeta __test__ un archivo llamado ```ejemplo.test.js```
-Dentro del archivo ejemplo.test.ts incluir el archivo ejemplo.ts con ```const sum = require('./../ejemplo');```
-Para cada función a probar ```funcionEjemplo(arg)``` la forma de diseñar un test es:
-```test(Desc, () => {expect(funcionEjemplo(ejemploArg)).toBe(resultado);});```
-Aqui los argumentos son:
-```funcionEjemplo``` = es la funcion que esta siendo probada, que tiene como inputs ```arg```
-```Desc``` = es un string, que debe describir brevemente el test que se lleva a cabo
-```ejemploArg``` = son los argumentos con los cuales se testeará la ```funcionEjemplo```
-```resultado``` = es el retorno esperado de ```funcionEjemplo``` con los argumentos ```ejemploArg```
-Pueden agrupar los test con ```describe("Nombre del grupo de tests", () =>{}```, dentro de ese bloque se especifican los test al igual que antes, cambiando ```test``` por ```it```
Los tests son ejecutados de las siguientes formas:
-Manualmente pueden ejecutarse todos mediante ```npm run test```
-Individualmente cada test puede ser ejecutado con ```npm test -- -t "Nombre del test" ``` (El nombre es el argumento del ```describe``` previamente mencionado)
-Alternativamente todos los test son ejecutados al llevar a cabo un push. Si uno de los test no es exitoso, el push no se lleva a cabo
\ No newline at end of file
This diff is collapsed.
{
"name": "repp_backend",
"version": "1.0.0",
"description": "Herramienta de calculo del requerimiento energetico ponderado de una poblacion, llamado REPP.",
"main": "index.ts",
"dependencies": {
"bcrypt": "^5.0.1",
"config.json": "0.0.4",
"cors": "^2.8.5",
"dotenv": "^10.0.0",
"express": "^4.17.1",
"gulp-eslint": "^6.0.0",
"mysql2": "^2.3.0",
"openapi-types": "^9.3.0",
"sequelize": "^6.6.5",
"swagger-jsdoc": "^6.1.0",
"swagger-ui-express": "^4.1.6",
"tsconfig.json": "^1.0.10",
"xlsx": "^0.17.1"
},
"devDependencies": {
"@types/bcrypt": "^5.0.0",
"@types/bluebird": "^3.5.36",
"@types/cors": "^2.8.12",
"@types/express": "^4.17.13",
"@types/node": "^16.9.6",
"@types/swagger-jsdoc": "^6.0.1",
"@types/swagger-ui-express": "^4.1.3",
"@types/validator": "^13.6.3",
"@typescript-eslint/eslint-plugin": "^2.34.0",
"@typescript-eslint/parser": "^2.0.0",
"eslint": "^7.32.0",
"eslint-config-airbnb": "^18.2.1",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-config-airbnb-typescript": "^7.2.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1",
"eslint-plugin-react": "^7.21.5",
"eslint-plugin-react-hooks": "^4.0.8",
"husky": "^4.3.8",
"jest": "^27.0.6",
"lint-staged": "^11.1.2",
"nodemon": "^2.0.12",
"sequelize-cli": "^6.2.0",
"ts-node": "^10.2.1",
"typescript": "^4.4.2"
},
"scripts": {
"start": "nodemon ./src/index.ts",
"watch": "tsc -w",
"test": "jest",
"pretest": "./node_modules/.bin/eslint --ignore-path .gitignore . --fix"
},
"repository": {
"type": "git",
"url": "https://gitlab.fing.edu.uy/julieta.dubra/repp.git"
},
"author": "",
"license": "ISC",
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.ts": [
"./node_modules/.bin/eslint --fix",
"git add"
]
}
}
import { Response, Router } from 'express';
import { User } from '../models/users.model';
import Paginator from '../interfaces/paginator.interface';
import UserService from '../Services/UserService';
const router = Router();
const list = async (req: any, res: Response): Promise<Response> => {
try {
let users: Paginator<User> = await UserService.list(req.query.limit, req.query.offset);
return res.status(200).send(users);
} catch (error) {
const e = error as Error;
return res.status(400).json({ error: e.message });
}
}
const create = async (req: any, res: Response): Promise<Response> => {
try {
let user: User = await UserService.create(req.body);
return res.status(200).send(user);
} catch (error) {
const e = error as Error;
return res.status(400).json({ error: e.message });
}
}
const update = async (req: any, res: Response): Promise<Response> => {
try {
let user: User = await UserService.update(req.params.id, req.body);
return res.status(200).send(user);
} catch (error) {
const e = error as Error;
return res.status(400).json({ error: e.message });
}
};
const password = async (req: any, res: Response): Promise<Response> => {
try {
let user: User = await UserService.password(req.params.id, req.body);
return res.status(200).send(user);
} catch (error) {
const e = error as Error;
return res.status(400).json({ error: e.message });
}
};
const approve = async (req: any, res: Response): Promise<Response> => {
try {
let user: User = await UserService.approve(req.params.id, req.body);
return res.status(200).send(user);
} catch (error) {
const e = error as Error;
return res.status(400).json({ error: e.message });
}
};
const active = async (req: any, res: Response): Promise<Response> => {
try {
let user: User = await UserService.active(req.params.id);
return res.status(200).send(user);
} catch (error) {
const e = error as Error;
return res.status(400).json({ error: e.message });
}
}
router.route('/')
.get(list)
.post(create)
router.route('/:id')
.put(update)
.patch(active);
router.route('/:id/password')
.put(password);
router.route('/:id/approve')
.put(approve);
router.route('/:id/active')
.patch(active);
export default router;
\ No newline at end of file
import UserController from './UserCotroller';
export {
UserController
}
\ No newline at end of file
export interface UserCreateDTO {
name: string;
email: string;
password: string;
repeat: string;
type: number;
status: number;
}
export interface UserLoginDTO {
email: string;
password: string;
}
\ No newline at end of file
import { profiles, status } from '../enums/index.enum';
import bcrypt from 'bcrypt';
import Paginator from '../interfaces/paginator.interface';
import { User } from '../models/users.model';
import { UserCreateDTO } from '../DTOs/UserDTO';
const list = async (limit: number, offset: number): Promise<Paginator<User>> => {
let options = {};
if (limit && offset) {
options = {
limit,
offset
}
}
return User.findAndCountAll({
attributes: [
'id', 'name', 'email', 'type', 'active'
],
order: [
['name', 'ASC']
],
...options
});
};
const create = async (userDTO: UserCreateDTO): Promise<User> => {
return User.findOne({
where: {
email: userDTO.email
}
}).then(async (user: User) => {
if (user) {
throw new Error('email in use');
} else {
let password = userDTO.password;
let repeat = userDTO.repeat;
if (password == repeat){
return User.create({
name: userDTO.name,
email: userDTO.email,
password: bcrypt.hashSync(userDTO.password || '1234', 10),
type: userDTO.type || profiles.unassigned,
status: userDTO.status || status.approved,
createdBy: 1,
createdAt: new Date()
}).catch((error: Error) => {1234
console.log(error);
throw new Error('create user error');
});
}else{
throw new Error('passwords doesn\'t match');
}
}
}).catch((error: Error) => {1234
console.log(error);
throw new Error('find user error');
});
}
const update = async (userId: number, userDTO: UserCreateDTO): Promise<User> => {
return User.findOne({
attributes: [
'id', 'name', 'email'
],
where: {
id: userId
}
}).then(async (user: User) => {
if (!user){
throw new Error('user not found');
}else{
let emailUser: User = await User.findOne({
where: {
email: userDTO.email
}
});
if (!emailUser || emailUser.get('id') == user.get('id')) {
return user.update({
name: userDTO.name,
email: userDTO.email,
type: userDTO.type || profiles.unassigned,
updatedAt: new Date()
}).catch((error: Error) => {
console.log(error);
throw new Error('user update error');
});
} else {
throw new Error('email in use');
}
}
}).catch((error: Error) => {
console.log(error);
throw new Error('find user error');
});
}
const password = async (userId: number, userDTO: UserCreateDTO): Promise<User> => {
return User.findOne({
attributes: [
'id', 'name', 'email'
],
where: {
id: userId
}
}).then(async (user: User) => {
if (!user){
throw new Error('user not found');
}else{
let password = userDTO.password;
let repeat = userDTO.repeat;
if (password == repeat){
return user.update({
password: bcrypt.hashSync(userDTO.password, 10),
updatedAt: new Date()
}).catch((error: Error) => {
console.log(error);
throw new Error('user update error');
});
}else{
throw new Error('passwords doesn\'t match');
}
}
}).catch((error: Error) => {
console.log(error);
throw new Error('find user error');
});
};
const approve = async (userId: number, userDTO: UserCreateDTO): Promise<User> => {
return User.findOne({
attributes: [
'id', 'name',
'email', 'type',
'createdAt'
],
where: {
id: userId
}
}).then(async (user: User) => {
if (!user){
throw new Error('user not found');
}else{
return user.update({
status: userDTO.status,
updatedAt: new Date()
}).catch((error: Error) => {
console.log(error);
throw new Error('user update error');
});
}
}).catch((error: any) => {
console.log(error);
throw new Error('find user error');
});
};
const active = async (userId: number): Promise<User> => {
return User.findOne({
where: {
id: userId
}
}).then(async (user: User) => {
if (!user) {
throw new Error('user not found');
} else {
return user.update({
active: !user.get('active'),
updatedAt: new Date()
}).catch((error: any) => {
throw new Error('user update error');
});
}
}).catch((error: any) => {
console.log(error);
throw new Error('find user error');
});
}
export default {
list,
create,
update,
password,
approve,
active
}
\ No newline at end of file
require('dotenv').config();
export const development = {
username: process.env.MYSQL_USERNAME,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DB,
host: "localhost",
dialect: "mysql",
logging: false
};
\ No newline at end of file
export enum status {
pending,
approved,
rejected
};
export enum profiles {
unassigned = 0,
administrator = 1,
client = 2
};
\ No newline at end of file
/* eslint-disable no-console */
import express, { Application } from 'express';
import 'dotenv/config';
import cors from 'cors';
import swaggerJsDoc, { Options } from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';
import Routes from './routes';
const app: Application = express();
const PORT = process.env.PORT || 8000;
// swagger init
const swaggerOptions: Options = {
swaggerDefinition: {
openapi: '3.0.0',
info: {
title: 'REPP Rest API',
version: '1.0.0',
description: '',
servers: ['http://localhost:3000'],
},
},
apis: ['src/routes.ts'],
};
const swaggerDocs = swaggerJsDoc(swaggerOptions);
// middlewares
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
app.use(express.json({
limit: '50mb',
}));
app.use(express.urlencoded({ extended: false }));
app.use(cors({
origin: '*',
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
preflightContinue: false,
optionsSuccessStatus: 204,
}));
app.use(express.raw({
limit: '50mb',
}));
app.use(Routes);
app.listen(PORT, (): void => {
console.log(`REPP Backend running here 👉 https://localhost:${PORT}`);
});
export default interface Paginator<T> {
count: number;
rows: T[];
};
\ No newline at end of file
import { Model } from 'sequelize';
interface Attributes {
id: number;
name?: string;
email: string;
password: string;
type: number;
token?: string;
status: number;
subscribed: boolean;
active: boolean;
createdAt: Date;
updatedAt: Date;
createdBy: number;
updatedBy: number;
deletedAt?: Date;
};
export default interface User extends Model<Attributes>, Attributes {};
\ No newline at end of file
export const up = (queryInterface: any, Sequelize: any) => {
return queryInterface.createTable('Users', {
id: {
type: Sequelize.INTEGER,
allowNull: false,
autoIncrement: true,
primaryKey: true
},
name: {
type: Sequelize.STRING,
allowNull: true
},
email: {
type: Sequelize.STRING,
allowNull: false,
unique: true
},
password: {
type: Sequelize.STRING,
allowNull: false
},
type: {
type: Sequelize.INTEGER,
allowNull: false
},
token: {
type: Sequelize.STRING,
allowNull: true
},
status: {
type: Sequelize.INTEGER,
allowNull: false
},
active: {
type: Sequelize.BOOLEAN,
allowNull: false,
defaultValue: true
},
createdAt: {
type: Sequelize.DATE,
allowNull: false
},
updatedAt: {
type: Sequelize.DATE,
allowNull: true
},
createdBy: {
type: Sequelize.INTEGER,
allowNull: false
},
updatedBy: {
type: Sequelize.INTEGER,
allowNull: true
},
deletedAt: {
type: Sequelize.DATE,
allowNull: true
}
});
};
export const down = (queryInterface: any, Sequelize: any) => {
return queryInterface.dropTable('Users');
};
\ No newline at end of file
import { User } from './users.model';
export {
User
}
\ No newline at end of file
import { status } from '../enums/index.enum';
import * as environments from '../config/config';
import { Sequelize, DataTypes, Model } from 'sequelize';
const current: string = process.env.NODE_ENV || 'development';
let config = (environments as any)[current];
let sequelize: Sequelize = new Sequelize(config.database, config.username, config.password, config);
export class User extends Model {}
User.init({
id: {
type: DataTypes.INTEGER,
allowNull: false,
primaryKey: true,
autoIncrement: true
},
name: {
type: DataTypes.STRING(255),
allowNull: true
},
email: {
type: DataTypes.STRING(255),
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING(255),
allowNull: false
},
type: {
type: DataTypes.INTEGER,
allowNull: false
},
token: {
type: DataTypes.STRING(255),
allowNull: true
},
status: {
type: DataTypes.INTEGER,
allowNull: false,
validate: {
isIn: {
args: [[
String(status.pending),
String(status.approved),
String(status.rejected)
]],
msg: 'invalid status'
}
}
},
active: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: true
},
createdAt: {
type: DataTypes.DATE,
allowNull: false
},
updatedAt: {
type: DataTypes.DATE,
allowNull: true
},
createdBy: {
type: DataTypes.INTEGER,
allowNull: false
},
updatedBy: {
type: DataTypes.INTEGER,
allowNull: true
},
deletedAt: {
type: DataTypes.DATE,
allowNull: true
}
},{
sequelize,
modelName: 'User'
});
\ No newline at end of file
import { Request, Response, Router } from 'express';
import { UserController } from './Controllers';
const router = Router();
router.get('/', (req: Request, res: Response): void => {
res.send('Hey! This is REPP API, you can go to /api-docs to learn more!');
});
router.use('/users', UserController);
export default router;
{
"compilerOptions": {
"module": "commonjs",
"esModuleInterop": true,
"target": "es6",
"allowJs": false,
"noImplicitAny": true,
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist",
"rootDir": "src",
"baseUrl": ".",
"paths": {
"*": [
"node_modules/*",
"src/types/*"
]
},
"lib": [
"ES2020"
]
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment