Skip to content

Commit 8ad6a29

Browse files
API for applications (#1405)
* API for applications * authorizeown or super user middleware in get request * validator changes * config changes * log for update application * test for get application API * completed get application tests * application API test completed * changed according to the discord link generate api changes * few changes based on new requirement and few changes after self review * controller message changed * removed unwanted code and updated types for controller methods * fix for failing tests * removed console log * made changes suggested * logger string change * fix issues in comments, logger error, and types * fix for logtype import issue
1 parent 7616265 commit 8ad6a29

File tree

11 files changed

+645
-0
lines changed

11 files changed

+645
-0
lines changed

constants/logs.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const logType = {
22
PROFILE_DIFF_APPROVED: "PROFILE_DIFF_APPROVED",
33
PROFILE_DIFF_REJECTED: "PROFILE_DIFF_REJECTED",
44
CLOUDFLARE_CACHE_PURGED: "CLOUDFLARE_CACHE_PURGED",
5+
APPLICATION_UPDATED: "USER_APPLICATION_UPDATED",
56
EVENTS_REMOVE_PEER: "EVENTS_REMOVE_PEER",
67
};
78

controllers/applications.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import { addLog } from "../models/logs";
2+
const { logType } = require("../constants/logs");
3+
import { CustomRequest, CustomResponse } from "../types/global";
4+
const { INTERNAL_SERVER_ERROR } = require("../constants/errorMessages");
5+
const ApplicationModel = require("../models/applications");
6+
7+
const getAllOrUserApplication = async (req: CustomRequest, res: CustomResponse): Promise<any> => {
8+
try {
9+
const { userId } = req.query;
10+
if (userId) {
11+
const application = await ApplicationModel.getUserApplications(userId);
12+
return res.json({
13+
message: "application returned successfully!",
14+
application,
15+
});
16+
}
17+
18+
const applications = await ApplicationModel.getAllApplications();
19+
return res.json({
20+
message: "applications returned successfully!",
21+
applications,
22+
});
23+
} catch (err) {
24+
logger.error(`Error in fetching application: ${err}`);
25+
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
26+
}
27+
};
28+
29+
const addApplication = async (req: CustomRequest, res: CustomResponse) => {
30+
try {
31+
const rawData = req.body;
32+
const application = await ApplicationModel.getUserApplications(req.userData.id);
33+
if (!application.notFound && !application.status) {
34+
return res.status(409).json({
35+
message: "User data is already present!",
36+
});
37+
}
38+
const data = {
39+
userId: req.userData.id,
40+
biodata: {
41+
firstName: rawData.firstName,
42+
lastName: rawData.lastName,
43+
},
44+
location: {
45+
city: rawData.city,
46+
state: rawData.state,
47+
country: rawData.country,
48+
},
49+
professional: {
50+
institution: rawData.college,
51+
skills: rawData.skills,
52+
},
53+
intro: {
54+
introduction: rawData.introduction,
55+
funFact: rawData.funFact,
56+
forFun: rawData.forFun,
57+
whyRds: rawData.whyRds,
58+
numberOfHours: rawData.numberOfHours,
59+
},
60+
foundFrom: rawData.foundFrom,
61+
};
62+
await ApplicationModel.addApplication(data);
63+
64+
return res.status(201).json({
65+
message: "User application added.",
66+
});
67+
} catch (err) {
68+
logger.error(`Error while adding application: ${err}`);
69+
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
70+
}
71+
};
72+
73+
const updateApplication = async (req: CustomRequest, res: CustomResponse) => {
74+
try {
75+
const { applicationId } = req.params;
76+
const rawBody = req.body;
77+
78+
const applicationLog = {
79+
type: logType.APPLICATION_UPDATED,
80+
meta: {
81+
applicationId,
82+
username: req.userData.username,
83+
userId: req.userData.id,
84+
},
85+
body: rawBody,
86+
};
87+
88+
const promises = [
89+
ApplicationModel.updateApplication(rawBody, applicationId),
90+
addLog(applicationLog.type, applicationLog.meta, applicationLog.body),
91+
];
92+
93+
await Promise.all(promises);
94+
return res.json({
95+
message: "Application updated successfully!",
96+
});
97+
} catch (err) {
98+
logger.error(`Error while updating the application: ${err}`);
99+
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
100+
}
101+
};
102+
103+
module.exports = {
104+
getAllOrUserApplication,
105+
addApplication,
106+
updateApplication,
107+
};
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* This middleware authorizes if the requested resource belongs to that user or the user is a superuser
3+
* for that route.
4+
* Note: This must be added on routes after the `authenticate` middleware.
5+
* @param req {Object} - Express request object
6+
* @param res {Object} - Express response object
7+
* @param next {Function} - Express middleware function
8+
* @return {Object} - Returns unauthorized user if the role is not assigned
9+
*
10+
**/
11+
12+
import { NextFunction } from "express";
13+
import { CustomRequest, CustomResponse } from "../types/global";
14+
15+
module.exports = async (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
16+
try {
17+
const isSuperUser = req.userData.roles.super_user;
18+
const { id } = req.userData;
19+
const userIdInQuery = req.query.userId;
20+
21+
if (isSuperUser || userIdInQuery === id) return next();
22+
else return res.boom.forbidden('Unauthorized User')
23+
} catch (err) {
24+
logger.error(err);
25+
return res.boom.badImplementation("Something went wrong please contact admin");
26+
}
27+
};
28+
29+
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { NextFunction } from "express";
2+
import { CustomRequest, CustomResponse } from "../../types/global";
3+
import { customWordCountValidator } from "../../utils/customWordCountValidator";
4+
const joi = require("joi");
5+
6+
const validateApplicationData = async (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
7+
const schema = joi
8+
.object()
9+
.strict()
10+
.keys({
11+
userId: joi.string().optional(),
12+
firstName: joi.string().min(1).required(),
13+
lastName: joi.string().min(1).required(),
14+
college: joi.string().min(1).required(),
15+
skills: joi.string().min(5).required(),
16+
city: joi.string().min(1).required(),
17+
state: joi.string().min(1).required(),
18+
country: joi.string().min(1).required(),
19+
foundFrom: joi.string().min(1).required(),
20+
introduction: joi.string().min(1).required(),
21+
forFun: joi
22+
.string()
23+
.custom((value, helpers) => customWordCountValidator(value, helpers, 100))
24+
.required(),
25+
funFact: joi
26+
.string()
27+
.custom((value, helpers) => customWordCountValidator(value, helpers, 100))
28+
.required(),
29+
whyRds: joi
30+
.string()
31+
.custom((value, helpers) => customWordCountValidator(value, helpers, 100))
32+
.required(),
33+
flowState: joi.string().optional(),
34+
numberOfHours: joi.number().min(1).max(100).required(),
35+
});
36+
37+
try {
38+
await schema.validateAsync(req.body);
39+
next();
40+
} catch (error) {
41+
logger.error(`Error in validating recruiter data: ${error}`);
42+
res.boom.badRequest(error.details[0].message);
43+
}
44+
};
45+
46+
const validateApplicationUpdateData = async (req: CustomRequest, res: CustomResponse, next: NextFunction) => {
47+
const schema = joi
48+
.object()
49+
.strict()
50+
.keys({
51+
status: joi.string().min(1).optional(),
52+
reason: joi.string().min(1).optional(),
53+
});
54+
55+
try {
56+
await schema.validateAsync(req.body);
57+
next();
58+
} catch (error) {
59+
logger.error(`Error in validating recruiter data: ${error}`);
60+
res.boom.badRequest(error.details[0].message);
61+
}
62+
};
63+
64+
module.exports = {
65+
validateApplicationData,
66+
validateApplicationUpdateData,
67+
};

models/applications.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { application } from "../types/application";
2+
3+
const { userState } = require("../constants/userStatus");
4+
const firestore = require("../utils/firestore");
5+
const { updateUserStatus } = require("./userStatus");
6+
const ApplicationsModel = firestore.collection("applicants");
7+
8+
9+
const getAllApplications = async () => {
10+
try {
11+
const allApplicationsData = [];
12+
const allApplications = await ApplicationsModel.get();
13+
allApplications.forEach((data: any) => {
14+
allApplicationsData.push({
15+
id: data.id,
16+
...data.data(),
17+
});
18+
});
19+
return allApplicationsData;
20+
} catch (err) {
21+
logger.log("error in getting all intros", err);
22+
throw err;
23+
}
24+
};
25+
26+
const getUserApplications = async (userId: string) => {
27+
try {
28+
const application = await ApplicationsModel.where("userId", "==", userId).limit(1).get();
29+
const [applicationDoc] = application.docs;
30+
if (applicationDoc) {
31+
return { id: applicationDoc.id, ...applicationDoc.data() };
32+
}
33+
return { notFound: true }
34+
} catch (err) {
35+
logger.log("error in getting user intro", err);
36+
throw err;
37+
}
38+
};
39+
40+
const addApplication = async (data: application) => {
41+
try {
42+
const application = await ApplicationsModel.add(data);
43+
return application.id;
44+
} catch (err) {
45+
logger.error("Error in adding data", err);
46+
throw err;
47+
}
48+
};
49+
50+
const updateApplication = async (dataToUpdate: object, applicationId: string) => {
51+
try {
52+
await ApplicationsModel.doc(applicationId).update(dataToUpdate);
53+
} catch (err) {
54+
logger.error("Error in updating intro", err);
55+
throw err;
56+
}
57+
};
58+
59+
module.exports = {
60+
getAllApplications,
61+
getUserApplications,
62+
addApplication,
63+
updateApplication,
64+
};

routes/applications.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
const express = require("express");
2+
const { SUPERUSER } = require("../constants/roles");
3+
const authenticate = require("../middlewares/authenticate");
4+
const authorizeRoles = require("../middlewares/authorizeRoles");
5+
const applications = require("../controllers/applications.ts");
6+
const authorizeOwnOrSuperUser = require("../middlewares/authorizeOwnOrSuperUser");
7+
const applicationValidator = require("../middlewares/validators/application");
8+
9+
const router = express.Router();
10+
11+
router.get(
12+
"/",
13+
authenticate,
14+
authorizeOwnOrSuperUser,
15+
applications.getAllOrUserApplication
16+
);
17+
router.post("/", authenticate, applicationValidator.validateApplicationData, applications.addApplication);
18+
router.patch("/:applicationId", authenticate, applicationValidator.validateApplicationUpdateData, authorizeRoles([SUPERUSER]), applications.updateApplication);
19+
20+
module.exports = router;

routes/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,6 @@ app.use("/issues", require("./issues.js"));
3131
app.use("/progresses", require("./progresses.js"));
3232
app.use("/monitor", require("./monitor.js"));
3333
app.use("/staging", require("./staging.js"));
34+
app.use("/applications", require("./applications.ts"));
3435
app.use("/goals", require("./goals.js"));
3536
module.exports = app;
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
module.exports = () => {
2+
return [
3+
{
4+
firstName: "vinayak",
5+
lastName: "triveid",
6+
college: "Christ Church college",
7+
skills: "React, Ember, Node js",
8+
city: "Kanpur",
9+
state: "Uttar Pradesh",
10+
country: "India",
11+
foundFrom: "Friend",
12+
introduction: "Software developer",
13+
forFun: "Watch movies, code, etc,",
14+
funFact: "Facts are not fun!",
15+
whyRds: "RDS is awesome, that's it, that's the reason",
16+
flowState: "3",
17+
numberOfHours: 10,
18+
intro: {
19+
numberOfHours: 20,
20+
},
21+
},
22+
{
23+
userId: "xyajkdfsfsd",
24+
firstName: "Ritik",
25+
lastName: "Jaiwal",
26+
college: "Tata Consultancy services",
27+
skills: "React, Ember, Node js",
28+
city: "Bangalore",
29+
state: "Karnataka",
30+
country: "India",
31+
foundFrom: "Friend",
32+
introduction: "Software developer",
33+
forFun: "Watch movies, code, etc,",
34+
funFact: "Facts are not fun!",
35+
whyRds: "RDS is awesome, that's it, that's the reason",
36+
flowState: "",
37+
numberOfHours: 10,
38+
intro: {
39+
numberOfHours: 20,
40+
},
41+
},
42+
{
43+
college: "Groww",
44+
state: "Karnataka",
45+
firstName: "Vaibhav",
46+
lastName: "Desai",
47+
flowState: "4",
48+
userId: "fasdjfklsdjlkfa",
49+
foundFrom: "twitter",
50+
whyRds:
51+
"mattis aliquam faucibus purus in massa tempor nec feugiat nisl pretium fusce id velit ut tortor pretium viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed viverra tellus in hac habitasse platea dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at augue eget arcu dictum varius duis at consectetur lorem donec massa sapien faucibus et molestie ac feugiat sed lectus vestibulum mattis ullamcorper velit sed ullamcorper morbi tincidunt ornare massa eget egestas purus viverra accumsan in nisl nisi scelerisque eu ultrices vitae auctor eu augue ut lectus arcu bibendum at",
52+
forFun:
53+
"mattis aliquam faucibus purus in massa tempor nec feugiat nisl pretium fusce id velit ut tortor pretium viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed viverra tellus in hac habitasse platea dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at augue eget arcu dictum varius duis at consectetur lorem donec massa sapien faucibus et molestie ac feugiat sed lectus vestibulum mattis ullamcorper velit sed ullamcorper morbi tincidunt ornare massa eget egestas purus viverra accumsan in nisl nisi scelerisque eu ultrices vitae auctor eu augue ut lectus arcu bibendum at",
54+
skills: "I know HTML, CSS, & Java script",
55+
city: "Rajanukunte",
56+
funFact:
57+
"mattis aliquam faucibus purus in massa tempor nec feugiat nisl pretium fusce id velit ut tortor pretium viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed viverra tellus in hac habitasse platea dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at augue eget arcu dictum varius duis at consectetur lorem donec massa sapien faucibus et molestie ac feugiat sed lectus vestibulum mattis ullamcorper velit sed ullamcorper morbi tincidunt ornare massa eget egestas purus viverra accumsan in nisl nisi scelerisque eu ultrices vitae auctor eu augue ut lectus arcu bibendum at",
58+
introduction:
59+
"mattis aliquam faucibus purus in massa tempor nec feugiat nisl pretium fusce id velit ut tortor pretium viverra suspendisse potenti nullam ac tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed viverra tellus in hac habitasse platea dictumst vestibulum rhoncus est pellentesque elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at augue eget arcu dictum varius duis at consectetur lorem donec massa sapien faucibus et molestie ac feugiat sed lectus vestibulum mattis ullamcorper velit sed ullamcorper morbi tincidunt ornare massa eget egestas purus viverra accumsan in nisl nisi scelerisque eu ultrices vitae auctor eu augue ut lectus arcu bibendum at",
60+
country: "India",
61+
numberOfHours: 20,
62+
},
63+
];
64+
};

0 commit comments

Comments
 (0)