Skip to content

Commit 6afab97

Browse files
committed
Merge branch 'develop' into website-backend-349
2 parents bf87e5f + 09c53d8 commit 6afab97

File tree

18 files changed

+455
-79
lines changed

18 files changed

+455
-79
lines changed

controllers/stocks.js

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,36 @@ const fetchStocks = async (req, res) => {
2929
try {
3030
const allStock = await stocks.fetchStocks()
3131
return res.json({
32-
message: 'Stocks returned successfully!',
32+
message: allStock.length > 0 ? 'Stocks returned successfully!' : 'No stocks found',
3333
stock: allStock.length > 0 ? allStock : []
3434
})
3535
} catch (err) {
3636
logger.error(`Error while fetching stocks ${err}`)
3737
return res.boom.badImplementation('An internal server error occurred')
3838
}
3939
}
40+
/**
41+
* Fetches all the stocks of the user
42+
*
43+
* @param req {Object} - Express request object
44+
* @param res {Object} - Express response object
45+
*/
46+
const getSelfStocks = async (req, res) => {
47+
try {
48+
const { id: userId } = req.userData
49+
const userStocks = await stocks.fetchUserStocks(userId)
50+
return res.json({
51+
message: userStocks.length > 0 ? 'User stocks returned successfully!' : 'No stocks found',
52+
userStocks
53+
})
54+
} catch (err) {
55+
logger.error(`Error while getting user stocks ${err}`)
56+
return res.boom.badImplementation('An internal server error occurred')
57+
}
58+
}
4059

4160
module.exports = {
4261
addNewStock,
43-
fetchStocks
62+
fetchStocks,
63+
getSelfStocks
4464
}

controllers/trading.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const tradeModel = require('../models/trading')
1+
const tradeService = require('../services/tradingService')
22
/**
33
* New Trading Request
44
*
@@ -13,15 +13,15 @@ const trade = async (req, res) => {
1313
username,
1414
userId
1515
}
16-
const { canUserTrade, errorMessage, userBalance } = await tradeModel.trade(tradeStockData)
16+
const { canUserTrade, errorMessage, userBalance } = await tradeService.trade(tradeStockData)
1717

1818
if (!canUserTrade) {
1919
return res.boom.forbidden(errorMessage)
2020
}
2121

2222
return res.json({ userBalance })
2323
} catch (err) {
24-
logger.error(`Error while updating task: ${err}`)
24+
logger.error(`Error during trading: ${err}`)
2525
return res.boom.badImplementation('An internal server error occurred')
2626
}
2727
}

controllers/users.js

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11

22
const userQuery = require('../models/users')
3-
const accountOwners = require('../mockdata/appOwners')
43
const imageService = require('../services/imageService')
54
/**
65
* Fetches the data about our users
@@ -23,22 +22,6 @@ const getUsers = async (req, res) => {
2322
}
2423
}
2524

26-
/**
27-
* Fetches the data about our account Owners
28-
*
29-
* @param req {Object} - Express request object
30-
* @param res {Object} - Express response object
31-
*/
32-
33-
const getAccountOwners = async (req, res) => {
34-
try {
35-
return accountOwners
36-
} catch (error) {
37-
logger.error(`Error while fetching application owners: ${error}`)
38-
return res.boom.badImplementation('Something went wrong please contact admin')
39-
}
40-
}
41-
4225
/**
4326
* Fetches the data about user with given id
4427
*
@@ -166,6 +149,5 @@ module.exports = {
166149
getSelfDetails,
167150
getUser,
168151
getUsernameAvailabilty,
169-
getAccountOwners,
170152
postUserPicture
171153
}

docs/swaggerDefinition.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,29 @@ const swaggerOptions = {
524524
}
525525
}
526526
},
527+
userStocks: {
528+
type: 'object',
529+
properties: {
530+
userId: {
531+
type: 'string'
532+
},
533+
stockId: {
534+
type: 'string'
535+
},
536+
stockName: {
537+
type: 'string'
538+
},
539+
quantity: {
540+
type: 'number'
541+
},
542+
orderValue: {
543+
type: 'number'
544+
},
545+
initialStockValue: {
546+
type: 'number'
547+
}
548+
}
549+
},
527550
auctions: {
528551
type: 'object',
529552
properties: {

firebase.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"emulators": {
33
"firestore": {
4-
"port": 8080
4+
"port": 8081
55
},
66
"ui": {
77
"enabled": true,

middlewares/authorization.js

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,54 @@
1-
const { isSuperUser } = require('../models/members')
2-
module.exports = (req, res, next) => {
3-
try {
4-
const { username } = req.userData
5-
if (!isSuperUser(username)) {
6-
return res.boom.forbidden('You are not allowed to perform this action.')
1+
/**
2+
* Describe the priority order of roles. Any route requiring `x` role is allowed
3+
* for user having authority of role `x` or a higher authority.
4+
* For e.g
5+
* - Route requiring `superUser` role is only allowed for `super_user`.
6+
* - Route requiring `appOwner` role is allowed for `superUser` and `app_owner`.
7+
*/
8+
const REQUIRED_ROLES_PRIORITY = {
9+
superUser: ['super_user'],
10+
appOwner: ['app_owner', 'super_user'],
11+
default: ['default', 'super_user', 'app_owner']
12+
}
13+
14+
/**
15+
* Check if the user has enough authorization based on their role.
16+
* User would have following roles schema -
17+
* userRoles = {app_owner: true, super_user: true}
18+
* @param {String} requiredRole - Required role level to authorize request.
19+
* @param {Object} userRoles - Roles information of the current user.
20+
* @returns {Boolean} - Whether the current user is authorized for required role level.
21+
*/
22+
const userHasPermission = (requiredRole, userRoles) => {
23+
const allowedRoles = REQUIRED_ROLES_PRIORITY[`${requiredRole}`] || ['default']
24+
return allowedRoles.some((role) => {
25+
return Boolean(userRoles[`${role}`])
26+
})
27+
}
28+
29+
/**
30+
* Create an authorization middleware for a route based on the required role needed
31+
* for that route. Currently following roles are supported:
32+
* - `authorizeUser('superUser')`
33+
* - `authorizeUser('appOwner')`
34+
* Note: This must be added on routes after the `authenticate` middleware.
35+
* @param {String} requiredRole - The least role authority required for a route.
36+
* @returns {Function} - A middleware function that authorizes given role.
37+
*/
38+
const authorizeUser = (requiredRole) => {
39+
return (req, res, next) => {
40+
const { roles = {} } = req.userData
41+
// All users should have `default` role
42+
roles.default = true
43+
44+
if (!userHasPermission(requiredRole, roles)) {
45+
return res.boom.unauthorized('You are not authorized for this action.')
746
}
847
return next()
9-
} catch (err) {
10-
logger.error(err)
11-
return res.boom.forbidden('You are not allowed to perform this action.')
1248
}
1349
}
50+
51+
module.exports = {
52+
authorizeUser,
53+
userHasPermission
54+
}

models/members.js

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,6 @@ const fetchMembers = async () => {
3737
}
3838
}
3939

40-
/**
41-
* Checks whether the user is a superuser
42-
* @return {Boolean}
43-
*/
44-
45-
const isSuperUser = (username) => {
46-
return username === 'ankush'
47-
}
48-
4940
module.exports = {
50-
fetchMembers,
51-
isSuperUser
41+
fetchMembers
5242
}

models/stocks.js

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
const firestore = require('../utils/firestore')
22
const stocksModel = firestore.collection('stocks')
3+
const userStocksModel = firestore.collection('user-stocks')
4+
35
/**
46
* Adds Stocks
57
*
@@ -38,7 +40,65 @@ const fetchStocks = async () => {
3840
}
3941
}
4042

43+
/**
44+
* Fetches the user stocks
45+
* @return {Promise<userStocks|object>}
46+
*/
47+
const fetchUserStocks = async (userId, stockId = null) => {
48+
try {
49+
let userStocksRef = ''
50+
const query = userStocksModel.where('userId', '==', userId)
51+
if (stockId) {
52+
userStocksRef = await query.where('stockId', '==', stockId).get()
53+
const [userStocks] = userStocksRef.docs
54+
if (userStocks) {
55+
return { id: userStocks.id, ...userStocks.data() }
56+
}
57+
return {}
58+
}
59+
60+
userStocksRef = await query.get()
61+
const userStocks = []
62+
userStocksRef.forEach((stock) => {
63+
userStocks.push({
64+
id: stock.id,
65+
...stock.data()
66+
})
67+
})
68+
return userStocks
69+
} catch (err) {
70+
logger.error('Error retrieving user stocks', err)
71+
throw err
72+
}
73+
}
74+
75+
/**
76+
* Update Users Stocks
77+
* @return {Promise<userStocks|object>}
78+
*/
79+
const updateUserStocks = async (userId, stockData) => {
80+
try {
81+
const userStocks = await fetchUserStocks(userId, stockData.stockId)
82+
if (!userStocks.id) {
83+
await userStocksModel.add({
84+
userId,
85+
...stockData
86+
})
87+
return true
88+
}
89+
90+
const userStocksRef = userStocksModel.doc(userStocks.id)
91+
const res = await userStocksRef.update(stockData)
92+
return !!res
93+
} catch (err) {
94+
logger.error('Error updating users stocks', err)
95+
throw err
96+
}
97+
}
98+
4199
module.exports = {
42100
addStock,
43-
fetchStocks
101+
fetchStocks,
102+
fetchUserStocks,
103+
updateUserStocks
44104
}

public/apiSchema.json

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

routes/stocks.js

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
const express = require('express')
22
const router = express.Router()
33
const authenticate = require('../middlewares/authenticate')
4-
const authorization = require('../middlewares/authorization')
5-
const { addNewStock, fetchStocks } = require('../controllers/stocks')
4+
const { authorizeUser } = require('../middlewares/authorization')
5+
const { addNewStock, fetchStocks, getSelfStocks } = require('../controllers/stocks')
66
const { createStock } = require('../middlewares/validators/stocks')
77

88
/**
@@ -62,6 +62,36 @@ router.get('/', fetchStocks)
6262
* schema:
6363
* $ref: '#/components/schemas/errors/badImplementation'
6464
*/
65-
router.post('/', authenticate, authorization, createStock, addNewStock)
65+
router.post('/', authenticate, authorizeUser('superUser'), createStock, addNewStock)
66+
67+
/**
68+
* @swagger
69+
* /stocks/user/self:
70+
* get:
71+
* summary: Used to get all the stocks of the user
72+
* tags:
73+
* - User Stocks
74+
* responses:
75+
* 200:
76+
* description: returns stocks of the user
77+
* content:
78+
* application/json:
79+
* schema:
80+
* $ref: '#/components/schemas/userStocks'
81+
* 401:
82+
* description: unAuthorized
83+
* content:
84+
* application/json:
85+
* schema:
86+
* $ref: '#/components/schemas/errors/unAuthorized'
87+
* 500:
88+
* description: badImplementation
89+
* content:
90+
* application/json:
91+
* schema:
92+
* $ref: '#/components/schemas/errors/badImplementation'
93+
*/
94+
95+
router.get('/user/self', authenticate, getSelfStocks)
6696

6797
module.exports = router

0 commit comments

Comments
 (0)