diff --git a/controllers/stocks.js b/controllers/stocks.js index 314fa5520..1df15c829 100644 --- a/controllers/stocks.js +++ b/controllers/stocks.js @@ -29,7 +29,7 @@ const fetchStocks = async (req, res) => { try { const allStock = await stocks.fetchStocks() return res.json({ - message: 'Stocks returned successfully!', + message: allStock.length > 0 ? 'Stocks returned successfully!' : 'No stocks found', stock: allStock.length > 0 ? allStock : [] }) } catch (err) { @@ -37,8 +37,28 @@ const fetchStocks = async (req, res) => { return res.boom.badImplementation('An internal server error occurred') } } +/** + * Fetches all the stocks of the user + * + * @param req {Object} - Express request object + * @param res {Object} - Express response object + */ +const getSelfStocks = async (req, res) => { + try { + const { id: userId } = req.userData + const userStocks = await stocks.fetchUserStocks(userId) + return res.json({ + message: userStocks.length > 0 ? 'User stocks returned successfully!' : 'No stocks found', + userStocks + }) + } catch (err) { + logger.error(`Error while getting user stocks ${err}`) + return res.boom.badImplementation('An internal server error occurred') + } +} module.exports = { addNewStock, - fetchStocks + fetchStocks, + getSelfStocks } diff --git a/controllers/trading.js b/controllers/trading.js index bf1654dc6..c6ce381ca 100644 --- a/controllers/trading.js +++ b/controllers/trading.js @@ -1,4 +1,4 @@ -const tradeModel = require('../models/trading') +const tradeService = require('../services/tradingService') /** * New Trading Request * @@ -13,7 +13,7 @@ const trade = async (req, res) => { username, userId } - const { canUserTrade, errorMessage, userBalance } = await tradeModel.trade(tradeStockData) + const { canUserTrade, errorMessage, userBalance } = await tradeService.trade(tradeStockData) if (!canUserTrade) { return res.boom.forbidden(errorMessage) @@ -21,7 +21,7 @@ const trade = async (req, res) => { return res.json({ userBalance }) } catch (err) { - logger.error(`Error while updating task: ${err}`) + logger.error(`Error during trading: ${err}`) return res.boom.badImplementation('An internal server error occurred') } } diff --git a/docs/swaggerDefinition.js b/docs/swaggerDefinition.js index abe210203..cca89f95f 100644 --- a/docs/swaggerDefinition.js +++ b/docs/swaggerDefinition.js @@ -524,6 +524,29 @@ const swaggerOptions = { } } }, + userStocks: { + type: 'object', + properties: { + userId: { + type: 'string' + }, + stockId: { + type: 'string' + }, + stockName: { + type: 'string' + }, + quantity: { + type: 'number' + }, + orderValue: { + type: 'number' + }, + initialStockValue: { + type: 'number' + } + } + }, auctions: { type: 'object', properties: { diff --git a/models/stocks.js b/models/stocks.js index 54f9235f2..e95e5f2f1 100644 --- a/models/stocks.js +++ b/models/stocks.js @@ -1,5 +1,7 @@ const firestore = require('../utils/firestore') const stocksModel = firestore.collection('stocks') +const userStocksModel = firestore.collection('user-stocks') + /** * Adds Stocks * @@ -38,7 +40,65 @@ const fetchStocks = async () => { } } +/** + * Fetches the user stocks + * @return {Promise} + */ +const fetchUserStocks = async (userId, stockId = null) => { + try { + let userStocksRef = '' + const query = userStocksModel.where('userId', '==', userId) + if (stockId) { + userStocksRef = await query.where('stockId', '==', stockId).get() + const [userStocks] = userStocksRef.docs + if (userStocks) { + return { id: userStocks.id, ...userStocks.data() } + } + return {} + } + + userStocksRef = await query.get() + const userStocks = [] + userStocksRef.forEach((stock) => { + userStocks.push({ + id: stock.id, + ...stock.data() + }) + }) + return userStocks + } catch (err) { + logger.error('Error retrieving user stocks', err) + throw err + } +} + +/** + * Update Users Stocks + * @return {Promise} + */ +const updateUserStocks = async (userId, stockData) => { + try { + const userStocks = await fetchUserStocks(userId, stockData.stockId) + if (!userStocks.id) { + await userStocksModel.add({ + userId, + ...stockData + }) + return true + } + + const userStocksRef = userStocksModel.doc(userStocks.id) + const res = await userStocksRef.update(stockData) + return !!res + } catch (err) { + logger.error('Error updating users stocks', err) + throw err + } +} + module.exports = { addStock, - fetchStocks + fetchStocks, + fetchUserStocks, + updateUserStocks } diff --git a/public/apiSchema.json b/public/apiSchema.json index ee91e5221..07f5f0a6c 100644 --- a/public/apiSchema.json +++ b/public/apiSchema.json @@ -1 +1 @@ -{"openapi":"3.0.1","info":{"version":"1.0.0","title":"RDS API documentation","description":"This is documentation for Real Dev Squad's API. Find out more about Real dev squad at [http://realdevsquad.com](http://realdevsquad.com)","contact":{"name":"Real Dev Squad","url":"http://realdevsquad.com"}},"tags":[{"name":"Healthcheck","description":"API for health check in the system"},{"name":"Authentication","description":"Authentication routes"}],"servers":[{"url":"https://api.realdevsquad.com","description":"Local server URL"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"healthCheck":{"type":"object","properties":{"uptime":{"type":"number"}}},"tasks":{"type":"object","properties":{"title":{"type":"string"},"purpose":{"type":"string"},"featureUrl":{"type":"string"},"type":{"type":"string"},"links":{"type":"array","items":{"link1":{"type":"string"}}},"endsOn":{"type":"string"},"startedOn":{"type":"string"},"status":{"type":"string"},"assignee":{"type":"string"},"percentCompleted":{"type":"number"},"dependsOn":{"type":"array","items":{"taskid":{"type":"string"}}},"participants":{"type":"array","items":{"username":{"type":"string"}}},"completionAward":{"type":"object","properties":{"neelam":{"type":"number"},"dinero":{"type":"number"}}},"lossRate":{"type":"object","properties":{"neelam":{"type":"number"},"dinero":{"type":"number"}}},"isNoteworthy":{"type":"boolean"}}},"contributions":{"type":"object","properties":{"noteworthy":{"type":"array","items":{"type":"object","properties":{"task":{"type":"object","properties":{"title":{"type":"string"},"purpose":{"type":"string"},"featureUrl":{"type":"string"},"endsOn":{"type":"string"},"startedOn":{"type":"string"},"status":{"type":"string"},"dependsOn":{"type":"array","items":{"type":"string"}},"participants":{"type":"array","items":{"type":"object","properties":{"firstname":{"type":"string"},"lastname":{"type":"string"},"img":{"type":"string"},"username":{"type":"string"}}}},"isNoteworthy":{"type":"boolean"}}},"prList":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string"},"state":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"raisedBy":{"type":"string"}}}}}}},"all":{"type":"array","items":{"type":"object","properties":{"task":{"type":"object","properties":{"title":{"type":"string"},"purpose":{"type":"string"},"featureUrl":{"type":"string"},"endsOn":{"type":"string"},"startedOn":{"type":"string"},"status":{"type":"string"},"dependsOn":{"type":"array","items":{"type":"string"}},"participants":{"type":"array","items":{"type":"object","properties":{"firstname":{"type":"string"},"lastname":{"type":"string"},"img":{"type":"string"},"username":{"type":"string"}}}},"isNoteworthy":{"type":"boolean"}}},"prList":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string"},"state":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"raisedBy":{"type":"string"}}}}}}}}},"challenges":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"level":{"type":"string"},"start_date":{"type":"string"},"end_date":{"type":"string"},"is_active":{"type":"boolean"},"participants":{"type":"array","items":[]}}},"pullRequests":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string"},"state":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"readyForReview":{"type":"boolean"},"labels":{"type":"array","items":[]},"assignees":{"type":"array","items":[]}}},"recruiters":{"type":"object","properties":{"company":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"designation":{"type":"string"},"reason":{"type":"string"},"email":{"type":"string"},"currency":{"type":"string"},"package":{"type":"number"}}},"users":{"type":"object","properties":{"username":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"yoe":{"type":"number"},"company":{"type":"string"},"designation":{"type":"string"},"img":{"type":"string"},"github_display_name":{"type":"string"},"github_id":{"type":"string"},"linkedin_id":{"type":"string"},"twitter_id":{"type":"string"},"instagram_id":{"type":"string"},"site":{"type":"string"},"isMember":{"type":"boolean"},"tokens":{"type":"object"}}},"badges":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"imgUrl":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"userAvailable":{"type":"object","properties":{"isUsernameAvailable":{"type":"boolean"}}},"stocks":{"type":"object","properties":{"name":{"type":"string"},"price":{"type":"number"},"quantity":{"type":"number"}}},"trading":{"type":"object","properties":{"stockId":{"type":"string"},"tradeType":{"type":"string"},"stockName":{"type":"string"},"quantity":{"type":"number"},"listedPrice":{"type":"number"},"totalPrice":{"type":"number"}}},"tradingSuccess":{"type":"object","properties":{"userBalance":{"type":"number"}}},"auctions":{"type":"object","properties":{"id":{"type":"string"},"seller":{"type":"string"},"item_type":{"type":"string"},"quantity":{"type":"number"},"highest_bid":{"type":"number"},"highest_bidder":{"type":"number"},"start_time":{"type":"number"},"end_time":{"type":"number"},"bidders_and_bids":{"type":"array"}}},"errors":{"unAuthorized":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}},"notFound":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}},"forbidden":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}},"badImplementation":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}},"serverUnavailable":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}}}}},"paths":{"/auctions/:id":{"get":{"summary":"Fetches auction details for given auctionId","tags":["Auctions"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Auction details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/auctions"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/auctions":{"get":{"summary":"Get all the active (ongoing) auctions","tags":["Auctions"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"All ongoing auctions","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Auctions returned successfully!"},"auctions":{"type":"array","items":{"$ref":"#/components/schemas/auctions"}}}}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}},"post":{"summary":"Creates a new auction","tags":["Auctions"],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"New auction","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Auction created successfully!"},"id":{"type":"string"}}}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/auctions/bid/:id":{"post":{"summary":"Makes a new bid given auctionId","tags":["Auctions"],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"New bid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Successfully placed bid!"},"id":{"type":"string"}}}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/auth/github/callback":{"get":{"summary":"Authenticates the user using the GitHub Oauth 2.0. Redirects to the UI on successful login","tags":["Authentication"],"parameters":[{"in":"query","name":"code","required":true,"type":"string","description":"Temporary code returned by GitHub Oauth"}],"responses":{"302":{"description":"Redirects to the UI on successful login","headers":{"Cookie":{"type":"string","description":"Cookie containing authentication token"},"Location":{"type":"string","description":"Redirection URL"}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}}}}},"/badges":{"get":{"summary":"Get all the badges in system.","tags":["Badges"],"responses":{"200":{"description":"Badge details","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Badges returned successfully!"},"badges":{"type":"array","items":{"$ref":"#/components/schemas/badges"}}}}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/challenges":{"get":{"summary":"Used to get all the challenges","tags":["Challenges"],"responses":{"200":{"description":"Return challenges","content":{"application/json":{"schema":{"$ref":"#/components/schemas/challenges"}}}},"404":{"description":"No challenges found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}},"post":{"summary":"Post new challenge","tags":["Challenges"],"responses":{"200":{"description":"Post challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/challenges"}}}},"404":{"description":"Unable to add challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/challenges/subscribe":{"post":{"summary":"Subscribe user to challenge","tags":["Challenges"],"responses":{"200":{"description":"Subscribed sucessfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"User has subscribed to challenge"}}}}}},"404":{"description":"Unable to add challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/contributions/{username}":{"get":{"summary":"Used to get all the contributions of user","tags":["Contributions"],"responses":{"200":{"description":"Return contributions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/contributions"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/healthcheck":{"get":{"summary":"Use to check health status of the server.","tags":["Healthcheck"],"responses":{"200":{"description":"Server uptime status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/healthCheck"}}}}}}},"/healthcheck/v2":{"get":{"summary":"Sample route to test authentication middleware.","tags":["Healthcheck"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Server uptime status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/healthCheck"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}}}}},"/members":{"get":{"summary":"Gets details of all the Real Dev Squad members","tags":["Members"],"responses":{"200":{"description":"Details of all the RDS members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/members/idle":{"get":{"summary":"Gets details of idle members of the Real Dev Squad","tags":["Members"],"responses":{"200":{"description":"Details of inactive/idle members of the RDS members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/members/intro/:username":{"post":{"summary":"Posts details of the recruiter","tags":["Members"],"responses":{"200":{"description":"Details of the recruiter and the member in which recruiter is interested","content":{"application/json":{"schema":{"$ref":"#/components/schemas/recruiters"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/pullrequests/open":{"get":{"summary":"Latest 10 Pull Requests in Real Dev Squad","tags":["Pull Requests"],"responses":{"200":{"description":"Pull Requests","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Open PRs"},"pullRequests":{"type":"array","items":{"$ref":"#/components/schemas/pullRequests"}}}}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/pullrequests/stale":{"get":{"summary":"All open Pull Requests in Real Dev Squad","tags":["Pull Requests"],"responses":{"200":{"description":"Pull Requests","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Stale PRs"},"pullRequests":{"type":"array","items":{"$ref":"#/components/schemas/pullRequests"}}}}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/pullrequests/user/:username":{"get":{"summary":"Pull Requests by a user in Real Dev Squad","tags":["Pull Requests"],"responses":{"200":{"description":"Pull Requests","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Pull requests returned successfully!"},"pullRequests":{"type":"array","items":{"$ref":"#/components/schemas/pullRequests"}}}}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/stocks":{"get":{"summary":"Used to get all the stocks","tags":["Stocks"],"responses":{"200":{"description":"returns stocks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/stocks"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}},"post":{"summary":"Used to create new stock","tags":["Stocks"],"requestBody":{"description":"Stock data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/stocks"}}}},"responses":{"200":{"description":"returns newly created stock","content":{"application/json":{"schema":{"$ref":"#/components/schemas/stocks"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/tasks":{"get":{"summary":"Used to get all the tasks","tags":["Tasks"],"responses":{"200":{"description":"returns tasks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}},"post":{"summary":"Used to create new task","tags":["Tasks"],"requestBody":{"description":"Task data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"responses":{"200":{"description":"returns newly created task","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}},"patch":{"summary":"Used to update task details","tags":["Tasks"],"requestBody":{"description":"Task data to be updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"responses":{"204":{"description":"no content"},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/tasks/self":{"get":{"summary":"Use to get all the tasks of the logged in user","tags":["Tasks"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"returns all tasks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/trade/stock/new/self":{"post":{"summary":"Used for new trading request","tags":["Trading"],"security":[{"bearerAuth":[]}],"requestBody":{"description":"Trading details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/trading"}}}},"responses":{"200":{"description":"Successful trading details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tradingSuccess"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/users/self":{"patch":{"summary":"Use to update the user data.","requestBody":{"description":"User data to be updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"No content"},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}},"get":{"summary":"Use to get self details.","tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/users":{"get":{"summary":"Get all the users in system.","tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User details","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Users returned successfully!"},"users":{"type":"array","items":{"$ref":"#/components/schemas/users"}}}}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/users/isUsernameAvailable/:username":{"get":{"summary":"check user exists or not","tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User Availability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/userAvailable"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/users/:username":{"get":{"summary":"Get the details of user with provided id.","tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/wallet/":{"get":{"summary":"Gets the user wallet details","tags":["wallet"],"responses":{"200":{"description":"Return wallet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/wallet"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/wallet/:username":{"get":{"summary":"Gets the user wallet details for a username, if you are an authorized superuser","tags":["wallet"],"responses":{"200":{"description":"Return wallet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/wallet"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}}}} \ No newline at end of file +{"openapi":"3.0.1","info":{"version":"1.0.0","title":"RDS API documentation","description":"This is documentation for Real Dev Squad's API. Find out more about Real dev squad at [http://realdevsquad.com](http://realdevsquad.com)","contact":{"name":"Real Dev Squad","url":"http://realdevsquad.com"}},"tags":[{"name":"Healthcheck","description":"API for health check in the system"},{"name":"Authentication","description":"Authentication routes"}],"servers":[{"url":"http://localhost:3000","description":"Local server URL"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT"}},"schemas":{"healthCheck":{"type":"object","properties":{"uptime":{"type":"number"}}},"tasks":{"type":"object","properties":{"title":{"type":"string"},"purpose":{"type":"string"},"featureUrl":{"type":"string"},"type":{"type":"string"},"links":{"type":"array","items":{"link1":{"type":"string"}}},"endsOn":{"type":"string"},"startedOn":{"type":"string"},"status":{"type":"string"},"assignee":{"type":"string"},"percentCompleted":{"type":"number"},"dependsOn":{"type":"array","items":{"taskid":{"type":"string"}}},"participants":{"type":"array","items":{"username":{"type":"string"}}},"completionAward":{"type":"object","properties":{"neelam":{"type":"number"},"dinero":{"type":"number"}}},"lossRate":{"type":"object","properties":{"neelam":{"type":"number"},"dinero":{"type":"number"}}},"isNoteworthy":{"type":"boolean"}}},"contributions":{"type":"object","properties":{"noteworthy":{"type":"array","items":{"type":"object","properties":{"task":{"type":"object","properties":{"title":{"type":"string"},"purpose":{"type":"string"},"featureUrl":{"type":"string"},"endsOn":{"type":"string"},"startedOn":{"type":"string"},"status":{"type":"string"},"dependsOn":{"type":"array","items":{"type":"string"}},"participants":{"type":"array","items":{"type":"object","properties":{"firstname":{"type":"string"},"lastname":{"type":"string"},"img":{"type":"string"},"username":{"type":"string"}}}},"isNoteworthy":{"type":"boolean"}}},"prList":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string"},"state":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"raisedBy":{"type":"string"}}}}}}},"all":{"type":"array","items":{"type":"object","properties":{"task":{"type":"object","properties":{"title":{"type":"string"},"purpose":{"type":"string"},"featureUrl":{"type":"string"},"endsOn":{"type":"string"},"startedOn":{"type":"string"},"status":{"type":"string"},"dependsOn":{"type":"array","items":{"type":"string"}},"participants":{"type":"array","items":{"type":"object","properties":{"firstname":{"type":"string"},"lastname":{"type":"string"},"img":{"type":"string"},"username":{"type":"string"}}}},"isNoteworthy":{"type":"boolean"}}},"prList":{"type":"array","items":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string"},"state":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"raisedBy":{"type":"string"}}}}}}}}},"challenges":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"level":{"type":"string"},"start_date":{"type":"string"},"end_date":{"type":"string"},"is_active":{"type":"boolean"},"participants":{"type":"array","items":[]}}},"pullRequests":{"type":"object","properties":{"title":{"type":"string"},"url":{"type":"string"},"state":{"type":"string"},"createdAt":{"type":"string"},"updatedAt":{"type":"string"},"readyForReview":{"type":"boolean"},"labels":{"type":"array","items":[]},"assignees":{"type":"array","items":[]}}},"recruiters":{"type":"object","properties":{"company":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"designation":{"type":"string"},"reason":{"type":"string"},"email":{"type":"string"},"currency":{"type":"string"},"package":{"type":"number"}}},"users":{"type":"object","properties":{"username":{"type":"string"},"first_name":{"type":"string"},"last_name":{"type":"string"},"yoe":{"type":"number"},"company":{"type":"string"},"designation":{"type":"string"},"img":{"type":"string"},"github_display_name":{"type":"string"},"github_id":{"type":"string"},"linkedin_id":{"type":"string"},"twitter_id":{"type":"string"},"instagram_id":{"type":"string"},"site":{"type":"string"},"isMember":{"type":"boolean"},"tokens":{"type":"object"}}},"badges":{"type":"object","properties":{"title":{"type":"string"},"description":{"type":"string"},"imgUrl":{"type":"string"},"users":{"type":"array","items":{"type":"string"}}}},"userAvailable":{"type":"object","properties":{"isUsernameAvailable":{"type":"boolean"}}},"stocks":{"type":"object","properties":{"name":{"type":"string"},"price":{"type":"number"},"quantity":{"type":"number"}}},"trading":{"type":"object","properties":{"stockId":{"type":"string"},"tradeType":{"type":"string"},"stockName":{"type":"string"},"quantity":{"type":"number"},"listedPrice":{"type":"number"},"totalPrice":{"type":"number"}}},"tradingSuccess":{"type":"object","properties":{"userBalance":{"type":"number"}}},"userStocks":{"type":"object","properties":{"userId":{"type":"string"},"stockId":{"type":"string"},"stockName":{"type":"string"},"quantity":{"type":"number"},"orderValue":{"type":"number"},"initialStockValue":{"type":"number"}}},"auctions":{"type":"object","properties":{"id":{"type":"string"},"seller":{"type":"string"},"item_type":{"type":"string"},"quantity":{"type":"number"},"highest_bid":{"type":"number"},"highest_bidder":{"type":"number"},"start_time":{"type":"number"},"end_time":{"type":"number"},"bidders_and_bids":{"type":"array"}}},"errors":{"unAuthorized":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}},"notFound":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}},"forbidden":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}},"badImplementation":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}},"serverUnavailable":{"type":"object","properties":{"statusCode":{"type":"integer"},"error":{"type":"string"},"message":{"type":"string"}}}}}},"paths":{"/auctions/:id":{"get":{"summary":"Fetches auction details for given auctionId","tags":["Auctions"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Auction details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/auctions"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/auctions":{"get":{"summary":"Get all the active (ongoing) auctions","tags":["Auctions"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"All ongoing auctions","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Auctions returned successfully!"},"auctions":{"type":"array","items":{"$ref":"#/components/schemas/auctions"}}}}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}},"post":{"summary":"Creates a new auction","tags":["Auctions"],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"New auction","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Auction created successfully!"},"id":{"type":"string"}}}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/auctions/bid/:id":{"post":{"summary":"Makes a new bid given auctionId","tags":["Auctions"],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"New bid","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Successfully placed bid!"},"id":{"type":"string"}}}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/auth/github/callback":{"get":{"summary":"Authenticates the user using the GitHub Oauth 2.0. Redirects to the UI on successful login","tags":["Authentication"],"parameters":[{"in":"query","name":"code","required":true,"type":"string","description":"Temporary code returned by GitHub Oauth"}],"responses":{"302":{"description":"Redirects to the UI on successful login","headers":{"Cookie":{"type":"string","description":"Cookie containing authentication token"},"Location":{"type":"string","description":"Redirection URL"}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}}}}},"/badges":{"get":{"summary":"Get all the badges in system.","tags":["Badges"],"responses":{"200":{"description":"Badge details","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Badges returned successfully!"},"badges":{"type":"array","items":{"$ref":"#/components/schemas/badges"}}}}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/challenges":{"get":{"summary":"Used to get all the challenges","tags":["Challenges"],"responses":{"200":{"description":"Return challenges","content":{"application/json":{"schema":{"$ref":"#/components/schemas/challenges"}}}},"404":{"description":"No challenges found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}},"post":{"summary":"Post new challenge","tags":["Challenges"],"responses":{"200":{"description":"Post challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/challenges"}}}},"404":{"description":"Unable to add challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/challenges/subscribe":{"post":{"summary":"Subscribe user to challenge","tags":["Challenges"],"responses":{"200":{"description":"Subscribed sucessfully","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"User has subscribed to challenge"}}}}}},"404":{"description":"Unable to add challenge","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/contributions/{username}":{"get":{"summary":"Used to get all the contributions of user","tags":["Contributions"],"responses":{"200":{"description":"Return contributions","content":{"application/json":{"schema":{"$ref":"#/components/schemas/contributions"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/healthcheck":{"get":{"summary":"Use to check health status of the server.","tags":["Healthcheck"],"responses":{"200":{"description":"Server uptime status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/healthCheck"}}}}}}},"/healthcheck/v2":{"get":{"summary":"Sample route to test authentication middleware.","tags":["Healthcheck"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Server uptime status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/healthCheck"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}}}}},"/members":{"get":{"summary":"Gets details of all the Real Dev Squad members","tags":["Members"],"responses":{"200":{"description":"Details of all the RDS members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/members/idle":{"get":{"summary":"Gets details of idle members of the Real Dev Squad","tags":["Members"],"responses":{"200":{"description":"Details of inactive/idle members of the RDS members","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/members/intro/:username":{"post":{"summary":"Posts details of the recruiter","tags":["Members"],"responses":{"200":{"description":"Details of the recruiter and the member in which recruiter is interested","content":{"application/json":{"schema":{"$ref":"#/components/schemas/recruiters"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/pullrequests/open":{"get":{"summary":"Latest 10 Pull Requests in Real Dev Squad","tags":["Pull Requests"],"responses":{"200":{"description":"Pull Requests","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Open PRs"},"pullRequests":{"type":"array","items":{"$ref":"#/components/schemas/pullRequests"}}}}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/pullrequests/stale":{"get":{"summary":"All open Pull Requests in Real Dev Squad","tags":["Pull Requests"],"responses":{"200":{"description":"Pull Requests","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Stale PRs"},"pullRequests":{"type":"array","items":{"$ref":"#/components/schemas/pullRequests"}}}}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/pullrequests/user/:username":{"get":{"summary":"Pull Requests by a user in Real Dev Squad","tags":["Pull Requests"],"responses":{"200":{"description":"Pull Requests","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Pull requests returned successfully!"},"pullRequests":{"type":"array","items":{"$ref":"#/components/schemas/pullRequests"}}}}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/stocks":{"get":{"summary":"Used to get all the stocks","tags":["Stocks"],"responses":{"200":{"description":"returns stocks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/stocks"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}},"post":{"summary":"Used to create new stock","tags":["Stocks"],"requestBody":{"description":"Stock data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/stocks"}}}},"responses":{"200":{"description":"returns newly created stock","content":{"application/json":{"schema":{"$ref":"#/components/schemas/stocks"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/stocks/user/self":{"get":{"summary":"Used to get all the stocks of the user","tags":["User Stocks"],"responses":{"200":{"description":"returns stocks of the user","content":{"application/json":{"schema":{"$ref":"#/components/schemas/userStocks"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/tasks":{"get":{"summary":"Used to get all the tasks","tags":["Tasks"],"responses":{"200":{"description":"returns tasks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}},"post":{"summary":"Used to create new task","tags":["Tasks"],"requestBody":{"description":"Task data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"responses":{"200":{"description":"returns newly created task","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}},"patch":{"summary":"Used to update task details","tags":["Tasks"],"requestBody":{"description":"Task data to be updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"responses":{"204":{"description":"no content"},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/tasks/self":{"get":{"summary":"Use to get all the tasks of the logged in user","tags":["Tasks"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"returns all tasks","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tasks"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/trade/stock/new/self":{"post":{"summary":"Used for new trading request","tags":["Trading"],"security":[{"bearerAuth":[]}],"requestBody":{"description":"Trading details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/trading"}}}},"responses":{"200":{"description":"Successful trading details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/tradingSuccess"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/users/self":{"patch":{"summary":"Use to update the user data.","requestBody":{"description":"User data to be updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"204":{"description":"No content"},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}},"get":{"summary":"Use to get self details.","tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/users":{"get":{"summary":"Get all the users in system.","tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User details","content":{"application/json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Users returned successfully!"},"users":{"type":"array","items":{"$ref":"#/components/schemas/users"}}}}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"503":{"description":"serverUnavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/serverUnavailable"}}}}}}},"/users/isUsernameAvailable/:username":{"get":{"summary":"check user exists or not","tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User Availability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/userAvailable"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/users/:username":{"get":{"summary":"Get the details of user with provided id.","tags":["Users"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User details","content":{"application/json":{"schema":{"$ref":"#/components/schemas/users"}}}},"404":{"description":"notFound","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/notFound"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/wallet/":{"get":{"summary":"Gets the user wallet details","tags":["wallet"],"responses":{"200":{"description":"Return wallet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/wallet"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}},"/wallet/:username":{"get":{"summary":"Gets the user wallet details for a username, if you are an authorized superuser","tags":["wallet"],"responses":{"200":{"description":"Return wallet","content":{"application/json":{"schema":{"$ref":"#/components/schemas/wallet"}}}},"401":{"description":"unAuthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/unAuthorized"}}}},"403":{"description":"forbidden","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/forbidden"}}}},"500":{"description":"badImplementation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/errors/badImplementation"}}}}}}}}} \ No newline at end of file diff --git a/routes/stocks.js b/routes/stocks.js index 2cc9a8774..19ea48d37 100644 --- a/routes/stocks.js +++ b/routes/stocks.js @@ -2,7 +2,7 @@ const express = require('express') const router = express.Router() const authenticate = require('../middlewares/authenticate') const authorization = require('../middlewares/authorization') -const { addNewStock, fetchStocks } = require('../controllers/stocks') +const { addNewStock, fetchStocks, getSelfStocks } = require('../controllers/stocks') const { createStock } = require('../middlewares/validators/stocks') /** @@ -64,4 +64,34 @@ router.get('/', fetchStocks) */ router.post('/', authenticate, authorization, createStock, addNewStock) +/** + * @swagger + * /stocks/user/self: + * get: + * summary: Used to get all the stocks of the user + * tags: + * - User Stocks + * responses: + * 200: + * description: returns stocks of the user + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/userStocks' + * 401: + * description: unAuthorized + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/errors/unAuthorized' + * 500: + * description: badImplementation + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/errors/badImplementation' + */ + +router.get('/user/self', authenticate, getSelfStocks) + module.exports = router diff --git a/models/trading.js b/services/tradingService.js similarity index 61% rename from models/trading.js rename to services/tradingService.js index 8e4daa5fc..c6e77f494 100644 --- a/models/trading.js +++ b/services/tradingService.js @@ -3,8 +3,11 @@ const stocksModel = firestore.collection('stocks') const transactionsModel = firestore.collection('transactions') const tradeLogsModel = firestore.collection('trade-logs') const { fetchWallet, updateWallet } = require('../models/wallets') +const { fetchUserStocks, updateUserStocks } = require('../models/stocks') +const { DINERO } = require('../constants/wallets') const INSUFFICIENT_FUNDS = 'Trade was not successful due to insufficient funds' +const INSUFFICIENT_QUANTITIES = 'Trade was not successful because you do not have enough quantity' /** * Updates the stock Price @@ -26,16 +29,17 @@ const getUpdatedPrice = (stockPrice) => { const trade = async (tradeData) => { // ! TODO - update as per curreny type, currently only using dinero try { - const { stockId, quantity, tradeType, totalPrice, userId } = tradeData + const { stockId, stockName, quantity, tradeType, totalPrice, userId } = tradeData const stockCollection = await stocksModel.doc(stockId).get() const stockData = stockCollection.data() let userBalance = 0 let quantityToUpdate = 0 - let stockPriceToBeUpdated = stockData.price - let orderValue = 0 - let qtyUserCanPurchase = 0 + let qtyUserCanPurchase = quantity + let userStocksQty = 0 + let initialStockValue = stockData.price const { currencies } = await fetchWallet(userId) + const userStocks = await fetchUserStocks(userId, stockId) const updatedCurrencyData = {} if (!currencies) { @@ -44,22 +48,30 @@ const trade = async (tradeData) => { switch (tradeType) { case 'SELL': { + if (!userStocks.id || userStocks.quantity < quantity) { + return { canUserTrade: false, errorMessage: INSUFFICIENT_QUANTITIES } + } + quantityToUpdate = quantity + stockData.quantity - userBalance = quantity * stockData.price - updatedCurrencyData.dinero = userBalance + currencies.dinero - stockPriceToBeUpdated = getUpdatedPrice(stockData.price) + userBalance = (quantity * stockData.price) + currencies[`${DINERO}`] + userStocksQty = userStocks.quantity - quantity break } case 'BUY': { qtyUserCanPurchase = Math.floor(totalPrice / stockData.price) - if (qtyUserCanPurchase <= 0 || totalPrice > currencies.dinero) { + if (qtyUserCanPurchase <= 0 || totalPrice > currencies[`${DINERO}`]) { return { canUserTrade: false, errorMessage: INSUFFICIENT_FUNDS } } - orderValue = qtyUserCanPurchase * stockData.price quantityToUpdate = stockData.quantity - qtyUserCanPurchase - userBalance = currencies.dinero - orderValue - updatedCurrencyData.dinero = userBalance - stockPriceToBeUpdated = getUpdatedPrice(stockData.price) + userBalance = currencies[`${DINERO}`] - (qtyUserCanPurchase * stockData.price) + userStocksQty = qtyUserCanPurchase + + initialStockValue = stockData.price + + if (userStocks.id) { + userStocksQty = userStocks.quantity + qtyUserCanPurchase + initialStockValue = userStocks.initialStockValue + } break } default: { @@ -67,12 +79,26 @@ const trade = async (tradeData) => { } } + const orderValue = qtyUserCanPurchase * stockData.price + const stockPriceToBeUpdated = getUpdatedPrice(stockData.price) + updatedCurrencyData[`${DINERO}`] = userBalance + const updatedStockData = { ...stockData, quantity: quantityToUpdate, price: stockPriceToBeUpdated } + // Update user stocks + + await updateUserStocks(userId, { + stockId, + stockName, + quantity: userStocksQty, + orderValue: userStocksQty * stockData.price, + initialStockValue + }) + // Transaction Log const { id } = await tradeLogsModel.add({ @@ -94,7 +120,7 @@ const trade = async (tradeData) => { // update user wallet - updateWallet(userId, { + await updateWallet(userId, { ...currencies, ...updatedCurrencyData })