diff --git a/api/sponsors.mjs b/api/sponsors.mjs index c95c9a46..197b1fad 100644 --- a/api/sponsors.mjs +++ b/api/sponsors.mjs @@ -5,6 +5,12 @@ const COLLECTIVE = 'webpack'; const API = 'https://api.opencollective.com/graphql/v2'; const PAGE_SIZE = 1000; // Open Collective's maximum page size +// Accounts to merge into another account, keyed by the slug a contribution comes +// from. Used when a company pays through an employee's personal account. +const ALIASES = { + 'ag-grid-226': 'ag-grid', // John Masterson → AG Grid +}; + // ACTIVE covers live recurring subscriptions; PAID covers one-time contributions; // CANCELLED keeps lapsed sponsors' all-time totals in the dataset. const ORDERS_QUERY = ` @@ -106,20 +112,27 @@ export const getOpenCollectiveSponsors = async () => { const account = node.fromAccount; if (!account?.slug) continue; // guest/incognito contributions - const existing = bySlug.get(account.slug) ?? { + const slug = ALIASES[account.slug] ?? account.slug; + const profile = { name: account.name ?? account.slug, - slug: account.slug, imageUrl: account.imageUrl ?? null, - url: account.website ?? `https://opencollective.com/${account.slug}`, + url: account.website ?? `https://opencollective.com/${slug}`, + }; + + const existing = bySlug.get(slug) ?? { + ...profile, + slug, monthly: 0, allTime: 0, }; + // The canonical account's own profile wins over an alias's. + if (slug === account.slug) Object.assign(existing, profile); if (node.status === 'ACTIVE') { existing.monthly += monthlyAmount(node); } existing.allTime += node.totalDonations?.value ?? 0; - bySlug.set(account.slug, existing); + bySlug.set(slug, existing); } const sponsors = [];