Skip to content

Commit

Permalink
rebase off develop
Browse files Browse the repository at this point in the history
  • Loading branch information
fxola committed Jul 4, 2019
2 parents 448392a + f0f3825 commit 6802e65
Show file tree
Hide file tree
Showing 23 changed files with 395 additions and 84 deletions.
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"main": "index.js",
"scripts": {
"test": "NODE_ENV=test npm run migration-rollback && npm run db-migration && nyc mocha src/**/*.spec.js --require @babel/register --exit || true",
"test-watch": "nodemon --exec \"npm test\"",
"start": "nodemon --exec babel-node src/index.js",
"build": "npm run clean && npm run babel-build",
"clean": "rm -rf dist",
Expand All @@ -26,6 +27,7 @@
"body-parser": "^1.18.3",
"chai": "^4.2.0",
"chai-http": "^4.3.0",
"cloudinary": "^1.14.0",
"cors": "^2.8.4",
"cross-env": "^5.2.0",
"dotenv": "^6.2.0",
Expand All @@ -35,14 +37,16 @@
"friendly-mail": "^1.0.0",
"jsonwebtoken": "^8.5.1",
"mocha": "^6.1.4",
"multer": "^1.4.1",
"pg": "^7.11.0",
"pg-hstore": "^2.3.3",
"sequelize": "^5.8.12",
"sequelize-cli": "^5.5.0",
"sequelize-test-helpers": "^1.1.2",
"sinon": "^7.3.2",
"sinon-chai": "^3.3.0",
"swagger-ui-express": "^4.0.6"
"swagger-ui-express": "^4.0.6",
"morgan": "^1.9.1"
},
"devDependencies": {
"coveralls": "^3.0.4",
Expand All @@ -52,7 +56,6 @@
"eslint-plugin-import": "^2.17.3",
"eslint-plugin-prettier": "^3.1.0",
"mocha-lcov-reporter": "^1.3.0",
"morgan": "^1.9.1",
"nodemon": "^1.19.1",
"nyc": "^14.1.1",
"prettier": "^1.18.2"
Expand Down
52 changes: 51 additions & 1 deletion src/controllers/auth.controllers.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
isUserExist
} from '../services/auth.service';
import Helper from '../services/helper';
import { upload } from '../helpers/image.helper';

/**
* @method signUp
Expand Down Expand Up @@ -57,4 +58,53 @@ const login = async (request, response) => {
});
};

export default { signUp, login };
/**
*
* @description Handles the Logic for Updating a User profile
* Route: PUT: /users/profileupdate
* @param {object} request
* @param {object} response
* @returns JSON API Response
*/
const profileUpdate = async (request, response, next) => {
try {
let imagePath;
const previousImage = request.foundUser.image;
let imageUniqueName;
let uploadedImage;
if (request.file) {
imagePath = request.file.path;
imageUniqueName = request.file.originalname;
const imageResponse = await upload(imagePath, imageUniqueName);
uploadedImage = imageResponse.secure_url;
}

const fields = {
...request.body,
image: uploadedImage || previousImage
};
const updatedUser = await request.foundUser.update(fields);
const {
firstName,
lastName,
bio,
twitterHandle,
facebookHandle,
image,
userName
} = updatedUser;
return Helper.successResponse(response, 200, {
firstName,
lastName,
bio,
userName,
twitterHandle,
facebookHandle,
image
});
} catch (error) {
next(error);
}
};

export default { signUp, login, profileUpdate };
11 changes: 11 additions & 0 deletions src/db/migrations/add-bio.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn('Users', 'bio', {
type: Sequelize.TEXT,
allowNull: true
});
},
down: queryInterface => {
return queryInterface.removeColumn('Users', 'bio');
}
};
11 changes: 11 additions & 0 deletions src/db/migrations/add-facebook-handle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn('Users', 'facebookHandle', {
type: Sequelize.STRING,
allowNull: true
});
},
down: queryInterface => {
return queryInterface.removeColumn('Users', 'facebookHandle');
}
};
11 changes: 11 additions & 0 deletions src/db/migrations/add-twitter-handle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn('Users', 'twitterHandle', {
type: Sequelize.STRING,
allowNull: true
});
},
down: queryInterface => {
return queryInterface.removeColumn('Users', 'twitterHandle');
}
};
12 changes: 12 additions & 0 deletions src/db/models/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ export default (sequelize, DataTypes) => {
type: DataTypes.STRING,
allowNull: true,
defaultValue: 'author'
},
bio: {
type: DataTypes.TEXT,
allowNull: true
},
twitterHandle: {
type: DataTypes.STRING,
allowNull: true
},
facebookHandle: {
type: DataTypes.STRING,
allowNull: true
}
});

Expand Down
21 changes: 21 additions & 0 deletions src/helpers/image.helper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import cloudinary from 'cloudinary';
cloudinary.config({
cloud_name: process.env.CLOUDINARY_NAME,
api_key: process.env.CLOUDINARY_API_KEY,
api_secret: process.env.CLOUDINARY_API_SECRET
});

export const upload = async (path, uniqueFilename) => {
return cloudinary.v2.uploader.upload(
path,
{ public_id: `avatar/${uniqueFilename}`, tags: `avatar` }, // directory and tags are optional
image => {
console.log('file uploaded to Cloudinary');
// remove file from server
const fs = require('fs');
fs.unlinkSync(path);
// return image details
return image;
}
);
};
15 changes: 7 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import Routes from './routes/v1';
const app = express();

// Normal express config defaults
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use('/uploads', express.static('uploads'));
app.use(bodyParser.json());
app.use(logger('dev'));
Routes(app);
Expand All @@ -23,24 +24,22 @@ app.get('/', (request, response) => {
});
});

app.use((req, res, next) => {
app.use((request, response, next) => {
const error = new Error('You are trying to access a wrong Route');
error.status = 404;
console.log(error.message);
next(error);
});

app.use((error, req, res, next) => {
res.status(error.status || 500);
res.json({
app.use((error, request, response, next) => {
response.status(error.status || 500);
response.json({
status: error.status || 500,
error: error.name,
message: error.message
});
next();
});

const PORT = process.env.PORT || 3000;

// finally, let's start our server...
app.listen(PORT, () => {
console.log(`Listening on port: ${PORT}`);
Expand Down
43 changes: 0 additions & 43 deletions src/middlewares/auth.middlewares.js

This file was deleted.

31 changes: 31 additions & 0 deletions src/middlewares/imageUpload.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import multer from 'multer';

const fileFilter = (req, file, cb) => {
const error = new Error('Only JPG/PNG images are allowed');
error.status = 422;
// accept only jpg or png images
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(error, false);
}
};

const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString() + file.originalname);
}
});

const upload = multer({
storage,
fileFilter,
limits: {
fileSize: 1024 * 1024 * 5
}
});

export default upload;
37 changes: 37 additions & 0 deletions src/middlewares/profileUpdateCheck.middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { findByUserName, findUserById } from '../services/auth.service';
import Helper from '../services/helper';
/**
*
* @description Handles neccessary checks on profile update
* @param {object} request
* @param {object} response
* @param {function} next
* @returns {(function|Object)} Function next() or an error Object
*/
const profileChecks = async (request, response, next) => {
try {
const userInstance = await findUserById(request.user.id);
if (!userInstance) {
return Helper.failResponse(response, 404, {
message: 'User account does not exist'
});
}

if (request.body.userName) {
const userNameExists = await findByUserName(request.body.userName);
const { id } = userNameExists;
if (userNameExists && id !== request.user.id) {
return Helper.failResponse(response, 409, {
message: 'Username has already been taken'
});
}
}
request.foundUser = userInstance;

return next();
} catch (error) {
return Helper.errorResponse(response, 500);
}
};

export default { profileChecks };
20 changes: 0 additions & 20 deletions src/routes/v1/auth.route.js

This file was deleted.

24 changes: 24 additions & 0 deletions src/routes/v1/auth.routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import express from 'express';
import authenticationValidator from '../../validators/user.validator';
import authenticationController from '../../controllers/auth.controllers';
import AuthenticationToken from '../../middlewares/auth.middleware';
import profileUpdateCheck from '../../middlewares/profileUpdateCheck.middleware';
import upload from '../../middlewares/imageUpload.middleware';
const { login, signUp, profileUpdate } = authenticationController;
const { validator, checkValidationResult } = authenticationValidator;
const { verifyToken } = AuthenticationToken;
const { profileChecks } = profileUpdateCheck;

const router = express.Router();
router
.post('/signup', validator('signup'), checkValidationResult, signUp)
.post('/login', validator('login'), checkValidationResult, login);
router.put(
'/profileupdate',
verifyToken,
upload.single('image'),
profileChecks,
profileUpdate
);

export default router;
2 changes: 1 addition & 1 deletion src/routes/v1/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import auth from './auth.route';
import auth from './auth.routes';

export default app => {
app.use('/api/v1/users', auth);
Expand Down
Loading

0 comments on commit 6802e65

Please sign in to comment.