Skip to content

Commit

Permalink
Merge f99c032 into d20a2c8
Browse files Browse the repository at this point in the history
  • Loading branch information
mifeille committed Jun 12, 2019
2 parents d20a2c8 + f99c032 commit 715d3cf
Show file tree
Hide file tree
Showing 16 changed files with 365 additions and 19 deletions.
4 changes: 1 addition & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,4 @@ DATABASE_URL=
LOCAL_DB_USER=
LOCAL_DB_PASSWORD=
LOCAL_DB_NAME=
TEST_DB_USER=
TEST_DB_PASSWORD=
TEST_DB_NAME=
TEST_DATABASE_URL=
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
language: node_js
node_js:
- "stable"
before_script:
- yarn add sequelize-cli
script:
- yarn test
before_install:
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@
"build": "babel src --out-dir dist",
"serve": "node dist/index.js",
"start": "node dist/index.js",
"test": "nyc --reporter=html --reporter=text mocha ./test/*.js --exit",
"test": "nyc --reporter=text ./node_modules/.bin/mocha --recursive ./test/* --exit --require @babel/register --require @babel/polyfill ",
"coverage": "nyc report --reporter=text-lcov | coveralls"
},
"author": "Andela Simulations Programme",
"license": "MIT",
"dependencies": {
"@hapi/joi": "^15.0.3",
"body-parser": "^1.18.3",
"cors": "^2.8.4",
"dotenv": "^8.0.0",
Expand All @@ -22,7 +23,9 @@
"express": "^4.16.3",
"express-jwt": "^5.3.1",
"express-session": "^1.15.6",
"joi": "^14.3.1",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.11",
"method-override": "^2.3.10",
"methods": "^1.1.2",
"morgan": "^1.9.1",
Expand All @@ -45,7 +48,7 @@
"chai-http": "^4.3.0",
"coveralls": "^3.0.4",
"eslint": "^5.16.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-config-airbnb-base": "^13.1.0",
"eslint-plugin-import": "^2.17.3",
"mocha": "^6.1.4",
"nodemon": "^1.19.1",
Expand Down
5 changes: 4 additions & 1 deletion src/api/routes/authRouter.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { Router } from 'express';
import authController from '../controllers/auth';
import validateBody from '../../middleware/userValidations';
import userValidation from '../../middleware/validUser';


const authRouter = Router();
const { signup } = authController;

authRouter.post('/signup', signup);
authRouter.post('/signup', validateBody('signup'), userValidation.usernameExists, userValidation.emailExists, signup);

export default authRouter;
6 changes: 1 addition & 5 deletions src/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ config.staging = {
};

config.test = {
username: process.env.TEST_DB_USER,
password: process.env.TEST_DB_PASSWORD,
database: process.env.TEST_DB_NAME,
host: '127.0.0.1',
dialect: 'postgres'
use_env_variable: 'TEST_DATABASE_URL',
};

config.production = {
Expand Down
File renamed without changes.
47 changes: 47 additions & 0 deletions src/helpers/userSchema.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import Joi from '@hapi/joi';

export default {
signup: Joi.object().keys({
firstName: Joi.string()
.trim()
.required()
.regex(/^[A-Za-z_-]+$/)
.min(3)
.label('First name is required, it must have at least 3 letters and must contain only letters, underscores(_) and hyphens (-)'),
lastName: Joi.string()
.trim()
.required()
.regex(/^[A-Za-z_.-]+$/)
.min(3)
.label('Last name is required, it must have at least 3 letters and must contain only letters, underscores(_) and hyphens (-)'),
username: Joi.string()
.trim()
.lowercase()
.required()
.regex(/^[a-zA-Z0-9_.-]+$/)
.min(3)
.label('Username is required, it must have at least 3 letters and must contain only letters, numbers, underscores(_), hyphens (-) and points (.)'),
email: Joi.string()
.trim()
.lowercase()
.email()
.required()
.label('Email is required and should look like this : example@email.com!'),
password: Joi.string()
.trim()
.regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})/)
.required()
.label('Password is required and must be at least 8 letters containing'
+ ' at least a number a Lowercase letter and an Uppercase letter'),
confirmPassword: Joi.any()
.required()
.valid(Joi.ref('password'))
.label('Password and Confirm Password do not match'),
bio: Joi.string(),
image: Joi.string(),
dateOfBirth: Joi.string(),
gender: Joi.string(),
socialId: Joi.string(),
provider: Joi.string()
}),
};
9 changes: 6 additions & 3 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import express from 'express';
import dotenv from 'dotenv';
import swaggerUi from 'swagger-ui-express';
import api from './api/routes';
import api from './api/routes/index';
import globalMiddleware from './middleware/globalMiddleware';
import swaggerDoc from '../swagger.json';
import db from './sequelize/models/index';
Expand All @@ -12,14 +12,17 @@ dotenv.config();
const port = process.env.PORT || 3000;
const app = express();

globalMiddleware(app);

app.use('/', swaggerUi.serve, swaggerUi.setup(swaggerDoc));
globalMiddleware(app);
app.use('/api', api);
app.get('/', swaggerUi.serve, swaggerUi.setup(swaggerDoc));


sequelize.sync().then(() => {
app.listen(port, () => {
// eslint-disable-next-line no-console
console.log(`Database succesfully connected\nServer listening on port: ${port} in ${process.env.NODE_ENV} mode`);
});
});

export default app;
31 changes: 31 additions & 0 deletions src/middleware/userValidations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Joi from '@hapi/joi';
import _ from 'lodash';
import Schemas from '../helpers/userSchema';

const validateUser = schema => (req, res, next) => {
const data = req.body;

if (_.has(Schemas, schema)) {
const chosenSchema = _.get(Schemas, schema);
const err = Joi.validate(data, chosenSchema, { abortEarly: false });

if (!err.error) {
req.body = data;
next();
} else {
const allErrors = [];
err.error.details.forEach((errors) => {
const findError = allErrors.filter(error => error === errors.context.label);
if (findError.length === 0) {
allErrors.push(errors.context.label);
}
});
return res.status(400).send({
message: allErrors,
});
}
}
next();
};

export default validateUser;
41 changes: 41 additions & 0 deletions src/middleware/validUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import models from '../sequelize/models';

const validUser = {
async emailExists(req, res, next) {
const { email } = req.body;
await models.User.findAll({
where: {
email
},
}).then((data) => {
if (data.length > 0) {
return res.status(400).json({
status: 400,
message: 'This email is already in use',
});
}
});

next();
},
async usernameExists(req, res, next) {
const { username } = req.body;
await models.User.findAll({
where: {
username
},
}).then((data) => {
if (data.length > 0) {
return res.status(400).json({
status: 400,
message: 'This username is not available, Please choose another one!',
});
}
});

next();
},
};


export default validUser;
60 changes: 60 additions & 0 deletions src/sequelize/migrations/20190611080237-create-user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@


module.exports = {
up: (queryInterface, Sequelize) => queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
firstName: {
type: Sequelize.STRING
},
lastName: {
type: Sequelize.STRING
},
username: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING
},
password: {
type: Sequelize.STRING
},
bio: {
type: Sequelize.TEXT
},
image: {
type: Sequelize.TEXT
},
dateOfBirth: {
type: Sequelize.DATE
},
gender: {
type: Sequelize.STRING
},
provider: {
allowNull: false,
type: Sequelize.STRING
},
socialId: {
allowNull: false,
type: Sequelize.STRING
},
verified: {
allowNull: false,
type: Sequelize.BOOLEAN
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
}),
down: queryInterface => queryInterface.dropTable('Users')
};
3 changes: 3 additions & 0 deletions src/sequelize/models/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import fs from 'fs';
import path from 'path';
import Sequelize from 'sequelize';
import dotenv from 'dotenv';

dotenv.config();

const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
Expand Down
17 changes: 17 additions & 0 deletions src/sequelize/models/user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module.exports = (sequelize, DataTypes) => {
const User = sequelize.define('User', {
firstName: DataTypes.STRING,
lastName: DataTypes.STRING,
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
bio: DataTypes.TEXT,
image: DataTypes.TEXT,
dateOfBirth: DataTypes.DATE,
gender: DataTypes.STRING,
provider: DataTypes.STRING,
socialId: DataTypes.STRING,
verified: DataTypes.BOOLEAN
}, {});
return User;
};
26 changes: 26 additions & 0 deletions src/sequelize/seeders/20190611101225-test-user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@


module.exports = {
up: queryInterface =>
/*
Add altering commands here.
Return a promise to correctly handle asynchronicity.
*/

queryInterface.bulkInsert('Users', [{
firstName: 'Mireille',
lastName: 'Niwemuhuza',
username: 'mifeille',
email: 'nimilleer@gmail.com',
password: 'Mireille1!',
bio: '',
image: '',
dateOfBirth: '12/12/2000',
gender: '',
provider: '',
socialId: '',
verified: 'false',
createdAt: new Date(),
updatedAt: new Date(),
}], {}),
};
Loading

0 comments on commit 715d3cf

Please sign in to comment.