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
34 changes: 0 additions & 34 deletions models/userStatus.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,39 +169,6 @@ const getUserStatus = async (userId) => {
}
};

/**
* @returns {Promise<userStatusModel|Array>} : returns an array of all the userStatus
*/
const getAllUserStatus = async (query) => {
try {
const allUserStatus = [];
let data;
if (!query.state) {
data = await userStatusModel.get();
} else {
data = await userStatusModel
.where("currentStatus.state", "==", query.state)
.orderBy("currentStatus.from", "asc")
.get();
}
data.forEach((doc) => {
const docData = doc.data();
const currentUserStatus = {
id: doc.id,
userId: docData.userId,
currentStatus: docData.currentStatus,
monthlyHours: docData.monthlyHours,
idleFrom: docData.idleFrom ?? null,
};
allUserStatus.push(currentUserStatus);
});
return { allUserStatus };
} catch (error) {
logger.error(`error in fetching the User Status of all Users. ${error}`);
throw error;
}
};

/**
* @param userId { String }: Id of the User
* @param newStatusData { Object }: Data to be Updated
Expand Down Expand Up @@ -775,7 +742,6 @@ const getUserStatusForUserIds = async (userIds) => {
module.exports = {
deleteUserStatus,
getUserStatus,
getAllUserStatus,
updateUserStatus,
updateAllUserStatus,
updateUserStatusOnNewTaskAssignment,
Expand Down
62 changes: 36 additions & 26 deletions models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const walletConstants = require("../constants/wallets");

const firestore = require("../utils/firestore");
const { fetchWallet, createWallet } = require("../models/wallets");
const { updateUserStatus } = require("../models/userStatus");
const { updateUserStatus, getUserStatusForUserIds } = require("../models/userStatus");
const { arraysHaveCommonItem, chunks } = require("../utils/array");
const {
ALLOWED_FILTER_PARAMS,
Expand All @@ -21,7 +21,6 @@ const ROLES = require("../constants/roles");
const userModel = firestore.collection("users");
const joinModel = firestore.collection("applicants");
const itemModel = firestore.collection("itemTags");
const userStatusModel = firestore.collection("usersStatus");
const photoVerificationModel = firestore.collection("photo-verification");
const { ITEM_TAG, USER_STATE } = ALLOWED_FILTER_PARAMS;
const admin = require("firebase-admin");
Expand Down Expand Up @@ -628,62 +627,73 @@ const getRdsUserInfoByGitHubUsername = async (githubUsername) => {
* @return {Promise<Array>} - Array of user documents that match the filter criteria
*/

const getActiveDiscordUsers = async () => {
const snapshot = await userModel.where("roles.in_discord", "==", true).where("roles.archived", "==", false).get();
return snapshot.docs.map((doc) => ({ id: doc.id, ...doc.data() }));
};

const getUsersBasedOnFilter = async (query) => {
const allQueryKeys = Object.keys(query);
const doesTagQueryExist = arraysHaveCommonItem(ITEM_TAG, allQueryKeys);
const doesStateQueryExist = arraysHaveCommonItem(USER_STATE, allQueryKeys);

const calls = {
item: itemModel,
state: userStatusModel,
item: itemModel.where("itemType", "==", "USER").where("tagType", "==", "SKILL"),
};
calls.item = calls.item.where("itemType", "==", "USER").where("tagType", "==", "SKILL");

Object.entries(query).forEach(([key, value]) => {
const isTagKey = ITEM_TAG.includes(key);
const isStateKey = USER_STATE.includes(key);
const isValueArray = Array.isArray(value);

if (isTagKey) {
calls.item = isValueArray ? calls.item.where(key, "in", value) : calls.item.where(key, "==", value);
} else if (isStateKey) {
calls.state = isValueArray
? calls.state.where("currentStatus.state", "in", value)
: calls.state.where("currentStatus.state", "==", value);
}
});

const tagItems = doesTagQueryExist ? (await calls.item.get()).docs.map((doc) => ({ id: doc.id, ...doc.data() })) : [];
const stateItems = doesStateQueryExist
? (await calls.state.get()).docs.map((doc) => ({ id: doc.id, ...doc.data() }))
: [];

let stateMatchedUsers = [];
let stateItems = [];
if (doesStateQueryExist) {
const activeUsers = await getActiveDiscordUsers();
const statusMap = await getUserStatusForUserIds(activeUsers.map((user) => user.id));
const requestedStates = Array.isArray(query.state) ? query.state : [query.state];

stateMatchedUsers = activeUsers.filter((user) => {
const status = statusMap[user.id];
return status?.currentStatus?.state && requestedStates.includes(status.currentStatus.state);
});
stateItems = stateMatchedUsers
.map((user) => statusMap[user.id])
.filter(Boolean)
.map((status) => ({ id: status.id, ...status }));
}

let finalItems = [];

if (doesTagQueryExist && doesStateQueryExist) {
if (stateItems.length && tagItems.length) {
const stateItemIds = new Set(stateItems.map((item) => item.userId));
if (stateMatchedUsers.length && tagItems.length) {
const stateItemIds = new Set(stateMatchedUsers.map((user) => user.id));
finalItems = tagItems.filter((item) => stateItemIds.has(item.itemId)).map((item) => item.itemId);
}
} else if (doesStateQueryExist) {
finalItems = stateItems.map((item) => item.userId);
if (query.time && query.state === "ONBOARDING") {
return getUsersWithOnboardingStateInRange(stateMatchedUsers, stateItems, query.time);
}
return stateMatchedUsers;
} else if (doesTagQueryExist) {
finalItems = tagItems.map((item) => item.itemId);
}

if (finalItems.length) {
finalItems = [...new Set(finalItems)];
if (doesStateQueryExist) {
const stateUserMap = new Map(stateMatchedUsers.map((user) => [user.id, user]));
return finalItems.map((id) => stateUserMap.get(id)).filter(Boolean);
}
const userRefs = finalItems.map((itemId) => userModel.doc(itemId));
const userDocs = (await firestore.getAll(...userRefs)).map((doc) => ({ id: doc.id, ...doc.data() }));
const filteredUserDocs = userDocs.filter((doc) => !doc.roles?.archived);
if (query.time && query.state === "ONBOARDING") {
const fetchUsersWithOnBoardingState = await getUsersWithOnboardingStateInRange(
filteredUserDocs,
stateItems,
query.time
);
return fetchUsersWithOnBoardingState;
}
return filteredUserDocs;
return userDocs.filter((doc) => !doc.roles?.archived);
}

const { role: roleQuery, verified: verifiedQuery } = query;
Expand Down
6 changes: 6 additions & 0 deletions test/fixtures/user/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ module.exports = () => {
twitter_id: "whatifi",
discordJoinedAt: "2023-04-06T01:47:34.488000+00:00",
phone: "1234567891",
roles: {
archived: false,
in_discord: true,
},
picture: {
publicId: "profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar",
url: "https://res.cloudinary.com/realdevsquad/image/upload/v1667685133/profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar.jpg",
Expand All @@ -78,6 +82,8 @@ module.exports = () => {
email: "pgajjewar@gmail.com",
roles: {
restricted: true,
archived: false,
in_discord: false,
},
picture: {
publicId: "profile/mtS4DhUvNYsKqI7oCWVB/aenklfhtjldc5ytei3ar",
Expand Down
8 changes: 7 additions & 1 deletion test/integration/usersFilter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,13 @@ describe("Filter Users", function () {
);
activeUser = await addUser(userData[8]);
await updateUserStatus(activeUser, generateUserStatusData(userState.ACTIVE, updatedAtDate, updatedAtDate));
onboardingUser = await addUser(userData[2]);
// Override: fixture[2] is not in_discord (used elsewhere); this case needs an active onboarding user
// with a recent join so ONBOARDING+time=31d still returns none
onboardingUser = await addUser({
...userData[2],
roles: { ...userData[2].roles, in_discord: true, archived: false },
discordJoinedAt: new Date().toISOString(),
});
await updateUserStatus(onboardingUser, generateUserStatusData(userState.ONBOARDING, updatedAtDate, updatedAtDate));

// creating tag and levels
Expand Down
16 changes: 15 additions & 1 deletion test/unit/models/users.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,10 @@ describe("users", function () {
beforeEach(async function () {
const userArr = userData();
userId0 = await addUser(userArr[0]);
userId1 = await addUser(userArr[1]);
userId1 = await addUser({
...userArr[1],
discordJoinedAt: new Date().toISOString(),
});
userId2 = await addUser(userArr[2]);
await userStatusModel.doc("userStatus000").set(generateStatusDataForState(userId0, userState.ONBOARDING));
await userStatusModel.doc("userStatus001").set(generateStatusDataForState(userId1, userState.ONBOARDING));
Expand All @@ -541,6 +544,17 @@ describe("users", function () {
};
const result = await users.getUsersBasedOnFilter(query);
expect(result.length).to.equal(1);
expect(result[0].id).to.equal(userId0);
});

it("should only include in_discord non-archived users when filtering by state", async function () {
const result = await users.getUsersBasedOnFilter({ state: "IDLE" });
expect(result.length).to.equal(0);
});

it("should return active discord users matching state", async function () {
const result = await users.getUsersBasedOnFilter({ state: "ONBOARDING" });
expect(result.map((user) => user.id).sort()).to.deep.equal([userId0, userId1].sort());
});
});

Expand Down
Loading