Skip to content

Commit

Permalink
Merge pull request #66 from tolulope-od/ft-specific-account-transacti…
Browse files Browse the repository at this point in the history
…on-165355639

#165355639 User can view a specific account transaction API endpoint
  • Loading branch information
tolulope-od committed Apr 22, 2019
2 parents dc3f74d + 5ca785d commit 379e426
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 8 deletions.
55 changes: 51 additions & 4 deletions server/controllers/TransactionController.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default class TransactionController {
log(info);
});
const data = {
transactionId: newTransaction[0].id,
transactionId: newTransaction[0].transactionid,
accountNumber,
amount: newTransaction[0].amount,
cashier: newTransaction[0].cashier,
Expand All @@ -96,10 +96,10 @@ export default class TransactionController {
}

/**
* @description Credit an account
* @description Debit an account
* @param {Object} req The request object
* @param {Object} res The response object
* @route POST /api/v1/transactions/<account-number>/credit
* @route POST /api/v1/transactions/<account-number>/debit
* @returns {Object} status code, data and message properties
* @access private Admin or staff only
*/
Expand Down Expand Up @@ -159,7 +159,7 @@ export default class TransactionController {
);
transporter.sendMail(emailNotif.getMailOptions(), (err, info) => err || info);
const data = {
transactionId: newTransaction[0].id,
transactionId: newTransaction[0].transactionid,
accountNumber,
amount: newTransaction[0].amount,
cashier: newTransaction[0].cashier,
Expand All @@ -172,4 +172,51 @@ export default class TransactionController {
message: 'Account debited successfully'
});
}

/**
* @description View an account transaction
* @param {Object} req The request object
* @param {Object} res The response object
* @route POST /api/v1/transactions/:transactionId
* @returns {Object} status code, data and message properties
* @access private Userr and Staff
*/
static async getSpecificTransaction(req, res) {
const { transactionId } = req.params;
const { id, type } = req.decoded;
if (type === 'client') {
const userAccountTransaction = await transactions.select(
['*'],
[`transactionid=${transactionId} AND owner=${id}`]
);

if (isEmpty(userAccountTransaction)) {
return res.status(404).json({
status: 404,
error: 'Transaction does not exist'
});
}

return res.status(200).json({
status: 200,
data: userAccountTransaction
});
}

const userAccountTransaction = await transactions.select(
['*'],
[`transactionid=${transactionId}`]
);

if (isEmpty(userAccountTransaction)) {
return res.status(404).json({
status: 404,
error: 'Transaction does not exist'
});
}
return res.status(200).json({
status: 200,
data: userAccountTransaction
});
}
}
2 changes: 1 addition & 1 deletion server/db/seeder.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const createTable = async () => {
FOREIGN KEY(owner) REFERENCES users(id) ON DELETE CASCADE
);
CREATE TABLE IF NOT EXISTS transactions (
id SERIAL PRIMARY KEY,
transactionId SERIAL PRIMARY KEY,
type VARCHAR NOT NULL,
accountNumber BIGINT NOT NULL,
owner INT NOT NULL,
Expand Down
14 changes: 12 additions & 2 deletions server/routes/TransactionRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ import asyncErrorHandler from '../middleware/asyncErrorHandler';

const router = Router();

const { creditAccount, debitAccount } = TransactionController;
const { creditAccount, debitAccount, getSpecificTransaction } = TransactionController;
const { checkToken } = Authorization;
const { validateCreditTransaction, validateDebitTransaction } = TransactionValidation;
const {
validateCreditTransaction,
validateDebitTransaction,
validateGetSingleTransaction
} = TransactionValidation;
const { validateGetSingleAccount } = AccountValidation;

router.post(
Expand All @@ -26,5 +30,11 @@ router.post(
validateDebitTransaction,
asyncErrorHandler(debitAccount)
);
router.get(
'/:transactionId',
checkToken,
validateGetSingleTransaction,
asyncErrorHandler(getSpecificTransaction)
);

export default router;
23 changes: 23 additions & 0 deletions server/validation/transactionValidation.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,27 @@ export default class TransactionValidation {

return next();
}

/**
* @description Function to check that the input parameters for transactions are properly fillerd
* @param {Object} req The request object
* @param {Object} res The response object
* @param {Function} next Call back function
* @route GET /api/v1/transactions/:transactionId
*
* @returns {Object} status code and error message properties
* @access private
*/
static validateGetSingleTransaction(req, res, next) {
const { transactionId } = req.params;
const isNum = /^\d+$/; // gotten from Scott Evernden on Stack Overflow
if (!isNum.test(transactionId)) {
return res.status(400).json({
status: 400,
error: 'Transaction ID can only contain digits'
});
}

return next();
}
}
90 changes: 89 additions & 1 deletion test/transaction.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ describe('Transaction Route', () => {
});
});

it('Shold not debit an account if the input is empty', done => {
it('Should not debit an account if the input is empty', done => {
const debitTransaction = {
debitAmount: ''
};
Expand Down Expand Up @@ -384,4 +384,92 @@ describe('Transaction Route', () => {
done();
});
});

it('Should get a single transaction for a client', done => {
const transactionId = 3;
chai
.request(app)
.get(`${API_PREFIX}/transactions/${transactionId}`)
.set('Authorization', authToken)
.end((err, res) => {
expect(res.body)
.to.have.property('status')
.eql(200);
expect(res.body).to.have.nested.property('data[0].transactionid');
expect(res.body).to.have.nested.property('data[0].type');
expect(res.status).to.equal(200);
done();
});
});

it('Should get a single transaction for a staff', done => {
const transactionId = 2;
chai
.request(app)
.get(`${API_PREFIX}/transactions/${transactionId}`)
.set('Authorization', staffAuthToken)
.end((err, res) => {
expect(res.body)
.to.have.property('status')
.eql(200);
expect(res.body).to.have.nested.property('data[0].transactionid');
expect(res.body).to.have.nested.property('data[0].type');
expect(res.status).to.equal(200);
done();
});
});

it("Should not let a user view a transaction that doesn't belong to them", done => {
const transactionId = 2;
chai
.request(app)
.get(`${API_PREFIX}/transactions/${transactionId}`)
.set('Authorization', authToken)
.end((err, res) => {
expect(res.body)
.to.have.property('status')
.eql(404);
expect(res.body)
.to.have.property('error')
.eql('Transaction does not exist');
expect(res.status).to.equal(404);
done();
});
});

it("Should return an error if a staff tries to view a transaction that doesn't exist", done => {
const transactionId = 12;
chai
.request(app)
.get(`${API_PREFIX}/transactions/${transactionId}`)
.set('Authorization', staffAuthToken)
.end((err, res) => {
expect(res.body)
.to.have.property('status')
.eql(404);
expect(res.body)
.to.have.property('error')
.eql('Transaction does not exist');
expect(res.status).to.equal(404);
done();
});
});

it('Should not return any data if the transactionId is not formatted properly', done => {
const transactionId = '12sw';
chai
.request(app)
.get(`${API_PREFIX}/transactions/${transactionId}`)
.set('Authorization', staffAuthToken)
.end((err, res) => {
expect(res.body)
.to.have.property('status')
.eql(400);
expect(res.body)
.to.have.property('error')
.eql('Transaction ID can only contain digits');
expect(res.status).to.equal(400);
done();
});
});
});

0 comments on commit 379e426

Please sign in to comment.