Skip to content

Commit 356ec08

Browse files
Merge pull request #377 from Real-Dev-Squad/FeatureFlags
Issue #376 Define CRUD operation for feature flag
2 parents 6d04193 + edbabb8 commit 356ec08

File tree

13 files changed

+818
-387
lines changed

13 files changed

+818
-387
lines changed

controllers/featureFlags.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const featureFlagQuery = require('../models/featureFlags')
2+
3+
/**
4+
* Fetches all the featureFlag
5+
*
6+
* @param req {Object} - Express request object
7+
* @param res {Object} - Express response object
8+
*/
9+
10+
const getFeatureFlags = async (req, res) => {
11+
try {
12+
const allFeatureFlags = await featureFlagQuery.fetchFeatureFlag()
13+
return res.json({
14+
message: 'FeatureFlags returned successfully!',
15+
featureflags: allFeatureFlags.length > 0 ? allFeatureFlags : []
16+
})
17+
} catch (err) {
18+
logger.error(`Error while fetching tasks ${err}`)
19+
return res.boom.badImplementation('An internal server error occurred')
20+
}
21+
}
22+
23+
/**
24+
* Posts the data of the featureFlag
25+
*
26+
* @param req {Object} - Express request object
27+
* @param res {Object} - Express response object
28+
*/
29+
30+
const addFeatureFlag = async (req, res) => {
31+
try {
32+
const featureFlag = await featureFlagQuery.addFeatureFlags(req.body, req.userData.username)
33+
return res.json({
34+
message: 'FeatureFlag added successfully!',
35+
data: featureFlag
36+
})
37+
} catch (err) {
38+
logger.error(`Error while adding featureFlag info: ${err}`)
39+
return res.boom.badImplementation('Something went wrong please contact admin')
40+
}
41+
}
42+
43+
/**
44+
* Update the data of the featureFlag
45+
*
46+
* @param req {Object} - Express request object
47+
* @param res {Object} - Express response object
48+
*/
49+
50+
const updateFeatureFlag = async (req, res) => {
51+
try {
52+
const result = await featureFlagQuery.updateFeatureFlags(req.body, req.params.id)
53+
if (result.isUpdated) {
54+
return res.status(204).send()
55+
}
56+
return res.boom.notFound('FeatureFlag doesn\'t exist')
57+
} catch (err) {
58+
logger.error(`Error while updating featureFlag info: ${err}`)
59+
return res.boom.badImplementation('Something went wrong please contact admin')
60+
}
61+
}
62+
63+
/**
64+
* Delete featureFlag
65+
*
66+
* @param req {Object} - Express request object
67+
* @param res {Object} - Express response object
68+
*/
69+
70+
const deleteFeatureFlag = async (req, res) => {
71+
try {
72+
const result = await featureFlagQuery.deleteFeatureFlag(req.params.id)
73+
if (result.isDeleted) {
74+
return res.json({
75+
message: 'FeatureFlag deleted successfully!!'
76+
})
77+
}
78+
return res.boom.notFound('featureFlag doesn\'t exist')
79+
} catch (err) {
80+
logger.error(`Error while deleting featureFlag info: ${err}`)
81+
return res.boom.badImplementation('Something went wrong please contact admin')
82+
}
83+
}
84+
85+
module.exports = {
86+
getFeatureFlags,
87+
addFeatureFlag,
88+
updateFeatureFlag,
89+
deleteFeatureFlag
90+
}

docs/swaggerDefinition.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,36 @@ const swaggerOptions = {
590590
}
591591
}
592592
},
593+
featureFlag: {
594+
type: 'object',
595+
properties: {
596+
name: {
597+
type: 'string'
598+
},
599+
id: {
600+
type: 'string'
601+
},
602+
title: {
603+
type: 'string'
604+
},
605+
created_at: {
606+
type: 'number'
607+
},
608+
updated_at: {
609+
type: 'number'
610+
},
611+
config: {
612+
type: 'object'
613+
},
614+
owner: {
615+
type: 'string'
616+
},
617+
launched_at: {
618+
type: 'number'
619+
}
620+
621+
}
622+
},
593623
errors: {
594624
unAuthorized: {
595625
type: 'object',
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const joi = require('joi')
2+
3+
const validateFeatureFlag = async (req, res, next) => {
4+
const schema = joi.object().keys({
5+
name: joi.string().required(),
6+
title: joi.string().required(),
7+
created_at: joi.number().optional(),
8+
updated_at: joi.number().optional(),
9+
config: joi.object({
10+
enabled: joi.boolean().required()
11+
}).required(),
12+
launched_at: joi.number().optional()
13+
})
14+
15+
try {
16+
await schema.validateAsync(req.body)
17+
next()
18+
} catch (error) {
19+
logger.error(`Error in validating featureFlag data: ${error}`)
20+
res.boom.badRequest(error.details[0].message)
21+
}
22+
}
23+
24+
const updateFeatureFlags = async (req, res, next) => {
25+
const schema = joi.object().keys({
26+
title: joi.string().optional(),
27+
config: joi.object({
28+
enabled: joi.boolean().required()
29+
}).required()
30+
})
31+
try {
32+
await schema.validateAsync(req.body)
33+
next()
34+
} catch (error) {
35+
logger.error(`Error in validating featureFlag data: ${error}`)
36+
res.boom.badRequest(error.details[0].message)
37+
}
38+
}
39+
40+
module.exports = {
41+
validateFeatureFlag,
42+
updateFeatureFlags
43+
}

models/featureFlags.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
const firestore = require('../utils/firestore')
2+
const featureFlagModel = firestore.collection('featureFlags')
3+
const userModel = require('./users')
4+
5+
/**
6+
* Fetch all tasks
7+
*
8+
* @return {Promise<featureFlags|Array>}
9+
*/
10+
const fetchFeatureFlag = async () => {
11+
try {
12+
const snapshot = await featureFlagModel.get()
13+
const featureFlags = []
14+
snapshot.forEach((doc) => {
15+
featureFlags.push({
16+
id: doc.id,
17+
...doc.data()
18+
})
19+
})
20+
const users = []
21+
const result = {}
22+
23+
featureFlags.forEach((item) => {
24+
if (!users.includes(item.owner)) { users.push(item.owner) }
25+
})
26+
27+
let start = 0
28+
let end = 10
29+
30+
for (let i = 0; i < Math.ceil(users.length / 10); i++) {
31+
const usersData = users.slice(start, end)
32+
const image = await userModel.fetchUserImage(usersData)
33+
start = end
34+
end += 10
35+
Object.assign(result, image)
36+
}
37+
38+
featureFlags.forEach((item) => {
39+
item.owner = {
40+
username: item.owner,
41+
img: result[item.owner]
42+
}
43+
})
44+
return featureFlags
45+
} catch (err) {
46+
logger.error('error getting featureFlags', err)
47+
throw err
48+
}
49+
}
50+
51+
/**
52+
* Add the feature flag data
53+
*
54+
* @param featureFlagData { Object }: featureFlag data object to be stored in DB
55+
* @param username { String }: Username String to be used to add owner in feature flag object
56+
* @return {Promise<{featureFlagData:object}>}
57+
*/
58+
const addFeatureFlags = async (featureFlag, username) => {
59+
try {
60+
featureFlag.created_at = Date.now()
61+
featureFlag.updated_at = featureFlag.created_at
62+
featureFlag.owner = username
63+
const { id } = await featureFlagModel.add(featureFlag)
64+
const featureFlagData = (await featureFlagModel.doc(id).get()).data()
65+
featureFlagData.id = id
66+
return featureFlagData
67+
} catch (err) {
68+
logger.error('Error in adding featureFlag', err)
69+
throw err
70+
}
71+
}
72+
73+
/**
74+
* Adds or updates the feature flag data
75+
*
76+
* @param featureFlag { Object }: feature flag data object to be stored in DB
77+
* @param featureFlagId { String }: feature flag Id String to be used to update the feature Flag
78+
* @return {Promise<{isUpdated: boolean}>}
79+
*/
80+
const updateFeatureFlags = async (featureFlag, featureFlagId) => {
81+
try {
82+
const doc = await featureFlagModel.doc(featureFlagId).get()
83+
if (!doc.data()) {
84+
return {
85+
isUpdated: false
86+
}
87+
}
88+
if (doc.data()) {
89+
featureFlag.updated_at = Date.now()
90+
featureFlag.launched_at = Date.now()
91+
await featureFlagModel.doc(featureFlagId).set({
92+
...doc.data(),
93+
...featureFlag
94+
})
95+
}
96+
return {
97+
isUpdated: true
98+
}
99+
} catch (err) {
100+
logger.error('Error in updating featureFlag', err)
101+
throw err
102+
}
103+
}
104+
105+
/**
106+
* Delete the feature flag data
107+
*
108+
* @param featureFlagId { String }: feature flag Id String to be used to delete the feature Flag
109+
* @return {Promise<{isDeleted: boolean}>}
110+
*/
111+
const deleteFeatureFlag = async (featureFlagId) => {
112+
try {
113+
const doc = await featureFlagModel.doc(featureFlagId).get()
114+
if (!doc.exists) {
115+
return {
116+
isDeleted: false
117+
}
118+
}
119+
await featureFlagModel.doc(featureFlagId).delete()
120+
return {
121+
isDeleted: true
122+
}
123+
} catch (err) {
124+
logger.error('Error in deleting featureFlag', err)
125+
throw err
126+
}
127+
}
128+
129+
module.exports = {
130+
fetchFeatureFlag,
131+
addFeatureFlags,
132+
updateFeatureFlags,
133+
deleteFeatureFlag
134+
}

models/users.js

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,26 @@ const updateUserPicture = async (image, userId) => {
167167
}
168168
}
169169

170+
/**
171+
* fetch the users image by passing array of users
172+
*
173+
* @param users {array}
174+
*/
175+
const fetchUserImage = async (users) => {
176+
const data = await userModel.where('username', 'in', users).get()
177+
const images = {}
178+
data.forEach((item) => {
179+
images[item.data().username] = item.data().img
180+
})
181+
return images
182+
}
183+
170184
module.exports = {
171185
addOrUpdate,
172186
fetchUsers,
173187
fetchUser,
174188
setIncompleteUserDetails,
175189
initializeUser,
176-
updateUserPicture
190+
updateUserPicture,
191+
fetchUserImage
177192
}

0 commit comments

Comments
 (0)