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

Feat/GitHub duplication #1818

Merged
merged 22 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4017e99
Refactor : Modified callback api to have github user id
joyguptaa Dec 28, 2023
7dacd3e
Feat : Added API to add github_user_id to all users
joyguptaa Dec 28, 2023
8407103
Test : Added test for github_user_id script
joyguptaa Dec 28, 2023
d4ae3ac
Bug : Fixed logic while for checking github_user_id
joyguptaa Dec 28, 2023
9f0cc96
Chore : Minor Fixes
joyguptaa Jan 4, 2024
3a3d361
Refactor : Moved code to models
joyguptaa Jan 5, 2024
3a4c604
Merge branch 'develop' into feat/github-duplication
joyguptaa Jan 5, 2024
a8f3d7e
Test : Added failing mock data
joyguptaa Jan 5, 2024
483527c
Merge branch 'feat/github-duplication' of https://github.com/Real-Dev…
joyguptaa Jan 5, 2024
5787ca3
Merge branch 'develop' into feat/github-duplication
joyguptaa Jan 6, 2024
788f63d
Merge branch 'develop' into feat/github-duplication
joyguptaa Jan 7, 2024
8d70a3a
Merge branch 'develop' into feat/github-duplication
joyguptaa Jan 7, 2024
8aa5d6f
Feat : Updated API params
joyguptaa Jan 15, 2024
f1844d6
Feat : Added pagination logic to migrations API
joyguptaa Jan 15, 2024
78e6600
Merge branch 'feat/github-duplication' of https://github.com/Real-Dev…
joyguptaa Jan 15, 2024
61881fb
Merge branch 'develop' into feat/github-duplication
joyguptaa Jan 16, 2024
eded930
Test : Updated the test case
joyguptaa Jan 16, 2024
8f29087
Feat : Updated query params
joyguptaa Jan 16, 2024
8e1eb5f
Test : Updated test cases for migration api
joyguptaa Jan 16, 2024
1c3af94
Merge branch 'feat/github-duplication' of https://github.com/Real-Dev…
joyguptaa Jan 16, 2024
eacc712
Merge branch 'develop' into feat/github-duplication
joyguptaa Jan 17, 2024
86ae835
Merge branch 'develop' into feat/github-duplication
Ajeyakrishna-k Jan 18, 2024
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
1 change: 1 addition & 0 deletions controllers/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const githubAuthCallback = (req, res, next) => {
github_id: user.username,
github_display_name: user.displayName,
github_created_at: Number(new Date(user._json.created_at).getTime()),
github_user_id: user.id,
created_at: Date.now(),
updated_at: Date.now(),
};
Expand Down
66 changes: 66 additions & 0 deletions controllers/users.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const firestore = require("../utils/firestore");
const chaincodeQuery = require("../models/chaincodes");
const userQuery = require("../models/users");
const profileDiffsQuery = require("../models/profileDiffs");
Expand Down Expand Up @@ -856,6 +857,70 @@ async function usersPatchHandler(req, res) {
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
}
}
const addGithubId = async (req, res) => {
const usersNotFound = [];
let countUserFound = 0;
let countUserNotFound = 0;
try {
const requestOptions = {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization:
"Basic " + btoa(`${config.get("githubOauth.clientId")}:${config.get("githubOauth.clientSecret")}`),
},
};
const usersSnapshot = await firestore.collection("users").get();
const totalUsers = usersSnapshot.docs.length;
const batchCount = Math.ceil(totalUsers / 500);
// Create batch write operations for each batch of documents
for (let i = 0; i < batchCount; i++) {
const batchDocs = usersSnapshot.docs.slice(i * 500, (i + 1) * 500);
joyguptaa marked this conversation as resolved.
Show resolved Hide resolved
const batchWrite = firestore.batch();
const batchWrites = [];
for (const userDoc of batchDocs) {
const githubUsername = userDoc.data().github_id;
const username = userDoc.data().username;
const userId = userDoc.id;
batchWrite.update(userDoc.ref, { github_user_id: null });

const promise = fetch(`https://api.github.com/users/${githubUsername}`, requestOptions)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we considered GitHub's rate limits?

Copy link
Contributor Author

@joyguptaa joyguptaa Jan 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc says that it is 5000 requests per hour for authenticated users. And I do not think that this will cause any issue as we anyway have less than 1k users.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It also says we cannot make more than 900 calls per minute on a single route. I think we have around 3000 users docs in our prod db.
https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#about-secondary-rate-limits
Screenshot 2024-01-07 at 10 05 44 PM
Screenshot 2024-01-07 at 10 07 39 PM

Ajeyakrishna-k marked this conversation as resolved.
Show resolved Hide resolved
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((data) => {
const githubUserId = data.id;
batchWrite.update(userDoc.ref, { github_user_id: `${githubUserId}` });
countUserFound++;
})
.catch((error) => {
countUserNotFound++;
const invalidUsers = { userId, username, githubUsername };
usersNotFound.push(invalidUsers);
logger.error("An error occurred at fetch:", error);
});
batchWrites.push(promise);
}
await Promise.all(batchWrites);
await batchWrite.commit();
}
return res.status(200).json({
message: "Result of migration",
data: {
totalUsers: totalUsers,
usersUpdated: countUserFound,
usersNotUpdated: countUserNotFound,
invalidUsersDetails: usersNotFound,
},
});
} catch (error) {
logger.error(`Error while Updating all users: ${error}`);
return res.boom.badImplementation(INTERNAL_SERVER_ERROR);
}
};

module.exports = {
verifyUser,
Expand Down Expand Up @@ -886,4 +951,5 @@ module.exports = {
updateDiscordUserNickname,
archiveUserIfNotInDiscord,
usersPatchHandler,
addGithubId,
};
11 changes: 8 additions & 3 deletions models/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,15 @@ const addOrUpdate = async (userData, userId = null) => {
}

// userId is null, Add or Update user
const user = await userModel.where("github_id", "==", userData.github_id).limit(1).get();
if (!user.empty) {
let user;
if (userData.github_user_id) {
user = await userModel.where("github_user_id", "==", userData.github_user_id).limit(1).get();
}
if (!user || (user && user.empty)) {
user = await userModel.where("github_id", "==", userData.github_id).limit(1).get();
}
if (user && !user.empty) {
Ajeyakrishna-k marked this conversation as resolved.
Show resolved Hide resolved
await userModel.doc(user.docs[0].id).set(userData, { merge: true });

return {
isNewUser: false,
userId: user.docs[0].id,
Expand Down
2 changes: 2 additions & 0 deletions routes/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,6 @@
router.patch("/rejectDiff", authenticate, authorizeRoles([SUPERUSER]), users.rejectProfileDiff);
router.patch("/:userId", authenticate, authorizeRoles([SUPERUSER]), users.updateUser);
router.get("/suggestedUsers/:skillId", authenticate, authorizeRoles([SUPERUSER]), users.getSuggestedUsers);
// WARNING!! - One time Script/Route to do migration
router.post("/migrate", authenticate, authorizeRoles([SUPERUSER]), users.addGithubId);
Fixed Show fixed Hide fixed
Ajeyakrishna-k marked this conversation as resolved.
Show resolved Hide resolved
module.exports = router;
166 changes: 166 additions & 0 deletions test/fixtures/user/prodUsers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
const prodUsers = [
{
profileURL: "https://my.realdevsquad.com/identity",
incompleteUserDetails: false,
discordJoinedAt: "2023-04-02T18:30:30.476000+00:00",
discordId: "1040700289348542566",
roles: {
archived: false,
in_discord: true,
member: true,
super_user: true,
},
last_name: "Prakash",
linkedin_id: "dev-amit-prakash",
picture: {
publicId: "profile/65QiTlqudZfDk3i5W9WO/iiti1pudbvbo9huvbqmk",
url: "https://res.cloudinary.com/realdevsquad/image/upload/v1691542131/profile/65QiTlqudZfDk3i5W9WO/iiti1pudbvbo9huvbqmk.jpg",
},
github_created_at: 1514318626000,
github_display_name: "Amit Prakash",
github_id: "iamitprakash",
company: "Boeing",
designation: "SDE-2",
twitter_id: "twts_tejas",
first_name: "Amit",
username: "amitprakash",
updated_at: 1702144542601,
created_at: 1702144542601,
},
{
incompleteUserDetails: false,
discordJoinedAt: "2023-04-13T05:49:42.784000+00:00",
website: "www.google.com",
discordId: "1095941231403597854",
roles: {
archived: false,
maven: true,
in_discord: true,
},
last_name: "Karanth",
linkedin_id: "ajeyakrishna-karanth-a229a8191",
yoe: 1,
company: "VTU",
designation: "SDE",
twitter_id: "AjeyakrishnaK",
first_name: "Ajeyakrishna",
username: "ajeyakrishna",
github_id: "Ajeyakrishna-k",
github_created_at: 1643691501000,
github_display_name: "Ajeyakrishna",
updated_at: 1702964506393,
created_at: 1702964506393,
},
{
profileURL: "https://ajoys-profile-service.onrender.com",
incompleteUserDetails: false,
website: "https://ajoykumardas.vercel.app",
discordJoinedAt: "2023-08-24T12:45:12.013000+00:00",
discordId: "661243030065643530",
roles: {
archived: false,
in_discord: true,
},
last_name: "Das",
linkedin_id: "https://www.linkedin.com/in/ajoy-kumar-das/",
picture: {
publicId: "profile/NMKS92IoTCGZzPopZu7b/s7pevs5aw7oh6yh2pj03",
url: "https://res.cloudinary.com/realdevsquad/image/upload/v1693214755/profile/NMKS92IoTCGZzPopZu7b/s7pevs5aw7oh6yh2pj03.png",
},
github_created_at: 1625806111000,
github_display_name: "Ajoy Kumar Das",
github_id: "ajoykumardas12",
company: "Government College of Engineering and Textile Technology, Serampore",
designation: "Graduate",
twitter_id: "ajoykdas",
first_name: "Ajoy Kumar",
username: "ajoy-kumar",
profileStatus: "VERIFIED",
updated_at: 1702905921930,
created_at: 1702905921930,
},

{
incompleteUserDetails: false,
discordJoinedAt: "2023-02-26T09:24:14.587000+00:00",
discordId: "799600467218923521",
roles: {
archived: false,
in_discord: true,
},
last_name: "Pawaskar",
linkedin_id: "Anish Pawaskar",
github_created_at: 1473931782000,
github_display_name: "Anish Pawaskar",
github_id: "anishpawaskar",
company: "Kirti M. Doongursee College",
designation: "Student",
twitter_id: "anish_pawaskar",
first_name: "Anish",
username: "anish-pawaskar",
updated_at: 1703474831637,
created_at: 1703474831637,
},
{
profileURL: "https://my.realdevsquad.com/identity",
discordJoinedAt: "2020-02-01T08:33:38.278000+00:00",
roles: {
archived: false,
in_discord: true,
member: true,
super_user: true,
admin: true,
},
yoe: "8",
github_created_at: 1341655281000,
company: "Amazon",
twitter_id: "ankushdharkar",
first_name: "Ankush",
" instagram_id": "ankushdharkar",
website: "NA",
incompleteUserDetails: false,
discordId: "154585730465660929",
linkedin_id: "ankushdharkar",
last_name: "Dharkar",
picture: {
publicId: "profile/XAF7rSUvk4p0d098qWYS/me40uk7taytbjaa67mhe",
url: "https://res.cloudinary.com/realdevsquad/image/upload/v1692058952/profile/XAF7rSUvk4p0d098qWYS/me40uk7taytbjaa67mhe.jpg",
},
github_display_name: "Ankush Dharkar",
company_name: "Amazon",
github_id: "ankushdharkar",
designation: "SDE",
status: "idle",
username: "ankush",
updated_at: 1703548515652,
created_at: 1703548515652,
},
{
profileURL: "https://my-profile-service-m5d2.onrender.com",
incompleteUserDetails: false,
discordJoinedAt: "2023-08-28T06:04:20.282000+00:00",
discordId: "751127804678242364",
roles: {
archived: false,
in_discord: true,
},
last_name: "Gupta",
linkedin_id: "ardourApex",
picture: {
publicId: "profile/AAM0MZxZXEfWKmfdYOUp/rsa1twaw1movhmmlyfsd",
url: "https://res.cloudinary.com/realdevsquad/image/upload/v1693248423/profile/AAM0MZxZXEfWKmfdYOUp/rsa1twaw1movhmmlyfsd.jpg",
},
github_created_at: 1570647377000,
github_display_name: "Joy",
github_id: "ardourApeX",
company: "Test Unity",
designation: "SDE1",
twitter_id: "ardourApex",
first_name: "Joy",
username: "joy-gupta",
profileStatus: "VERIFIED",
updated_at: 1703486457963,
created_at: 1703486457963,
},
];
module.exports = prodUsers;
56 changes: 55 additions & 1 deletion test/unit/models/users.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
const chai = require("chai");
const sinon = require("sinon");
const { expect } = chai;

const cleanDb = require("../../utils/cleanDb");
const users = require("../../../models/users");
const firestore = require("../../../utils/firestore");
Expand All @@ -22,6 +21,10 @@ const photoVerificationModel = firestore.collection("photo-verification");
const userData = require("../../fixtures/user/user");
const addUser = require("../../utils/addUser");
const { userState } = require("../../../constants/userStatus");
const app = require("../../../server");
const prodUsers = require("../../fixtures/user/prodUsers");
const authService = require("../../../services/authService");
const cookieName = config.get("userToken.cookieName");
/**
* Test the model functions and validate the data stored
*/
Expand Down Expand Up @@ -474,4 +477,55 @@ describe("users", function () {
expect(userListResult[0].discordId).to.be.deep.equal(userDataArray[0].discordId);
});
});
describe("Adding github_user_id for each user document", function () {
let userId, userToken, superUserId, superUserToken;
beforeEach(async function () {
userId = await addUser(prodUsers[1]);
userToken = authService.generateAuthToken({ userId: userId });
superUserId = await addUser(prodUsers[0]);
superUserToken = authService.generateAuthToken({ userId: superUserId });
});

afterEach(async function () {
await cleanDb();
});

it("API should not be accessible to any regular user", function (done) {
chai
.request(app)
.post("/users/migrate")
.set("cookie", `${cookieName}=${userToken}`)
.send()
.end((err, res) => {
if (err) {
return done();
}
expect(res).to.have.status(401);
expect(res.body).to.be.an("object");
expect(res.body).to.eql({
statusCode: 401,
error: "Unauthorized",
message: "You are not authorized for this action.",
});
return done();
});
});
it("API should be accessible by super user", async function () {
for (const user of prodUsers.slice(2)) {
await addUser(user);
}
const res = await chai
.request(app)
.post("/users/migrate")
.set("cookie", `${cookieName}=${superUserToken}`)
.send();

expect(res).to.have.status(200);
expect(res.body).to.have.property("data").that.is.an("object");
expect(res.body.data).to.have.property("totalUsers").that.is.a("number");
expect(res.body.data).to.have.property("usersUpdated").that.is.a("number");
expect(res.body.data).to.have.property("usersNotUpdated").that.is.a("number");
expect(res.body.data).to.have.property("invalidUsersDetails").that.is.an("array");
});
});
});
Loading