Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated function definition and calling signature for addOrUpdate function for users collection #1977

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
Open
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
29 changes: 5 additions & 24 deletions controllers/external-accounts.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ const getExternalAccountData = async (req, res) => {
};
const linkExternalAccount = async (req, res) => {
try {
const { id: userId, roles } = req.userData;
const { id: userId } = req.userData;

const externalAccountData = await externalAccountsModel.fetchExternalAccountData(req.query, req.params.token);
if (!externalAccountData.id) {
Expand All @@ -62,7 +62,7 @@ const linkExternalAccount = async (req, res) => {

await addOrUpdate(
{
roles: { ...roles, in_discord: true },
"roles.in_discord": true,
discordId: attributes.discordId,
discordJoinedAt: attributes.discordJoinedAt,
},
Expand All @@ -85,52 +85,33 @@ const linkExternalAccount = async (req, res) => {
const syncExternalAccountData = async (req, res) => {
try {
const [discordUserData, rdsUserData] = await Promise.all([getDiscordMembers(), retrieveDiscordUsers()]);
const rdsUserDataMap = {};
const updateUserDataPromises = [];
const userUpdatedWithInDiscordFalse = [];
const updateArchivedPromises = [];

rdsUserData.forEach((rdsUser) => {
rdsUserDataMap[rdsUser.discordId] = {
id: rdsUser.id,
roles: rdsUser.roles,
};
});

for (const rdsUser of rdsUserData) {
const discordUser = discordUserData.find((discordUser) => discordUser.user.id === rdsUser.discordId);

let userData = {};
if (rdsUser.roles?.in_discord && !discordUser) {
userData = {
roles: {
...rdsUser.roles,
in_discord: false,
},
"roles.in_discord": false,
};
userUpdatedWithInDiscordFalse.push(rdsUser);
} else if (discordUser) {
userData = {
discordJoinedAt: discordUser.joined_at,
roles: {
...rdsUser.roles,
in_discord: true,
},
"roles.in_discord": true,
};
}
updateUserDataPromises.push(addOrUpdate(userData, rdsUser.id));
}

await Promise.all(updateUserDataPromises);

const inDiscordUsers = await getUsersByRole("in_discord");
inDiscordUsers.forEach((user) => {
if (user.roles.archived === true) {
const userData = {
roles: {
...user.roles,
archived: false,
},
"roles.archived": false,
};
updateArchivedPromises.push(addOrUpdate(userData, user.id));
}
Expand Down
33 changes: 5 additions & 28 deletions controllers/staging.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,16 @@
const { addOrUpdate, getUsersByRole } = require("../models/users");
const { flattenObject } = require("../utils/flattenObject");

const updateRoles = async (req, res) => {
try {
const userData = await req.userData;
if (process.env.NODE_ENV === "production") {
return res.status(403).json({
message: "FORBIDDEN | To be used only in staging and development",
});
}
const userId = req.userData.id;
await addOrUpdate(
{
roles: {
...userData.roles,
...req.body,
},
updated_at: Date.now(),
},
userId
);
const rolesToBeAdded = flattenObject({ roles: req.body });
await addOrUpdate(rolesToBeAdded, userId);
return res.status(200).json({
message: "Roles Updated successfully",
});
Expand All @@ -42,28 +34,13 @@ const removePrivileges = async (req, res) => {
const superUsers = await getUsersByRole("super_user");

members.forEach((member) => {
updateUserPromises.push(
addOrUpdate(
{
roles: {
...member.roles,
member: false,
},
updated_at: Date.now(),
},
member.id
)
);
updateUserPromises.push(addOrUpdate({ "roles.member": false }, member.id));
});
superUsers.forEach((superUser) => {
updateUserPromises.push(
addOrUpdate(
{
roles: {
...superUser.roles,
super_user: false,
},
updated_at: Date.now(),
"roles.super_user": false,
},
superUser.id
)
Expand Down
7 changes: 5 additions & 2 deletions controllers/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const {
const { addLog } = require("../models/logs");
const { getUserStatus } = require("../models/userStatus");
const config = require("config");
const { flattenObject } = require("../utils/flattenObject");
const discordDeveloperRoleId = config.get("discordDeveloperRoleId");

const verifyUser = async (req, res) => {
Expand Down Expand Up @@ -420,7 +421,8 @@ const updateSelf = async (req, res) => {
}
}

const updatedUser = await userQuery.addOrUpdate(req.body, userId);
const updatePayload = flattenObject(req.body);
const updatedUser = await userQuery.addOrUpdate(updatePayload, userId);

if (!updatedUser.isNewUser) {
// Success criteria, user finished the sign-up process.
Expand Down Expand Up @@ -837,7 +839,8 @@ const updateRoles = async (req, res) => {

const response = await getRoleToUpdate(result.user, dataToUpdate);
if (response.updateRole) {
await userQuery.addOrUpdate(response.newUserRoles, result.user.id);
const rolesToBeUpdated = flattenObject(response.newUserRoles);
await userQuery.addOrUpdate(rolesToBeUpdated, result.user.id);
if (dataToUpdate?.archived) {
const body = {
reason: reason || "",
Expand Down
11 changes: 3 additions & 8 deletions models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,9 @@ const addOrUpdate = async (userData, userId = null) => {
const isNewUser = !user.data();
// user exists update user
if (user.data()) {
await userModel.doc(userId).set(
{
...user.data(),
...userData,
updated_at: Date.now(),
},
{ merge: true }
);
const userRef = userModel.doc(userId);
userData.updated_at = Date.now();
await userRef.update(userData);
}

return { isNewUser, userId };
Expand Down
8 changes: 5 additions & 3 deletions test/integration/users.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const cookieName = config.get("userToken.cookieName");
const { userPhotoVerificationData } = require("../fixtures/user/photo-verification");
const Sinon = require("sinon");
const { INTERNAL_SERVER_ERROR, SOMETHING_WENT_WRONG } = require("../../constants/errorMessages");
const { flattenObject } = require("../../utils/flattenObject");
const photoVerificationModel = firestore.collection("photo-verification");

chai.use(chaiHttp);
Expand Down Expand Up @@ -2111,9 +2112,10 @@ describe("Users", function () {
archived: true,
in_discord: false,
};
await addOrUpdate({ ...userData[0], roles }, userId1);
await addOrUpdate({ ...userData[1], roles }, userId2);
await addOrUpdate({ ...userData[2], roles }, userId3);
const newRolesToBeAdded = flattenObject({ roles: roles });
await addOrUpdate(newRolesToBeAdded, userId1);
await addOrUpdate(newRolesToBeAdded, userId2);
await addOrUpdate(newRolesToBeAdded, userId3);

const res = await chai
.request(app)
Expand Down
43 changes: 43 additions & 0 deletions test/unit/utils/flattenObject.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const { expect } = require("chai");
import { flattenObject } from "../../../utils/flattenObject";

describe("flattenObject", function () {
it("flattenObject should flatten nested objects", () => {
const input = {
key1: "value1",
key2: {
key3: "value2",
key4: {
key5: "value3",
},
},
};
const expectedOutput = {
key1: "value1",
"key2.key3": "value2",
"key2.key4.key5": "value3",
};
const result = flattenObject(input);
expect(result).to.deep.equal(expectedOutput);
});

it("flattenObject should handle empty input", () => {
const input = {};
const expectedOutput = {};
const result = flattenObject(input);
expect(result).to.deep.equal(expectedOutput);
});

it("flattenObject should handle arrays", () => {
const input = {
key1: "value1",
key2: ["value2", "value3"],
};
const expectedOutput = {
key1: "value1",
key2: ["value2", "value3"],
};
const result = flattenObject(input);
expect(result).to.deep.equal(expectedOutput);
});
});
7 changes: 3 additions & 4 deletions test/utils/addUser.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const users = require("../../models/users");
const { flattenObject } = require("../../utils/flattenObject");

// Import fixtures
const userData = require("../fixtures/user/user")();
Expand All @@ -23,10 +24,8 @@ module.exports = async (user) => {
const { userId } = await users.addOrUpdate(userWithoutRoles);

if (rolesToBeAdded) {
const persistedUser = (await users.fetchUser({ userId })).user;
const existingRoles = persistedUser.roles;
const rolesToBeUpdated = { ...existingRoles, ...rolesToBeAdded };
await users.addOrUpdate({ roles: rolesToBeUpdated }, userId);
const rolesToBeUpdated = flattenObject({ roles: rolesToBeAdded });
await users.addOrUpdate(rolesToBeUpdated, userId);
}
return userId;
};
27 changes: 27 additions & 0 deletions utils/flattenObject.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Recursively flattens an object with nested objects and arrays into a single-depth object.
*
* @param {object} obj - The object to flatten.
* @param {string} [parentKey=""] - The parent key for nested objects.
* @return {object} The flattened object.
*/

export function flattenObject(obj, parentKey = "") {
return Object.keys(obj).reduce((acc, key) => {
const newKey = parentKey ? `${parentKey}.${key}` : key;
if (Array.isArray(obj[key])) {
acc[newKey] = obj[key].map((item, index) => {
if (typeof item === "object" && item !== null) {
return flattenObject(item, `${newKey}[${index}]`);
} else {
return item;
}
});
} else if (typeof obj[key] === "object" && obj[key] !== null) {
Object.assign(acc, flattenObject(obj[key], newKey));
} else {
acc[newKey] = obj[key];
}
return acc;
}, {});
}
Loading