Skip to content

Commit

Permalink
Merge 4719aca into a9c3b92
Browse files Browse the repository at this point in the history
  • Loading branch information
shonubijerry committed May 27, 2019
2 parents a9c3b92 + 4719aca commit ba61778
Show file tree
Hide file tree
Showing 12 changed files with 1,070 additions and 69 deletions.
11 changes: 5 additions & 6 deletions server/controllers/loansController.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ class LoansController {

static async createLoan(req, res) {
try {
const userEmail = req.user.email;
const currentLoan = await loansModel.checkCurrentOrPendingLoan(userEmail);
const user = await usersModel.findUserById(req.user.id);
const currentLoan = await loansModel.checkCurrentOrPendingLoan(user.email);
if (currentLoan) {
return ResponseHelper.error(
res, 409,
`You have an unpaid loan of ${currentLoan.balance} which is under review or yet to be fully repaid`,
);
}
const newLoan = await loansModel.createLoan(req, userEmail);
const newLoan = await loansModel.createLoan(req, user.email);
return ResponseHelper.success(res, 201, newLoan);
} catch (error) {
return ResponseHelper.error(res, 500, errorStrings.serverError);
Expand All @@ -50,13 +50,12 @@ class LoansController {

static async getLoans(req, res) {
try {
const { email } = req.user;
const user = await usersModel.findUserByEmail(req.user.email);
const user = await usersModel.findUserById(req.user.id);
if (Utils.hasQuery(req)) {
const loans = await LoansController.perpareLoansQuery(req, res, user.isadmin);
return loans;
}
const loans = await loansModel.getLoans(email, user.isadmin);
const loans = await loansModel.getLoans(user.email, user.isadmin);
return ResponseHelper.success(res, 200, loans);
} catch (error) {
return ResponseHelper.error(res, 500, errorStrings.serverError);
Expand Down
6 changes: 3 additions & 3 deletions server/controllers/repaymentsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ class RepaymentsController {
try {
const { loanId } = req.params;
const loanRepayments = await repaymentModel.getLoanRepayments(loanId);
if (!loanRepayments) {
return ResponseHelper.error(res, 404, errorStrings.noRepayments);
if (loanRepayments === 'no-loan') {
return ResponseHelper.error(res, 404, errorStrings.noLoan);
}
return ResponseHelper.success(res, 200, loanRepayments);
} catch (error) {
return ResponseHelper.error(res, 500, errorStrings.serverError);
return ResponseHelper.error(res, 500, error.message);
}
}

Expand Down
2 changes: 1 addition & 1 deletion server/helpers/rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const rules = {
validAmount: /^[0-9]+(\.[0-9]{1,2})?$/,
validNumber: /^[0-9]*$/,
validApproveLoan: /(approved|rejected)/,
validUuid: /[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}/,
validUuid: /^[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}$/,
};

export default rules;
Expand Down
4 changes: 2 additions & 2 deletions server/helpers/token.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ const expirationTime = 86400;
* @returns {Object} generateToken
*/
const generateToken = ({
id, email, firstname, lastname,
id, isadmin,
}) => jwt.sign({
id, email, firstname, lastname,
id, isadmin,
}, secretKey,
{
expiresIn: expirationTime,
Expand Down
7 changes: 2 additions & 5 deletions server/middleware/Auth.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import jwt from 'jsonwebtoken';
import errorStrings from '../helpers/errorStrings';
import ResponseHelper from '../helpers/responseHelper';
import UsersModel from '../model/usersModel';

const secretKey = process.env.SECRET_KEY;
const usersModel = new UsersModel('users');

/**
* @class Authenticate User
Expand Down Expand Up @@ -39,12 +37,11 @@ class Auth {
* @param {Function} next
* @return {Object}
*/
static async authenticateAdmin(request, response, next) {
static authenticateAdmin(request, response, next) {
try {
const token = request.headers.authorization;
request.user = Auth.verifyToken(token);
const user = await usersModel.findUserByEmail(request.user.email);
if (user.isadmin === false) {
if (request.user.isadmin === false) {
return ResponseHelper.error(response, 403, errorStrings.notAllowed);
}
return next();
Expand Down
2 changes: 1 addition & 1 deletion server/middleware/ValidateUser.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ class ValidateUser {

static async checkVerified(req, res, next) {
try {
const user = await usersModel.findUserByEmail(req.user.email);
const user = await usersModel.findUserById(req.user.id);
if (user.status !== 'verified') {
return responseHelper.error(res, 401, errorStrings.notVerified);
}
Expand Down
10 changes: 0 additions & 10 deletions server/model/db/seed.db.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,6 @@ const repayments = [
loanid: loans[4].id,
amount: 11760.00,
},
{
id: '70798c7b-02b7-4145-a412-7e24fb0c694b',
loanid: loans[1].id,
amount: 12000.00,
},
{
id: 'd389e5ff-a831-464e-8603-eea1eae8caef',
loanid: loans[1].id,
amount: 11000.00,
},
{
id: '51eec787-ed02-43fc-8c30-f505bbd7b1b5',
loanid: loans[2].id,
Expand Down
6 changes: 2 additions & 4 deletions server/model/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,8 @@ class Model {
}
}

async selectWithJoin(columns, selectors, values) {
const queryString = `SELECT ${columns} FROM ${this.table} repay
JOIN loans ON (repay.loanid = loans.id)
JOIN users ON (loans.loanuser = users.email)
async selectWithJoin(columns, selectors, joinStatement, values) {
const queryString = `SELECT ${columns} FROM ${this.table} ${joinStatement}
WHERE ${selectors} `;
debug('app/debug')(queryString);
try {
Expand Down
35 changes: 21 additions & 14 deletions server/model/repaymentsModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,19 @@ class RepaymentsModel extends Model {

async getLoanRepayments(loanId) {
try {
const { rows } = await this.selectWithJoin(
'repay.id, loanid, paymentinstallment monthlyinstallment, repay.createdon, repay.amount paidamount, loans.amount, loans.balance, loans.createdon loandate, loans.loanuser, users.firstname, users.lastname, users.address',
'loanid=$1', [loanId],
const user = await loansModel.selectWithJoin(
'amount, tenor, balance, createdon loandate, paymentinstallment monthlyinstallment, loanuser, firstname, lastname, address',
'loans.id=$1',
'JOIN users ON (loans.loanuser = users.email)',
[loanId],
);
return rows;
if (user.rows.length < 1) {
return 'no-loan';
}
const [result] = user.rows;
const { rows } = await this.selectWhere('*', 'loanid=$1', [loanId]);
result.repayments = rows;
return result;
} catch (error) {
throw error;
}
Expand All @@ -49,17 +57,16 @@ class RepaymentsModel extends Model {
const { rows } = await this.insert('id, loanid, amount', '$1, $2, $3', [uuid(), loanId, Number.parseFloat(amount)]);
const updatedLoan = await loansModel.updateLoanAfterRepayment(loanId, amount, loan.balance);

// get paid amount because when repayment is merged with loan, amount will be overwritten
rows[0].paidamount = rows[0].amount;

// get monthlyinstallment as given in the response specification
rows[0].monthlyinstallment = updatedLoan.paymentinstallment;

// remove paymentinstallment since we already have monthlyinstallment
delete updatedLoan.paymentinstallment;
const user = await loansModel.selectWithJoin(
'amount, tenor, balance, createdon loandate, paymentinstallment monthlyinstallment, loanuser, firstname, lastname, address',
'loans.id=$1',
'JOIN users ON (loans.loanuser = users.email)',
[updatedLoan.id],
);

Object.assign(rows[0], updatedLoan); // merge repayment and it's loan together into repayment
return rows[0];
const result = user.rows[0];
[result.repayment] = rows;
return result;
} catch (error) {
throw error;
}
Expand Down
17 changes: 16 additions & 1 deletion server/model/usersModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class UsersModel extends Model {
}

/**
* check if user email already exists
* Find a user by email
* @param {String} email
* @return boolean
*/
Expand All @@ -71,6 +71,21 @@ class UsersModel extends Model {
}
}

/**
* Find a user by id
* @param {String} id
* @return boolean
*/

async findUserById(id) {
try {
const { rows } = await this.selectWhere('*', 'id=$1', [id]);
return rows[0];
} catch (error) {
throw error;
}
}

/**
* verify a specific user by email
* @param {string} email email of the user to verify
Expand Down
71 changes: 49 additions & 22 deletions server/tests/repaymentsTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,27 +43,52 @@ describe('Repayment Controller', () => {
expect(res).to.have.status(200);
expect(res.body).to.be.a('object');
expect(res.body).to.have.property('data');
expect(res.body.data).to.be.a('array');
expect(res.body.data[0]).to.be.a('object');
expect(res.body.data[0]).to.have.property('id');
expect(res.body.data[0]).to.have.property('loanid');
expect(res.body.data[0]).to.have.property('createdon');
expect(res.body.data[0]).to.have.property('amount');
expect(res.body.data[0]).to.have.property('monthlyinstallment');
expect(res.body.data).to.be.a('object');
expect(res.body.data.repayments).to.be.a('array');
expect(res.body.data.repayments[0]).to.have.property('id');
expect(res.body.data.repayments[0]).to.have.property('loanid');
expect(res.body.data.repayments[0]).to.have.property('createdon');
expect(res.body.data.repayments[0]).to.have.property('amount');
done();
});
});

it('it should return empty data set if no repayment is found', (done) => {
chai.request(app)
.get(`${loansUrl}/00298627-71e4-4ede-af91-aec1862ffe55/repayments`)
.get(`${loansUrl}/ff741315-7075-4488-8627-8f8ccccbada6/repayments`)
.set('Authorization', currentToken)
.end((error, res) => {
expect(res).to.have.status(200);
expect(res.body).to.be.a('object');
expect(res.body).to.have.property('data');
expect(res.body.data).to.be.a('array');
expect(res.body.data).to.eql([]);
expect(res.body.data).to.be.a('object');
expect(res.body.data.repayments).to.eql([]);
done();
});
});

it('it should not get repayment if uuid is wrong', (done) => {
chai.request(app)
.get(`${loansUrl}/ff741315-7075-4488-8627-8f8ccccbada67/repayments`)
.set('Authorization', currentToken)
.end((error, res) => {
expect(res).to.have.status(400);
expect(res.body).to.be.a('object');
expect(res.body).to.have.property('error');
expect(res.body.error).to.equal(errorStrings.validLoanId);
done();
});
});

it('it should not get repayments if uuid is correct but loan does not exist in database', (done) => {
chai.request(app)
.get(`${loansUrl}/ff741315-7075-4488-8627-8f8cc7cbada6/repayments`)
.set('Authorization', currentToken)
.end((error, res) => {
expect(res).to.have.status(404);
expect(res.body).to.be.a('object');
expect(res.body).to.have.property('error');
expect(res.body.error).to.equal(errorStrings.noLoan);
done();
});
});
Expand Down Expand Up @@ -129,13 +154,14 @@ describe('Repayment Controller', () => {
expect(res.body).to.be.a('object');
expect(res.body).to.have.property('data');
expect(res.body.data).to.be.a('object');
expect(res.body.data).to.have.property('id');
expect(res.body.data).to.have.property('loanid');
expect(res.body.data).to.have.property('createdon');
expect(res.body.data).to.have.property('repayment');
expect(res.body.data).to.have.property('amount');
expect(res.body.data).to.have.property('monthlyinstallment');
expect(res.body.data).to.have.property('paidamount');
expect(res.body.data).to.have.property('balance');
expect(res.body.data).to.have.property('tenor');
expect(res.body.data.repayment).to.be.a('object');
expect(res.body.data.repayment).to.have.property('id');
expect(res.body.data.repayment).to.have.property('loanid');
expect(res.body.data.repayment).to.have.property('createdon');
expect(res.body.data.repayment).to.have.property('amount');
done();
});
});
Expand All @@ -150,13 +176,14 @@ describe('Repayment Controller', () => {
expect(res.body).to.be.a('object');
expect(res.body).to.have.property('data');
expect(res.body.data).to.be.a('object');
expect(res.body.data).to.have.property('id');
expect(res.body.data).to.have.property('loanid');
expect(res.body.data).to.have.property('createdon');
expect(res.body.data).to.have.property('repayment');
expect(res.body.data).to.have.property('amount');
expect(res.body.data).to.have.property('monthlyinstallment');
expect(res.body.data).to.have.property('paidamount');
expect(res.body.data).to.have.property('balance');
expect(res.body.data).to.have.property('tenor');
expect(res.body.data.repayment).to.be.a('object');
expect(res.body.data.repayment).to.have.property('id');
expect(res.body.data.repayment).to.have.property('loanid');
expect(res.body.data.repayment).to.have.property('createdon');
expect(res.body.data.repayment).to.have.property('amount');
done();
});
});
Expand Down

0 comments on commit ba61778

Please sign in to comment.