Skip to content

Commit 5128b36

Browse files
Merge pull request #1 from Real-Dev-Squad/develop
merge - 02/03/2021
2 parents 8f4e63a + 3f9e456 commit 5128b36

File tree

12 files changed

+432
-16
lines changed

12 files changed

+432
-16
lines changed

.eslintrc.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ module.exports = {
2424
'no-console': 'error',
2525

2626
// Custom mocha rules
27-
// 'mocha/no-skipped-tests': 'error',
27+
'mocha/no-skipped-tests': 'error',
2828
'mocha/no-exclusive-tests': 'error'
2929
},
3030
ignorePatterns: ['public/*']

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ $ npm run test-config
3838
- Create an application on [FireStore](https://firebase.google.com/docs/firestore) and [generate a service file](https://cloud.google.com/iam/docs/creating-managing-service-account-keys). Add the service file with the name `firestore-private-key.json` in the project root.
3939

4040
## API Documentation:
41-
- Run the server and navigate to `http://<HOSTNAME>:<PORT>/api-docs` to view the API documentation.
42-
- Alternatively, you can import the file [API Schema](https://github.com/Real-Dev-Squad/website-backend/blob/develop/public/apiSchema.json) to [Postman](https://www.postman.com/) or [SwaggerHub](https://swagger.io/tools/swaggerhub/).
41+
- View the RDS API documentation: [Real Dev Squad API](https://documenter.getpostman.com/view/2021368/TW6wH8Ns)
42+
- You can also run the server and navigate to `http://<HOSTNAME>:<PORT>/api-docs` to view the API documentation.
43+
- You can import the file [API Schema](https://github.com/Real-Dev-Squad/website-backend/blob/develop/public/apiSchema.json) to [Postman](https://www.postman.com/) or [SwaggerHub](https://swagger.io/tools/swaggerhub/).
4344
- If any API changes have been made:
4445
- Write JS Doc on top of your routes using YAML based annotations in OPEN API 3.0 format.
4546
- Run `npm run generate-api-schema` to generate the API schema. A file `public/apiSchema.json` will be created/updated.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const contributionsService = require('../services/contributions')
2+
const { fetchUser } = require('../models/users')
3+
4+
const ERROR_MESSAGE = 'Something went wrong. Please try again or contact admin'
5+
6+
/**
7+
* Get the contributions of the user
8+
* @param {Object} req - Express request object
9+
* @param {Object} res - Express response object
10+
*/
11+
12+
const getUserContributions = async (req, res) => {
13+
try {
14+
const username = req.params.username
15+
const result = await fetchUser({ username: req.params.username })
16+
if (result.userExists) {
17+
const contributions = await contributionsService.getUserContributions(username)
18+
return res.json(contributions)
19+
}
20+
return res.boom.notFound('User doesn\'t exist')
21+
} catch (err) {
22+
logger.error(`Error while retriving contributions ${err}`)
23+
return res.boom.badImplementation(ERROR_MESSAGE)
24+
}
25+
}
26+
27+
module.exports = {
28+
getUserContributions
29+
}

controllers/usersController.js

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

22
const userQuery = require('../models/users')
3-
const { decodeAuthToken } = require('../services/authService')
43

54
/**
65
* Fetches the data about our users
@@ -101,11 +100,9 @@ const addNewUser = async (req, res) => {
101100
*/
102101
const updateSelf = async (req, res) => {
103102
try {
104-
const token = req.cookies[config.get('userToken.cookieName')]
105-
const { userId } = decodeAuthToken(token)
106-
103+
const { id: userId } = req.userData
107104
if (req.body.username) {
108-
const { user } = await userQuery.fetchUser({ username: req.body.username })
105+
const { user } = await userQuery.fetchUser({ userId })
109106
if (!user.incompleteUserDetails) {
110107
return res.boom.forbidden('Cannot update username again')
111108
}

models/tasks.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
const firestore = require('../utils/firestore')
22
const tasksModel = firestore.collection('tasks')
3-
3+
const { fetchUser } = require('./users')
44
/**
55
* Adds and Updates tasks
66
*
@@ -64,8 +64,33 @@ const fetchTask = async (taskId) => {
6464
}
6565
}
6666

67+
/**
68+
* Fetch all tasks of a user
69+
*
70+
* @return {Promise<tasks|Array>}
71+
*/
72+
73+
const fetchUserTasks = async (username) => {
74+
try {
75+
const { user } = await fetchUser({ username })
76+
const tasksSnapshot = await tasksModel.where('participants', 'array-contains', user.username).get()
77+
const tasks = []
78+
tasksSnapshot.forEach((task) => {
79+
tasks.push({
80+
id: task.id,
81+
...task.data()
82+
})
83+
})
84+
return tasks
85+
} catch (err) {
86+
logger.error('error getting tasks', err)
87+
throw err
88+
}
89+
}
90+
6791
module.exports = {
6892
updateTask,
6993
fetchTasks,
70-
fetchTask
94+
fetchTask,
95+
fetchUserTasks
7196
}

models/users.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const addOrUpdate = async (userData, userId = null) => {
4444
return { isNewUser: true, userId: userInfo.id }
4545
} catch (err) {
4646
logger.error('Error in adding or updating user', err)
47-
throw err
47+
return err
4848
}
4949
}
5050

@@ -73,7 +73,7 @@ const fetchUsers = async (query) => {
7373
return allUsers
7474
} catch (err) {
7575
logger.error('Error retrieving user data', err)
76-
throw err
76+
return err
7777
}
7878
}
7979

@@ -108,7 +108,7 @@ const fetchUser = async ({ userId = null, username = null }) => {
108108
}
109109
} catch (err) {
110110
logger.error('Error retrieving user data', err)
111-
throw err
111+
return err
112112
}
113113
}
114114

public/apiSchema.json

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

routes/contributions.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
const express = require('express')
2+
const router = express.Router()
3+
const contributionsController = require('../controllers/contributionsController')
4+
5+
/**
6+
* @swagger
7+
* /contributions/{username}:
8+
* get:
9+
* summary: Used to get all the contributions of user
10+
* tags:
11+
* - Contributions
12+
* responses:
13+
* 200:
14+
* description: Return contributions
15+
* content:
16+
* application/json:
17+
* schema:
18+
* $ref: '#/components/schemas/contributions'
19+
* 404:
20+
* description: notFound
21+
* content:
22+
* application/json:
23+
* schema:
24+
* $ref: '#/components/schemas/errors/notFound'
25+
* 500:
26+
* description: badImplementation
27+
* content:
28+
* application/json:
29+
* schema:
30+
* $ref: '#/components/schemas/errors/badImplementation'
31+
*/
32+
router.get('/:username', contributionsController.getUserContributions)
33+
34+
module.exports = router

routes/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ app.use('/members', require('./members.js'))
88
app.use('/tasks', require('./tasks'))
99
app.use('/challenges', require('./challenges.js'))
1010
app.use('/pullrequests', require('./pullrequests.js'))
11+
app.use('/contributions', require('./contributions'))
1112

1213
module.exports = app

services/contributions.js

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
const githubService = require('../services/githubService')
2+
const tasks = require('../models/tasks')
3+
const { fetchUser } = require('../models/users')
4+
5+
/**
6+
* Get the contributions of the user
7+
* @param {string} username
8+
*/
9+
10+
const getUserContributions = async (username) => {
11+
const contributions = {}
12+
const { data } = await githubService.fetchPRsByUser(username)
13+
const allUserTasks = await tasks.fetchUserTasks(username)
14+
const noteworthy = []
15+
const all = []
16+
17+
if (data.total_count) {
18+
const allPRsDetails = extractPRdetails(data)
19+
20+
const participantsDetailsMap = new Map()
21+
const prMaps = new Map()
22+
23+
allPRsDetails.forEach(pr => {
24+
prMaps.set(pr.url, pr)
25+
})
26+
27+
for (const task of allUserTasks) {
28+
const noteworthyObject = {}
29+
const participantsDetails = []
30+
31+
noteworthyObject.task = extractTaskdetails(task)
32+
33+
for (const username of task.participants) {
34+
const userDetails = participantsDetailsMap.get(username)
35+
if (userDetails) {
36+
participantsDetails.push(userDetails)
37+
} else {
38+
const user = await getUserDetails(username)
39+
participantsDetailsMap.set(username, user)
40+
participantsDetails.push(user)
41+
}
42+
}
43+
44+
noteworthyObject.task.participants = participantsDetails
45+
const prList = []
46+
47+
task.links.forEach(link => {
48+
const prObject = prMaps.get(link)
49+
if (prObject) {
50+
prList.push(prObject)
51+
prMaps.delete(link)
52+
}
53+
})
54+
55+
noteworthyObject.prList = prList
56+
57+
if (task.isNoteworthy) {
58+
noteworthy.push(noteworthyObject)
59+
} else {
60+
all.push(noteworthyObject)
61+
}
62+
}
63+
64+
for (const prDetails of prMaps.values()) {
65+
const allObject = {
66+
prList: [prDetails],
67+
task: {}
68+
}
69+
all.push(allObject)
70+
}
71+
}
72+
contributions.noteworthy = noteworthy
73+
contributions.all = all
74+
return contributions
75+
}
76+
77+
/**
78+
* Extracts only the necessary details required from the object returned by Github API
79+
* @param data {Object} - Object returned by Github API
80+
*/
81+
82+
const extractPRdetails = (data) => {
83+
const allPRs = []
84+
data.items.forEach(({ title, user, html_url: url, state, created_at: createdAt, updated_at: updatedAt }) => {
85+
allPRs.push({
86+
title,
87+
state,
88+
createdAt,
89+
updatedAt,
90+
url,
91+
raisedBy: user.login
92+
})
93+
})
94+
return allPRs
95+
}
96+
97+
/**
98+
* Extracts only the necessary details required from the object returned by Task API
99+
* @param data {Object} - Object returned by Task API
100+
*/
101+
102+
const extractTaskdetails = (data) => {
103+
const { title, purpose, endsOn, startedOn, dependsOn, status, participants, featureUrl, isNoteworthy } = data
104+
return {
105+
title,
106+
purpose,
107+
endsOn,
108+
startedOn,
109+
dependsOn,
110+
status,
111+
participants,
112+
featureUrl,
113+
isNoteworthy
114+
}
115+
}
116+
117+
/**
118+
* Get the user details
119+
* @param username {string}
120+
*/
121+
122+
const getUserDetails = async (username) => {
123+
const { user } = await fetchUser({ username })
124+
const userDetails = extractUserDetails(user)
125+
return userDetails
126+
}
127+
128+
/**
129+
* Extracts only the necessary details required from the object returned by user API
130+
* @param data {Object} - Object returned by User api
131+
*/
132+
133+
const extractUserDetails = (data) => {
134+
const { username, firstname, lastname, img } = data
135+
if (!data.incompleteUserDetails) {
136+
return {
137+
firstname,
138+
lastname,
139+
img,
140+
username
141+
}
142+
} else {
143+
return { username }
144+
}
145+
}
146+
147+
module.exports = {
148+
getUserContributions
149+
}

0 commit comments

Comments
 (0)