-
Notifications
You must be signed in to change notification settings - Fork 6.5k
chore: add github sponsors on supporters #8531
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
46d49e7
00e81c5
309a8bb
70c5076
21655fa
448e37e
0ad4e41
6da91ff
fccada1
29829ce
f419e15
c493a8d
6f5435b
536b553
7cb53c4
492c9ed
512ba91
dfdf2ab
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,9 @@ | ||
| import { OPENCOLLECTIVE_MEMBERS_URL } from '#site/next.constants.mjs'; | ||
| import { | ||
| OPENCOLLECTIVE_MEMBERS_URL, | ||
| GITHUB_GRAPHQL_URL, | ||
| GITHUB_API_KEY, | ||
| } from '#site/next.constants.mjs'; | ||
| import { shuffle } from '#site/util/array'; | ||
| import { fetchWithRetry } from '#site/util/fetch'; | ||
|
|
||
| /** | ||
|
|
@@ -15,15 +20,201 @@ async function fetchOpenCollectiveData() { | |
| const members = payload | ||
| .filter(({ role, isActive }) => role === 'BACKER' && isActive) | ||
| .sort((a, b) => b.totalAmountDonated - a.totalAmountDonated) | ||
| .map(({ name, website, image, profile }) => ({ | ||
| .map(({ name, image, profile }) => ({ | ||
| name, | ||
| image, | ||
| url: website, | ||
| profile, | ||
| url: profile, | ||
|
bjohansebas marked this conversation as resolved.
Outdated
|
||
| source: 'opencollective', | ||
| })); | ||
|
|
||
| return members; | ||
| } | ||
|
|
||
| export default fetchOpenCollectiveData; | ||
| /** | ||
| * Fetches supporters data from Github API, filters active backers, | ||
| * and maps it to the Supporters type. | ||
| * | ||
| * @returns {Promise<Array<import('#site/types/supporters').GithubSponsorSupporter>>} Array of supporters | ||
| */ | ||
| async function fetchGithubSponsorsData() { | ||
|
bjohansebas marked this conversation as resolved.
|
||
| if (!GITHUB_API_KEY) { | ||
| return []; | ||
| } | ||
|
|
||
| const sponsors = []; | ||
|
|
||
| // Fetch sponsorship pages | ||
| let cursor = null; | ||
|
|
||
| while (true) { | ||
| const query = sponsorshipsQuery(cursor); | ||
| const data = await graphql(query); | ||
|
|
||
| if (data.errors) { | ||
| throw new Error(JSON.stringify(data.errors)); | ||
| } | ||
|
|
||
| const nodeRes = data.data.organization?.sponsorshipsAsMaintainer; | ||
| if (!nodeRes) { | ||
| break; | ||
| } | ||
|
|
||
| const { nodes, pageInfo } = nodeRes; | ||
| const mapped = nodes.map(n => { | ||
| const s = n.sponsor || n.sponsorEntity || n.sponsorEntity; // support different field names | ||
|
cursor[bot] marked this conversation as resolved.
Outdated
|
||
| return { | ||
| name: s?.name || s?.login || null, | ||
| image: s?.avatarUrl || null, | ||
| url: s?.url || null, | ||
| source: 'github', | ||
| }; | ||
|
bjohansebas marked this conversation as resolved.
|
||
| }); | ||
|
|
||
| sponsors.push(...mapped); | ||
|
|
||
| if (!pageInfo.hasNextPage) { | ||
| break; | ||
| } | ||
|
|
||
| cursor = pageInfo.endCursor; | ||
| } | ||
|
|
||
| const query = donationsQuery(); | ||
| const data = await graphql(query); | ||
|
|
||
| if (data.errors) { | ||
| throw new Error(JSON.stringify(data.errors)); | ||
| } | ||
|
|
||
| const nodeRes = data.data.organization?.sponsorsActivities; | ||
|
bjohansebas marked this conversation as resolved.
|
||
| if (!nodeRes) { | ||
| return sponsors; | ||
| } | ||
|
|
||
| const { nodes } = nodeRes; | ||
|
bjohansebas marked this conversation as resolved.
|
||
| const mapped = nodes.map(n => { | ||
| const s = n.sponsor || n.sponsorEntity || n.sponsorEntity; // support different field names | ||
| return { | ||
| name: s?.name || s?.login || null, | ||
| image: s?.avatarUrl || null, | ||
| url: s?.url || null, | ||
| source: 'github', | ||
| }; | ||
| }); | ||
|
bjohansebas marked this conversation as resolved.
|
||
|
|
||
| sponsors.push(...mapped); | ||
|
bjohansebas marked this conversation as resolved.
Outdated
bjohansebas marked this conversation as resolved.
Outdated
|
||
|
|
||
| return sponsors; | ||
|
bjohansebas marked this conversation as resolved.
Outdated
bjohansebas marked this conversation as resolved.
Outdated
|
||
| } | ||
|
|
||
| function sponsorshipsQuery(cursor = null) { | ||
| return ` | ||
| query { | ||
| organization(login: "nodejs") { | ||
| sponsorshipsAsMaintainer (first: 100, includePrivate: false, after: "${cursor}", activeOnly: false) { | ||
|
bjohansebas marked this conversation as resolved.
Outdated
bjohansebas marked this conversation as resolved.
Outdated
bjohansebas marked this conversation as resolved.
Outdated
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. definitely, I’d prefer that we only show sponsors that are active, but why do we want to show everyone? see #8268.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wdyt @nodejs/nodejs-website @nodejs/marketing ?
cursor[bot] marked this conversation as resolved.
Outdated
|
||
| nodes { | ||
| sponsor: sponsorEntity { | ||
| ...on User { | ||
| id: databaseId, | ||
| name, | ||
| login, | ||
| avatarUrl, | ||
| url, | ||
| websiteUrl | ||
| } | ||
| ...on Organization { | ||
| id: databaseId, | ||
| name, | ||
| login, | ||
| avatarUrl, | ||
| url, | ||
| websiteUrl | ||
| } | ||
| }, | ||
| } | ||
|
bjohansebas marked this conversation as resolved.
Outdated
|
||
| pageInfo { | ||
| endCursor | ||
| startCursor | ||
| hasNextPage | ||
| hasPreviousPage | ||
| } | ||
| } | ||
| } | ||
| }`; | ||
| } | ||
|
|
||
| function donationsQuery() { | ||
| return ` | ||
| query { | ||
| organization(login: "nodejs") { | ||
| sponsorsActivities (first: 100, includePrivate: false) { | ||
| nodes { | ||
| id | ||
| sponsor { | ||
| ...on User { | ||
| id: databaseId, | ||
| name, | ||
| login, | ||
| avatarUrl, | ||
| url, | ||
| websiteUrl | ||
| } | ||
| ...on Organization { | ||
| id: databaseId, | ||
| name, | ||
| login, | ||
| avatarUrl, | ||
| url, | ||
| websiteUrl | ||
| } | ||
| }, | ||
| timestamp | ||
| tier: sponsorsTier { | ||
| monthlyPriceInDollars, | ||
| isOneTime | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }`; | ||
| } | ||
|
|
||
|
bjohansebas marked this conversation as resolved.
Outdated
|
||
| const graphql = async (query, variables = {}) => { | ||
| const res = await fetch(GITHUB_GRAPHQL_URL, { | ||
| method: 'POST', | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| Authorization: `Bearer ${GITHUB_API_KEY}`, | ||
|
bjohansebas marked this conversation as resolved.
Outdated
|
||
| }, | ||
| body: JSON.stringify({ query, variables }), | ||
| }); | ||
|
bjohansebas marked this conversation as resolved.
|
||
|
|
||
| if (!res.ok) { | ||
| const text = await res.text(); | ||
| throw new Error(`GitHub API error: ${res.status} ${text}`); | ||
| } | ||
|
|
||
| return res.json(); | ||
| }; | ||
|
|
||
| /** | ||
| * Fetches supporters data from Open Collective API and GitHub Sponsors, filters active backers, | ||
| * and maps it to the Supporters type. | ||
| * | ||
| * @returns {Promise<Array<import('#site/types/supporters').OpenCollectiveSupporter | import('#site/types/supporters').GithubSponsorSupporter>>} Array of supporters | ||
| */ | ||
| async function sponsorsData() { | ||
| const seconds = 300; // Change every 5 minutes | ||
| const seed = Math.floor(Date.now() / (seconds * 1000)); | ||
|
|
||
| const sponsors = await Promise.all([ | ||
| fetchGithubSponsorsData(), | ||
| fetchOpenCollectiveData(), | ||
| ]); | ||
|
|
||
| const shuffled = await shuffle(sponsors.flat(), seed); | ||
|
|
||
| return shuffled; | ||
| } | ||
|
Comment on lines
+220
to
+241
|
||
|
|
||
| export default sponsorsData; | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -7,3 +7,4 @@ export type Supporter<T extends string> = { | |||||
| }; | ||||||
|
|
||||||
| export type OpenCollectiveSupporter = Supporter<'opencollective'>; | ||||||
| export type GithubSponsorSupporter = Supporter<'github'>; | ||||||
|
||||||
| export type GithubSponsorSupporter = Supporter<'github'>; | |
| export type GitHubSponsorSupporter = Supporter<'github'>; |
Uh oh!
There was an error while loading. Please reload this page.