Skip to content

Commit ddecd5f

Browse files
committed
feat: DevRank Data Enrichment (LinkedIn & Orgs) (#9025)
- Added logic to fetch and parse LinkedIn URLs. - Implemented Organization fetching using REST API () to support public memberships without scope.
1 parent 62a62ac commit ddecd5f

1 file changed

Lines changed: 39 additions & 6 deletions

File tree

apps/devrank/services/Updater.mjs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ class Updater extends Base {
7272
* @private
7373
*/
7474
async fetchUserData(username) {
75-
// 1. Fetch Basic Profile
75+
// 1. Fetch Basic Profile & Social Accounts
7676
const profileQuery = `
7777
query {
7878
user(login: "${username}") {
@@ -86,6 +86,12 @@ class Updater extends Base {
8686
isHireable
8787
twitterUsername
8888
websiteUrl
89+
socialAccounts(first: 5) {
90+
nodes {
91+
provider
92+
url
93+
}
94+
}
8995
}
9096
}`;
9197

@@ -100,12 +106,37 @@ class Updater extends Base {
100106

101107
if (!profileRes?.user) return null;
102108

103-
const { createdAt, avatarUrl, name, location, company, bio, followers } = profileRes.user;
109+
const { createdAt, avatarUrl, name, location, company, bio, followers, socialAccounts } = profileRes.user;
104110
const startYear = new Date(createdAt).getFullYear();
105111
const currentYear = new Date().getFullYear();
106112

107-
// 2. Build Multi-Year Contribution Query
108-
// We construct a single dynamic query aliasing each year
113+
// Extract LinkedIn URL
114+
let linkedin_url = null;
115+
116+
const linkedInAccount = socialAccounts?.nodes?.find(acc => acc.provider === 'LINKEDIN');
117+
if (linkedInAccount) {
118+
linkedin_url = linkedInAccount.url;
119+
} else if (profileRes.user.websiteUrl && profileRes.user.websiteUrl.includes('linkedin.com/in/')) {
120+
linkedin_url = profileRes.user.websiteUrl;
121+
}
122+
123+
// 2. Fetch Organizations (REST API for Public Memberships)
124+
// GraphQL requires 'read:org' scope even for public orgs, whereas REST /users/:username/orgs does not.
125+
let orgs = [];
126+
try {
127+
const orgRes = await GitHub.rest(`users/${username}/orgs`);
128+
if (Array.isArray(orgRes)) {
129+
orgs = orgRes.map(org => ({
130+
name: org.login, // REST API often just gives login, description is separate.
131+
avatar_url: org.avatar_url,
132+
login: org.login
133+
}));
134+
}
135+
} catch (e) {
136+
console.warn(`[Updater] Skipped orgs for ${username} (REST Error): ${e.message}`);
137+
}
138+
139+
// 3. Build Multi-Year Contribution Query
109140
let contribQuery = `query { user(login: "${username}") {`;
110141
for (let year = startYear; year <= currentYear; year++) {
111142
contribQuery += ` y${year}: contributionsCollection(from: "${year}-01-01T00:00:00Z", to: "${year}-12-31T23:59:59Z") { contributionCalendar { totalContributions } }`;
@@ -115,7 +146,7 @@ class Updater extends Base {
115146
const contribRes = await GitHub.query(contribQuery);
116147
if (!contribRes?.user) return null;
117148

118-
// 3. Aggregate Data
149+
// 4. Aggregate Data
119150
let total = 0;
120151
const yearsData = {};
121152

@@ -139,7 +170,9 @@ class Updater extends Base {
139170
total_contributions: total,
140171
years: yearsData,
141172
first_year: startYear,
142-
last_updated: new Date().toISOString()
173+
last_updated: new Date().toISOString(),
174+
linkedin_url,
175+
organizations: orgs
143176
};
144177
}
145178
}

0 commit comments

Comments
 (0)