Skip to content

Commit ba7a9e9

Browse files
authored
Dev to main sync (#2349)
Dev to Main Sync
2 parents 4bf146e + 2aaed8f commit ba7a9e9

17 files changed

+736
-117
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515

1616
strategy:
1717
matrix:
18-
node-version: [20.11.x]
18+
node-version: [22.10.0]
1919

2020
steps:
2121
- uses: actions/checkout@v3

controllers/onboardingExtension.ts

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
11
import {
22
ERROR_WHILE_CREATING_REQUEST,
3+
ERROR_WHILE_UPDATING_REQUEST,
34
LOG_ACTION,
45
ONBOARDING_REQUEST_CREATED_SUCCESSFULLY,
56
REQUEST_ALREADY_PENDING,
7+
REQUEST_APPROVED_SUCCESSFULLY,
8+
REQUEST_DOES_NOT_EXIST,
69
REQUEST_LOG_TYPE,
10+
REQUEST_REJECTED_SUCCESSFULLY,
711
REQUEST_STATE,
812
REQUEST_TYPE,
913
UNAUTHORIZED_TO_CREATE_ONBOARDING_EXTENSION_REQUEST,
1014
} from "../constants/requests";
1115
import { userState } from "../constants/userStatus";
1216
import { addLog } from "../services/logService";
13-
import { createRequest, getRequestByKeyValues } from "../models/requests";
17+
import { createRequest, getRequestByKeyValues, updateRequest } from "../models/requests";
1418
import { fetchUser } from "../models/users";
1519
import { getUserStatus } from "../models/userStatus";
1620
import { User } from "../typeDefinitions/users";
1721
import {
1822
CreateOnboardingExtensionBody,
1923
OnboardingExtension,
2024
OnboardingExtensionCreateRequest,
21-
OnboardingExtensionResponse
25+
OnboardingExtensionResponse,
26+
UpdateOnboardingExtensionStateRequest,
27+
UpdateOnboardingExtensionStateRequestBody
2228
} from "../types/onboardingExtension";
2329
import { convertDateStringToMilliseconds, getNewDeadline } from "../utils/requests";
2430
import { convertDaysToMilliseconds } from "../utils/time";
@@ -34,7 +40,11 @@ import { convertDaysToMilliseconds } from "../utils/time";
3440
* @param {OnboardingExtensionResponse} res - The Express response object used to send back the response.
3541
* @returns {Promise<OnboardingExtensionResponse>} Resolves to a response with the status and data or an error message.
3642
*/
37-
export const createOnboardingExtensionRequestController = async (req: OnboardingExtensionCreateRequest, res: OnboardingExtensionResponse): Promise<OnboardingExtensionResponse> => {
43+
export const createOnboardingExtensionRequestController = async (
44+
req: OnboardingExtensionCreateRequest,
45+
res: OnboardingExtensionResponse )
46+
: Promise<OnboardingExtensionResponse> => {
47+
3848
try {
3949

4050
const data = req.body as CreateOnboardingExtensionBody;
@@ -121,4 +131,72 @@ export const createOnboardingExtensionRequestController = async (req: Onboarding
121131
logger.error(ERROR_WHILE_CREATING_REQUEST, err);
122132
return res.boom.badImplementation(ERROR_WHILE_CREATING_REQUEST);
123133
}
124-
};
134+
};
135+
136+
/**
137+
* Updates the state of an onboarding extension request.
138+
*
139+
* @param {UpdateOnboardingExtensionStateRequest} req - The request object containing the update details.
140+
* @param {OnboardingExtensionResponse} res - The response object to send the result of the update.
141+
* @returns {Promise<OnboardingExtensionResponse>} Sends the response with the result of the update operation.
142+
*/
143+
export const updateOnboardingExtensionRequestState = async (
144+
req: UpdateOnboardingExtensionStateRequest,
145+
res: OnboardingExtensionResponse )
146+
: Promise<OnboardingExtensionResponse> => {
147+
148+
const dev = req.query.dev === "true";
149+
150+
if(!dev) return res.boom.notImplemented("Feature not implemented");
151+
152+
const body = req.body as UpdateOnboardingExtensionStateRequestBody;
153+
const lastModifiedBy = req?.userData?.id;
154+
const extensionId = req.params.id;
155+
156+
let requestBody: UpdateOnboardingExtensionStateRequestBody = {
157+
state: body.state,
158+
type: body.type,
159+
}
160+
161+
if(body.message){
162+
requestBody = { ...requestBody, message: body.message };
163+
}
164+
165+
try {
166+
const response = await updateRequest(extensionId, requestBody, lastModifiedBy, REQUEST_TYPE.ONBOARDING);
167+
168+
if ("error" in response) {
169+
if (response.error === REQUEST_DOES_NOT_EXIST) {
170+
return res.boom.notFound(response.error);
171+
}
172+
return res.boom.badRequest(response.error);
173+
}
174+
175+
const [logType, returnMessage] = response.state === REQUEST_STATE.APPROVED
176+
? [REQUEST_LOG_TYPE.REQUEST_APPROVED, REQUEST_APPROVED_SUCCESSFULLY]
177+
: [REQUEST_LOG_TYPE.REQUEST_REJECTED, REQUEST_REJECTED_SUCCESSFULLY];
178+
179+
const requestLog = {
180+
type: logType,
181+
meta: {
182+
requestId: extensionId,
183+
action: LOG_ACTION.UPDATE,
184+
createdBy: lastModifiedBy,
185+
},
186+
body: response,
187+
};
188+
189+
await addLog(requestLog.type, requestLog.meta, requestLog.body);
190+
191+
return res.status(200).json({
192+
message: returnMessage,
193+
data: {
194+
id: response.id,
195+
...response,
196+
},
197+
});
198+
}catch(error){
199+
logger.error(ERROR_WHILE_UPDATING_REQUEST, error);
200+
return res.boom.badImplementation(ERROR_WHILE_UPDATING_REQUEST);
201+
}
202+
}

controllers/requests.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ import { createTaskExtensionRequest, updateTaskExtensionRequest } from "./extens
1313
import { UpdateRequest } from "../types/requests";
1414
import { TaskRequestRequest } from "../types/taskRequests";
1515
import { createTaskRequestController } from "./taskRequestsv2";
16-
import { OnboardingExtensionCreateRequest, OnboardingExtensionResponse } from "../types/onboardingExtension";
17-
import { createOnboardingExtensionRequestController } from "./onboardingExtension";
16+
import { OnboardingExtensionCreateRequest, OnboardingExtensionResponse, UpdateOnboardingExtensionStateRequest } from "../types/onboardingExtension";
17+
import { createOnboardingExtensionRequestController, updateOnboardingExtensionRequestState } from "./onboardingExtension";
1818

1919
export const createRequestController = async (
2020
req: OooRequestCreateRequest | ExtensionRequestRequest | TaskRequestRequest | OnboardingExtensionCreateRequest,
@@ -30,6 +30,8 @@ export const createRequestController = async (
3030
return await createTaskRequestController(req as TaskRequestRequest, res as CustomResponse);
3131
case REQUEST_TYPE.ONBOARDING:
3232
return await createOnboardingExtensionRequestController(req as OnboardingExtensionCreateRequest, res as OnboardingExtensionResponse);
33+
case REQUEST_TYPE.ONBOARDING:
34+
return await createOnboardingExtensionRequestController(req as OnboardingExtensionCreateRequest, res as OnboardingExtensionResponse);
3335
default:
3436
return res.boom.badRequest("Invalid request type");
3537
}
@@ -42,6 +44,8 @@ export const updateRequestController = async (req: UpdateRequest, res: CustomRes
4244
return await updateOooRequestController(req as UpdateRequest, res as ExtensionRequestResponse);
4345
case REQUEST_TYPE.EXTENSION:
4446
return await updateTaskExtensionRequest(req as UpdateRequest, res as ExtensionRequestResponse);
47+
case REQUEST_TYPE.ONBOARDING:
48+
return await updateOnboardingExtensionRequestState(req as unknown as UpdateOnboardingExtensionStateRequest, res as OnboardingExtensionResponse);
4549
default:
4650
return res.boom.badRequest("Invalid request type");
4751
}

middlewares/validators/requests.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ export const updateRequestsMiddleware = async (
6565
.messages({
6666
"any.only": "state must be APPROVED or REJECTED",
6767
}),
68-
type: joi.string().valid(REQUEST_TYPE.OOO, REQUEST_TYPE.EXTENSION).required(),
68+
type: joi.string().valid(REQUEST_TYPE.OOO, REQUEST_TYPE.EXTENSION, REQUEST_TYPE.ONBOARDING).required(),
69+
message: joi.string().optional()
6970
});
7071

7172
try {
@@ -84,7 +85,7 @@ export const getRequestsMiddleware = async (req: OooRequestCreateRequest, res: O
8485
id: joi.string().optional(),
8586
type: joi
8687
.string()
87-
.valid(REQUEST_TYPE.OOO, REQUEST_TYPE.EXTENSION, REQUEST_TYPE.TASK, REQUEST_TYPE.ALL)
88+
.valid(REQUEST_TYPE.OOO, REQUEST_TYPE.EXTENSION, REQUEST_TYPE.TASK, REQUEST_TYPE.ALL, REQUEST_TYPE.ONBOARDING)
8889
.optional(),
8990
requestedBy: joi.string().insensitive().optional(),
9091
state: joi

models/discordactions.js

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ const { FIRESTORE_IN_CLAUSE_SIZE } = require("../constants/users");
2828
const discordService = require("../services/discordService");
2929
const { buildTasksQueryForMissedUpdates } = require("../utils/tasks");
3030
const { buildProgressQueryForMissedUpdates } = require("../utils/progresses");
31+
const { getRequestByKeyValues } = require("./requests");
32+
const { REQUEST_TYPE, REQUEST_STATE } = require("../constants/requests");
3133
const allMavens = [];
3234

3335
/**
@@ -753,12 +755,59 @@ const updateIdle7dUsersOnDiscord = async (dev) => {
753755
};
754756
};
755757

758+
/**
759+
* Filters out onboarding users who have an approved onboarding extension request that is still valid.
760+
*
761+
* This function iterates through the given list of onboarding users and checks if each user has a valid
762+
* approved onboarding extension request. If a valid extension request exists with a `newEndsOn`
763+
* date greater than the current date, the user is skipped. Otherwise, the user is added to the
764+
* returned list.
765+
*
766+
* @async
767+
* @function skipOnboardingUsersHavingApprovedExtensionRequest
768+
* @param {Array<Object>} [users=[]] - An array of user objects to be filtered. Each user object
769+
* must have an `id` property.
770+
* @returns {Promise<Array<Object>>} A promise that resolves to an array of users who do not have
771+
* a valid approved onboarding extension request.
772+
*/
773+
const skipOnboardingUsersHavingApprovedExtensionRequest = async (users = []) => {
774+
const currentTime = Date.now();
775+
776+
const results = await Promise.all(
777+
users.map(async (user) => {
778+
try {
779+
const latestApprovedExtension = await getRequestByKeyValues({
780+
type: REQUEST_TYPE.ONBOARDING,
781+
state: REQUEST_STATE.APPROVED,
782+
userId: user.id,
783+
});
784+
785+
if (latestApprovedExtension && latestApprovedExtension.newEndsOn > currentTime) {
786+
return null;
787+
}
788+
789+
return user;
790+
} catch (error) {
791+
logger.error(`Error while fetching latest approved extension for user ${user.id}:`, error);
792+
return null;
793+
}
794+
})
795+
);
796+
797+
return results.filter(Boolean);
798+
};
799+
756800
const updateUsersWith31DaysPlusOnboarding = async () => {
757801
try {
758-
const allOnboardingUsers31DaysCompleted = await getUsersBasedOnFilter({
802+
let allOnboardingUsers31DaysCompleted = await getUsersBasedOnFilter({
759803
state: userState.ONBOARDING,
760804
time: "31d",
761805
});
806+
807+
allOnboardingUsers31DaysCompleted = await skipOnboardingUsersHavingApprovedExtensionRequest(
808+
allOnboardingUsers31DaysCompleted
809+
);
810+
762811
const discordMembers = await getDiscordMembers();
763812
const groupOnboardingRole = await getGroupRole("group-onboarding-31d+");
764813
const groupOnboardingRoleId = groupOnboardingRole.role.roleid;
@@ -1131,4 +1180,5 @@ module.exports = {
11311180
addInviteToInviteModel,
11321181
groupUpdateLastJoinDate,
11331182
deleteGroupRole,
1183+
skipOnboardingUsersHavingApprovedExtensionRequest,
11341184
};

models/logs.js

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -193,20 +193,24 @@ const fetchAllLogs = async (query) => {
193193
throw error;
194194
}
195195

196-
const buildTimestamp = (date) => ({
197-
_seconds: Math.floor(date / 1000),
198-
_nanoseconds: 0,
196+
const buildTimestamp = (milliseconds) => ({
197+
_seconds: Math.floor(milliseconds / 1000),
198+
_nanoseconds: (milliseconds % 1000) * 1000000,
199199
});
200200

201201
if (startDate) {
202-
requestQuery = requestQuery.where("timestamp", ">=", buildTimestamp(startDate));
202+
const startTimestamp = buildTimestamp(startDate);
203+
requestQuery = requestQuery.where("timestamp._seconds", ">=", startTimestamp._seconds);
203204
}
205+
204206
if (endDate) {
205-
requestQuery = requestQuery.where("timestamp", "<=", buildTimestamp(endDate));
207+
const endTimestamp = buildTimestamp(endDate);
208+
requestQuery = requestQuery.where("timestamp._seconds", "<=", endTimestamp._seconds);
206209
}
207210
}
208211

209212
requestQuery = requestQuery.orderBy("timestamp", "desc");
213+
210214
let requestQueryDoc = requestQuery;
211215

212216
if (prev) {

models/requests.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ export const updateRequest = async (id: string, body: any, lastModifiedBy: strin
7171

7272
export const getRequests = async (query: any) => {
7373
let { id, type, requestedBy, state, prev, next, page, size = SIZE } = query;
74+
const dev = query.dev === "true";
75+
7476
size = parseInt(size);
7577
page = parseInt(page);
7678
try {
@@ -86,11 +88,15 @@ export const getRequests = async (query: any) => {
8688
...requestDoc.data(),
8789
};
8890
}
89-
90-
if (requestedBy) {
91+
92+
if(requestedBy && dev){
93+
requestQuery = requestQuery.where("requestedBy", "==", requestedBy);
94+
}
95+
else if (requestedBy) {
9196
const requestedByUserId = await getUserId(requestedBy);
9297
requestQuery = requestQuery.where("requestedBy", "==", requestedByUserId);
9398
}
99+
94100
if (type) {
95101
requestQuery = requestQuery.where("type", "==", type);
96102
}

0 commit comments

Comments
 (0)