diff --git a/source/plugins/sponsors/index.mjs b/source/plugins/sponsors/index.mjs index b3931f8b..e3b73b77 100644 --- a/source/plugins/sponsors/index.mjs +++ b/source/plugins/sponsors/index.mjs @@ -9,24 +9,59 @@ export default async function({login, q, imports, data, graphql, queries, accoun //Load inputs const {sections, past} = await imports.metadata.plugins.sponsors.inputs({data, account, q}) - //Query sponsors and goal + //Query description and goal console.debug(`metrics/compute/${login}/plugins > sponsors > querying sponsors and goal`) - const {[account]:{sponsorsListing:{fullDescription, activeGoal}, sponsorshipsAsMaintainer:{nodes, totalCount:count}}} = await graphql(queries.sponsors({login, account})) + const {[account]:{sponsorsListing:{fullDescription, activeGoal}}} = await graphql(queries.sponsors.description({login, account})) const about = await imports.markdown(fullDescription, {mode:"multiline"}) const goal = activeGoal ? {progress:activeGoal.percentComplete, title:activeGoal.title, description:await imports.markdown(activeGoal.description)} : null - let list = nodes.map(({sponsorEntity:{login, avatarUrl}, tier}) => ({login, avatarUrl, amount:tier?.monthlyPriceInDollars ?? null, past:false})) - await Promise.all(list.map(async user => user.avatar = await imports.imgb64(user.avatarUrl))) + const count = {active:{total:0, user:0, organization:0}, past:{total:0, user:0, organization:0}} + + //Query active sponsors + let list = [] + { + const fetched = [] + let cursor = null + let pushed = 0 + do { + console.debug(`metrics/compute/${login}/sponsors > retrieving sponsors after ${cursor}`) + const {[account]:{sponsorshipsAsMaintainer:{edges, nodes}}} = await graphql(queries.sponsors.active({login, account, after:cursor ? `after: "${cursor}"` : ""})) + cursor = edges?.[edges?.length - 1]?.cursor + fetched.push(...nodes) + pushed = nodes.length + console.debug(`metrics/compute/${login}/sponsors > retrieved ${pushed} sponsors after ${cursor}`) + } while ((pushed) && (cursor)) + list.push(...fetched.map(({sponsorEntity:{login, avatarUrl, url:organization = null}, tier}) => ({login, avatarUrl, type:organization ? "organization" : "user", amount:tier?.monthlyPriceInDollars ?? null, past:false}))) + await Promise.all(list.map(async user => user.avatar = await imports.imgb64(user.avatarUrl))) + count.active.total = list.length + count.active.user = list.filter(user => user.type === "user").length + count.active.organization = list.filter(user => user.type === "organization").length + } //Query past sponsors if (past) { console.debug(`metrics/compute/${login}/plugins > sponsors > querying past sponsors`) const active = new Set(list.map(({login}) => login)) - const {[account]:{sponsorsActivities:{nodes:events}}} = await graphql(queries.sponsors.all({login, account})) - const users = events.map(({sponsor:{login, avatarUrl}, sponsorsTier}) => ({login, avatarUrl, amount:sponsorsTier?.monthlyPriceInDollars ?? null, past:true})) + const users = [] + { + const fetched = [] + let cursor = null + let pushed = 0 + do { + console.debug(`metrics/compute/${login}/sponsors > retrieving sponsors events after ${cursor}`) + const {[account]:{sponsorsActivities:{edges, nodes}}} = await graphql(queries.sponsors.all({login, account, after:cursor ? `after: "${cursor}"` : ""})) + cursor = edges?.[edges?.length - 1]?.cursor + fetched.push(...nodes) + pushed = nodes.length + console.debug(`metrics/compute/${login}/sponsors > retrieved ${pushed} sponsors events after ${cursor}`) + } while ((pushed) && (cursor)) + users.push(...fetched.map(({sponsor:{login, avatarUrl, url:organization = null}, sponsorsTier}) => ({login, avatarUrl, type:organization ? "organization" : "user", amount:sponsorsTier?.monthlyPriceInDollars ?? null, past:true}))) + } for (const user of users) { if (!active.has(user.login)) { active.add(user.login) list.push({...user, avatar:await imports.imgb64(user.avatarUrl)}) + count.past.total++ + count.past[user.type]++ } } } diff --git a/source/plugins/sponsors/queries/sponsors.graphql b/source/plugins/sponsors/queries/active.graphql similarity index 53% rename from source/plugins/sponsors/queries/sponsors.graphql rename to source/plugins/sponsors/queries/active.graphql index 18eb1cc3..1ee4a171 100644 --- a/source/plugins/sponsors/queries/sponsors.graphql +++ b/source/plugins/sponsors/queries/active.graphql @@ -1,21 +1,20 @@ -query SponsorsDefault { +query SponsorsActive { $account(login: "$login") { - sponsorsListing { - fullDescription - activeGoal { - percentComplete - title - description + sponsorshipsAsMaintainer($after first: 100) { + edges { + cursor } - } - sponsorshipsAsMaintainer(first: 100) { - totalCount nodes { sponsorEntity { ... on User { login avatarUrl(size: 36) } + ... on Organization { + login + avatarUrl(size: 36) + url + } } tier { monthlyPriceInDollars @@ -23,4 +22,4 @@ query SponsorsDefault { } } } -} +} \ No newline at end of file diff --git a/source/plugins/sponsors/queries/all.graphql b/source/plugins/sponsors/queries/all.graphql index 7d4719eb..5fc6c756 100644 --- a/source/plugins/sponsors/queries/all.graphql +++ b/source/plugins/sponsors/queries/all.graphql @@ -1,12 +1,20 @@ query SponsorsAll { $account(login: "$login") { - sponsorsActivities(last: 100, period: ALL) { + sponsorsActivities($after first: 100, period: ALL) { + edges { + cursor + } nodes { sponsor { ... on User { avatarUrl login } + ... on Organization { + login + avatarUrl(size: 36) + url + } } sponsorsTier { monthlyPriceInDollars diff --git a/source/plugins/sponsors/queries/description.graphql b/source/plugins/sponsors/queries/description.graphql new file mode 100644 index 00000000..c52c05b8 --- /dev/null +++ b/source/plugins/sponsors/queries/description.graphql @@ -0,0 +1,12 @@ +query SponsorsDescription { + $account(login: "$login") { + sponsorsListing { + fullDescription + activeGoal { + percentComplete + title + description + } + } + } +} diff --git a/source/templates/classic/partials/sponsors.ejs b/source/templates/classic/partials/sponsors.ejs index 22f3d7e3..fbf495db 100644 --- a/source/templates/classic/partials/sponsors.ejs +++ b/source/templates/classic/partials/sponsors.ejs @@ -34,14 +34,14 @@ <% } %>
- <% if (plugins.sponsors.count) { %> - <%= plugins.sponsors.count %> sponsor<%= plugins.sponsors.count !== 1 ? "s are" : " is" %> funding <%= user.login %>'s work + <% if (plugins.sponsors.count.active.total) { %> + <%= plugins.sponsors.count.active.total %> sponsor<%= plugins.sponsors.count.active.total !== 1 ? "s are" : " is" %> funding <%= user.login %>'s work <% } %> <%= plugins.sponsors.goal.title %>
- <% for (const user of plugins.sponsors.list) { %>" src="<%= user.avatar %>" width="24" height="24" alt="" /><% } %> + <% for (const user of plugins.sponsors.list) { %> <%= user.past ? "past" : "" %>" src="<%= user.avatar %>" width="24" height="24" alt="" /><% } %>
diff --git a/tests/mocks/api/github/graphql/sponsors.default.mjs b/tests/mocks/api/github/graphql/sponsors.active.mjs similarity index 62% rename from tests/mocks/api/github/graphql/sponsors.default.mjs rename to tests/mocks/api/github/graphql/sponsors.active.mjs index b23a4165..2cfda5fa 100644 --- a/tests/mocks/api/github/graphql/sponsors.default.mjs +++ b/tests/mocks/api/github/graphql/sponsors.active.mjs @@ -1,18 +1,19 @@ /**Mocked data */ export default function({ faker, query, login = faker.internet.userName() }) { console.debug("metrics/compute/mocks > mocking graphql api result > sponsors/default") - return ({ + return /after: "MOCKED_CURSOR"/m.test(query) ? + ({ user: { - sponsorsListing: { - fullDescription: faker.lorem.sentences(), - activeGoal: { - percentComplete: faker.datatype.number(100), - title: faker.lorem.sentence(), - description: faker.lorem.sentence(), - }, - }, sponsorshipsAsMaintainer: { - totalCount: faker.datatype.number(100), + edges:[], + nodes:[], + } + } + }) +: ({ + user: { + sponsorshipsAsMaintainer: { + edges: new Array(10).fill("MOCKED_CURSOR"), nodes: new Array(10).fill(null).map(_ => ({ sponsorEntity: { login: faker.internet.userName(), diff --git a/tests/mocks/api/github/graphql/sponsors.all.mjs b/tests/mocks/api/github/graphql/sponsors.all.mjs index 5e814f98..b7808966 100644 --- a/tests/mocks/api/github/graphql/sponsors.all.mjs +++ b/tests/mocks/api/github/graphql/sponsors.all.mjs @@ -1,9 +1,19 @@ /**Mocked data */ export default function({ faker, query, login = faker.internet.userName() }) { console.debug("metrics/compute/mocks > mocking graphql api result > sponsors/all") - return ({ + return /after: "MOCKED_CURSOR"/m.test(query) ? + ({ + user: { + sponsorsActivities: { + edges:[], + nodes:[], + } + } + }) + : ({ user: { sponsorsActivities: { + edges: new Array(10).fill("MOCKED_CURSOR"), nodes: new Array(10).fill(null).map(_ => ({ sponsor: { login: faker.internet.userName(), diff --git a/tests/mocks/api/github/graphql/sponsors.description.mjs b/tests/mocks/api/github/graphql/sponsors.description.mjs new file mode 100644 index 00000000..ccfdc53e --- /dev/null +++ b/tests/mocks/api/github/graphql/sponsors.description.mjs @@ -0,0 +1,16 @@ +/**Mocked data */ +export default function({ faker, query, login = faker.internet.userName() }) { + console.debug("metrics/compute/mocks > mocking graphql api result > sponsors/default") + return ({ + user: { + sponsorsListing: { + fullDescription: faker.lorem.sentences(), + activeGoal: { + percentComplete: faker.datatype.number(100), + title: faker.lorem.sentence(), + description: faker.lorem.sentence(), + }, + }, + }, + }) +}