Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion constants/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ const APPLICATION_STATUS = {
* Business requirement: Applications created after this date are considered reviewed
* and cannot be resubmitted. This date marks the start of the new application review cycle.
*/
const APPLICATION_SCORE = {
INITIAL_SCORE: 50,
NUDGE_BONUS: 10,
};

const APPLICATION_REVIEW_CYCLE_START_DATE = new Date("2026-01-01T00:00:00.000Z");

module.exports = {
Expand All @@ -57,5 +62,6 @@ module.exports = {
APPLICATION_ERROR_MESSAGES,
APPLICATION_LOG_MESSAGES,
APPLICATION_REVIEW_CYCLE_START_DATE,
APPLICATION_STATUS
APPLICATION_STATUS,
APPLICATION_SCORE
};
1 change: 1 addition & 0 deletions controllers/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ const nudgeApplication = async (req: CustomRequest, res: CustomResponse) => {
message: API_RESPONSE_MESSAGES.NUDGE_SUCCESS,
nudgeCount: result.nudgeCount,
lastNudgeAt: result.lastNudgeAt,
score: result.score,
});
default:
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
Expand Down
11 changes: 10 additions & 1 deletion middlewares/validators/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const validateApplicationData = async (req: CustomRequest, res: CustomResponse,
userId: joi.string().optional(),
firstName: joi.string().min(1).required(),
lastName: joi.string().min(1).required(),
college: joi.string().min(1).required(),
institution: joi.string().min(1).required(),
skills: joi.string().min(5).required(),
city: joi.string().min(1).required(),
state: joi.string().min(1).required(),
Expand Down Expand Up @@ -130,6 +130,15 @@ const validateApplicationUpdateData = async (req: CustomRequest, res: CustomResp
.strict()
.min(1)
.keys({
institution: joi.string().min(1).optional(),
skills: joi.string().min(5).optional(),
city: joi.string().min(1).optional(),
state: joi.string().min(1).optional(),
country: joi.string().min(1).optional(),
role: joi
.string()
.valid(...Object.values(APPLICATION_ROLES))
.optional(),
imageUrl: joi.string().uri().optional(),
foundFrom: joi.string().min(1).optional(),
introduction: joi.string().min(1).optional(),
Expand Down
11 changes: 7 additions & 4 deletions models/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { logType } = require("../constants/logs");
const firestore = require("../utils/firestore");
const logger = require("../utils/logger");
const ApplicationsModel = firestore.collection("applicants");
const { APPLICATION_STATUS_TYPES, APPLICATION_STATUS } = require("../constants/application");
const { APPLICATION_STATUS_TYPES, APPLICATION_STATUS, APPLICATION_SCORE } = require("../constants/application");
const { convertDaysToMilliseconds } = require("../utils/time");

const getAllApplications = async (limit: number, lastDocId?: string) => {
Expand All @@ -16,7 +16,7 @@ const getAllApplications = async (limit: number, lastDocId?: string) => {
lastDoc = await ApplicationsModel.doc(lastDocId).get();
}

let dbQuery = ApplicationsModel.orderBy("createdAt", "desc");
let dbQuery = ApplicationsModel.where("isNew", "==", true).orderBy("createdAt", "desc");

if (lastDoc) {
dbQuery = dbQuery.startAfter(lastDoc);
Expand Down Expand Up @@ -59,7 +59,7 @@ const getApplicationsBasedOnStatus = async (status: string, limit: number, lastD
try {
let lastDoc = null;
const applications = [];
let dbQuery = ApplicationsModel.where("status", "==", status);
let dbQuery = ApplicationsModel.where("isNew", "==", true).where("status", "==", status);

if (userId) {
dbQuery = dbQuery.where("userId", "==", userId);
Expand All @@ -86,7 +86,7 @@ const getApplicationsBasedOnStatus = async (status: string, limit: number, lastD
});
});

let countQuery = ApplicationsModel.where("status", "==", status);
let countQuery = ApplicationsModel.where("isNew", "==", true).where("status", "==", status);

const totalApplications = await countQuery.get();
const totalCount = totalApplications.size;
Expand Down Expand Up @@ -219,16 +219,19 @@ const nudgeApplication = async ({ applicationId, userId }: { applicationId: stri
const currentNudgeCount = application.nudgeCount || 0;
const updatedNudgeCount = currentNudgeCount + 1;
const newLastNudgeAt = new Date(currentTime).toISOString();
const updatedScore = (application.score || 0) + APPLICATION_SCORE.NUDGE_BONUS;

transaction.update(applicationRef, {
nudgeCount: updatedNudgeCount,
lastNudgeAt: newLastNudgeAt,
score: updatedScore,
});

return {
status: APPLICATION_STATUS.success,
nudgeCount: updatedNudgeCount,
lastNudgeAt: newLastNudgeAt,
score: updatedScore,
};
});

Expand Down
5 changes: 3 additions & 2 deletions services/applicationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const {
APPLICATION_STATUS_TYPES,
APPLICATION_ERROR_MESSAGES,
APPLICATION_REVIEW_CYCLE_START_DATE,
APPLICATION_SCORE,
} = require("../constants/application");
const logger = require("../utils/logger");

Expand All @@ -31,7 +32,7 @@ const transformPayloadToApplication = (payload: applicationPayload, userId: stri
country: payload.country,
},
professional: {
institution: payload.college,
institution: payload.institution,
skills: payload.skills,
},
intro: {
Expand Down Expand Up @@ -86,7 +87,7 @@ export const createApplicationService = async (

const applicationData: application = {
...transformPayloadToApplication(payload, userId),
score: 0,
score: APPLICATION_SCORE.INITIAL_SCORE,
status: APPLICATION_STATUS_TYPES.PENDING,
createdAt,
isNew: true,
Expand Down
19 changes: 12 additions & 7 deletions test/fixtures/applications/applications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module.exports = () => {
{
firstName: "vinayak",
lastName: "triveid",
college: "Christ Church college",
institution: "Christ Church institution",
skills: "React, Ember, Node js",
city: "Kanpur",
state: "Uttar Pradesh",
Expand All @@ -21,12 +21,13 @@ module.exports = () => {
},
createdAt: null,
role: "developer",
isNew: true,
},
{
userId: "xyajkdfsfsd",
firstName: "Ritik",
lastName: "Jaiwal",
college: "Tata Consultancy services",
institution: "Tata Consultancy services",
skills: "React, Ember, Node js",
city: "Bangalore",
state: "Karnataka",
Expand All @@ -44,9 +45,10 @@ module.exports = () => {
},
createdAt: null,
role: "developer",
isNew: true,
},
{
college: "Groww",
institution: "Groww",
state: "Karnataka",
firstName: "Vaibhav",
lastName: "Desai",
Expand All @@ -68,9 +70,10 @@ module.exports = () => {
status: "rejected",
createdAt: null,
role: "developer",
isNew: true,
},
{
college: "Groww",
institution: "Groww",
state: "Karnataka",
firstName: "Vaibhav",
lastName: "Desai",
Expand All @@ -92,9 +95,10 @@ module.exports = () => {
status: "rejected",
createdAt: null,
role: "developer",
isNew: true,
},
{
college: "Groww",
institution: "Groww",
state: "Karnataka",
firstName: "Vaibhav",
lastName: "Desai",
Expand All @@ -116,9 +120,10 @@ module.exports = () => {
status: "rejected",
createdAt: null,
role: "developer",
isNew: true,
},
{
college: "Groww",
institution: "Groww",
state: "Karnataka",
firstName: "Vaibhav",
lastName: "Desai",
Expand All @@ -145,7 +150,7 @@ module.exports = () => {
city: "Kanpur",
state: "UP",
country: "India",
college: "Christ Church College",
institution: "Christ Church College",
skills: "React, NodeJs, Ember",
introduction:
"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",
Expand Down
75 changes: 61 additions & 14 deletions test/integration/application.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const applicationModel = require("../../models/applications");

const applicationsData = require("../fixtures/applications/applications")();
const cookieName = config.get("userToken.cookieName");
const { APPLICATION_ERROR_MESSAGES, API_RESPONSE_MESSAGES } = require("../../constants/application");
const { APPLICATION_ERROR_MESSAGES, API_RESPONSE_MESSAGES, APPLICATION_SCORE } = require("../../constants/application");

const appOwner = userData[3];
const superUser = userData[4];
Expand Down Expand Up @@ -334,25 +334,27 @@ describe("Application", function () {
});

describe("POST /applications", function () {
it("should create a application and return 201 if the user has not yet submitted the application", function (done) {
chai
it("should create a application and return 201 if the user has not yet submitted the application", async function () {
const res = await chai
.request(app)
.post(`/applications`)
.set("cookie", `${cookieName}=${secondUserJwt}`)
.send({
...applicationsData[5],
imageUrl: "https://example.com/image.jpg",
})
.end((err, res) => {
if (err) {
return done(err);
}

expect(res).to.have.status(201);
expect(res.body.message).to.be.equal("Application created successfully");
expect(res.body).to.have.property("applicationId");
return done();
});

expect(res).to.have.status(201);
expect(res.body.message).to.be.equal("Application created successfully");
expect(res.body).to.have.property("applicationId");

const getRes = await chai
.request(app)
.get(`/applications/${res.body.applicationId}`)
.set("cookie", `${cookieName}=${superUserJwt}`);

expect(getRes).to.have.status(200);
expect(getRes.body.application.score).to.be.equal(50);
});
});

Expand Down Expand Up @@ -501,6 +503,48 @@ describe("Application", function () {
expect(secondRes.body.error).to.be.equal("Conflict");
expect(secondRes.body.message).to.be.equal(APPLICATION_ERROR_MESSAGES.EDIT_TOO_SOON);
});

it("should return 200 when updating city, state, and country", async function () {
const applicationData = { ...applicationsData[0], userId };
const testApplicationId = await applicationModel.addApplication(applicationData);

const res = await chai
.request(app)
.patch(`/applications/${testApplicationId}`)
.set("cookie", `${cookieName}=${jwt}`)
.send({ city: "New Delhi", state: "Delhi", country: "India" });

expect(res).to.have.status(200);
expect(res.body.message).to.be.equal("Application updated successfully");
});

it("should return 200 when updating role with a valid role", async function () {
const applicationData = { ...applicationsData[0], userId };
const testApplicationId = await applicationModel.addApplication(applicationData);

const res = await chai
.request(app)
.patch(`/applications/${testApplicationId}`)
.set("cookie", `${cookieName}=${jwt}`)
.send({ role: "designer" });

expect(res).to.have.status(200);
expect(res.body.message).to.be.equal("Application updated successfully");
});

it("should return 400 when updating role with an invalid role", async function () {
const applicationData = { ...applicationsData[0], userId };
const testApplicationId = await applicationModel.addApplication(applicationData);

const res = await chai
.request(app)
.patch(`/applications/${testApplicationId}`)
.set("cookie", `${cookieName}=${jwt}`)
.send({ role: "invalid_role" });

expect(res).to.have.status(400);
expect(res.body.error).to.be.equal("Bad Request");
});
});

describe("PATCH /applications/:applicationId/feedback", function () {
Expand Down Expand Up @@ -800,7 +844,7 @@ describe("Application", function () {
let nudgeApplicationId: string;

beforeEach(async function () {
const applicationData = { ...applicationsData[0], userId };
const applicationData = { ...applicationsData[0], userId, score: APPLICATION_SCORE.INITIAL_SCORE };
nudgeApplicationId = await applicationModel.addApplication(applicationData);
});

Expand All @@ -820,6 +864,7 @@ describe("Application", function () {
expect(res.body.message).to.be.equal(API_RESPONSE_MESSAGES.NUDGE_SUCCESS);
expect(res.body.nudgeCount).to.be.equal(1);
expect(res.body.lastNudgeAt).to.be.a("string");
expect(res.body.score).to.be.equal(APPLICATION_SCORE.INITIAL_SCORE + APPLICATION_SCORE.NUDGE_BONUS);
done();
});
});
Expand All @@ -834,6 +879,7 @@ describe("Application", function () {

expect(res).to.have.status(200);
expect(res.body.nudgeCount).to.be.equal(1);
expect(res.body.score).to.be.equal(APPLICATION_SCORE.INITIAL_SCORE + APPLICATION_SCORE.NUDGE_BONUS);

const twentyFiveHoursAgo = new Date(Date.now() - 25 * 60 * 60 * 1000).toISOString();
applicationModel
Expand All @@ -856,6 +902,7 @@ describe("Application", function () {
expect(res.body.message).to.be.equal(API_RESPONSE_MESSAGES.NUDGE_SUCCESS);
expect(res.body.nudgeCount).to.be.equal(2);
expect(res.body.lastNudgeAt).to.be.a("string");
expect(res.body.score).to.be.equal(APPLICATION_SCORE.INITIAL_SCORE + 2 * APPLICATION_SCORE.NUDGE_BONUS);
done();
});
})
Expand Down
4 changes: 2 additions & 2 deletions test/unit/services/applicationService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ describe("createApplicationService", () => {

const applicationData = addApplicationStub.getCall(0).args[0];
expect(applicationData.isNew).to.equal(true);
expect(applicationData.score).to.equal(0);
expect(applicationData.score).to.equal(50);
expect(applicationData.status).to.equal(APPLICATION_STATUS_TYPES.PENDING);
expect(applicationData.nudgeCount).to.equal(0);
});
Expand Down Expand Up @@ -186,7 +186,7 @@ describe("createApplicationService", () => {
expect(applicationData.location.city).to.equal(mockPayload.city);
expect(applicationData.location.state).to.equal(mockPayload.state);
expect(applicationData.location.country).to.equal(mockPayload.country);
expect(applicationData.professional.institution).to.equal(mockPayload.college);
expect(applicationData.professional.institution).to.equal(mockPayload.institution);
expect(applicationData.professional.skills).to.equal(mockPayload.skills);
expect(applicationData.intro.introduction).to.equal(mockPayload.introduction);
expect(applicationData.intro.funFact).to.equal(mockPayload.funFact);
Expand Down
4 changes: 2 additions & 2 deletions test/unit/utils/application.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ describe("getUserApplicationObject", async function () {
city: "Kanpur",
state: "UP",
country: "India",
college: "Christ Church College",
institution: "Christ Church College",
skills: "React, NodeJs, Ember",
introduction: "not needed",
funFact: "kdfkasdjfkdk",
Expand All @@ -33,7 +33,7 @@ describe("getUserApplicationObject", async function () {
country: rawData.country,
},
professional: {
institution: rawData.college,
institution: rawData.institution,
skills: rawData.skills,
},
intro: {
Expand Down
Loading
Loading