From 19bb1b3766c408f3f40c6457a426e6870c5aabb2 Mon Sep 17 00:00:00 2001 From: Simon Lecoq <22963968+lowlighter@users.noreply.github.com> Date: Fri, 9 Apr 2021 20:51:45 +0200 Subject: [PATCH] Add notable contributions plugin (#222) --- .../github/graphql/notable.contributions.mjs | 28 +++++++++++ .../github/graphql/notable.organizations.mjs | 14 ++++++ source/app/web/statics/app.placeholder.js | 6 +++ source/plugins/base/metadata.yml | 2 +- source/plugins/base/queries/user.graphql | 2 +- source/plugins/notable/README.md | 21 ++++++++ source/plugins/notable/index.mjs | 48 +++++++++++++++++++ source/plugins/notable/metadata.yml | 13 +++++ .../notable/queries/contributions.graphql | 16 +++++++ .../notable/queries/organizations.graphql | 11 +++++ source/plugins/notable/tests.yml | 5 ++ source/plugins/skyline/metadata.yml | 2 +- source/templates/classic/partials/_.json | 1 + source/templates/classic/partials/notable.ejs | 27 +++++++++++ source/templates/classic/style.css | 26 +++++++++- 15 files changed, 218 insertions(+), 4 deletions(-) create mode 100644 source/app/mocks/api/github/graphql/notable.contributions.mjs create mode 100644 source/app/mocks/api/github/graphql/notable.organizations.mjs create mode 100644 source/plugins/notable/README.md create mode 100644 source/plugins/notable/index.mjs create mode 100644 source/plugins/notable/metadata.yml create mode 100644 source/plugins/notable/queries/contributions.graphql create mode 100644 source/plugins/notable/queries/organizations.graphql create mode 100644 source/plugins/notable/tests.yml create mode 100644 source/templates/classic/partials/notable.ejs diff --git a/source/app/mocks/api/github/graphql/notable.contributions.mjs b/source/app/mocks/api/github/graphql/notable.contributions.mjs new file mode 100644 index 00000000..a27e7639 --- /dev/null +++ b/source/app/mocks/api/github/graphql/notable.contributions.mjs @@ -0,0 +1,28 @@ +/**Mocked data */ + export default function({faker, query, login = faker.internet.userName()}) { + console.debug("metrics/compute/mocks > mocking graphql api result > notable/contributions") + return /after: "MOCKED_CURSOR"/m.test(query) ? ({ + user:{ + repositoriesContributedTo:{ + edges:[], + }, + }, + }) : ({ + user:{ + repositoriesContributedTo:{ + edges:[ + { + cursor:"MOCKED_CURSOR", + node:{ + isInOrganization:true, + owner:{ + login:faker.internet.userName(), + avatarUrl:null, + }, + }, + }, + ], + }, + }, + }) + } diff --git a/source/app/mocks/api/github/graphql/notable.organizations.mjs b/source/app/mocks/api/github/graphql/notable.organizations.mjs new file mode 100644 index 00000000..9d47a2a5 --- /dev/null +++ b/source/app/mocks/api/github/graphql/notable.organizations.mjs @@ -0,0 +1,14 @@ +/**Mocked data */ + export default function({faker, query, login = faker.internet.userName()}) { + console.debug("metrics/compute/mocks > mocking graphql api result > notable/organizations") + return ({ + user:{ + organizations:{ + nodes:[{ + login:faker.internet.userName(), + avatarUrl:null, + }], + }, + }, + }) + } diff --git a/source/app/web/statics/app.placeholder.js b/source/app/web/statics/app.placeholder.js index e5d9cf7e..bd5463b7 100644 --- a/source/app/web/statics/app.placeholder.js +++ b/source/app/web/statics/app.placeholder.js @@ -165,6 +165,12 @@ pr:{get count() { return this.open + this.merged }, open:faker.datatype.number(1000), merged:faker.datatype.number(1000)}, } }) : null), + //Notable + ...(set.plugins.enabled.notable ? ({ + notable:{ + contributions:new Array(2+faker.datatype.number(2)).fill(null).map(_ => ({name:faker.lorem.slug(), avatar:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg=="})), + } + }) : null), //Gists ...(set.plugins.enabled.gists ? ({ gists:{ diff --git a/source/plugins/base/metadata.yml b/source/plugins/base/metadata.yml index 4a0423ef..7cd29a95 100644 --- a/source/plugins/base/metadata.yml +++ b/source/plugins/base/metadata.yml @@ -1,5 +1,5 @@ name: "đŸ—ƒī¸ Base content" -cost: 1 GraphQL request +cost: 2 GraphQL requests + 1 GraphQL request per 100 repositories fetched categorie: core supports: - user diff --git a/source/plugins/base/queries/user.graphql b/source/plugins/base/queries/user.graphql index f49b1e3e..2f3f63c4 100644 --- a/source/plugins/base/queries/user.graphql +++ b/source/plugins/base/queries/user.graphql @@ -48,7 +48,7 @@ query BaseUser { } } } - repositoriesContributedTo { + repositoriesContributedTo(includeUserRepositories: true) { totalCount } followers { diff --git a/source/plugins/notable/README.md b/source/plugins/notable/README.md new file mode 100644 index 00000000..c67c3279 --- /dev/null +++ b/source/plugins/notable/README.md @@ -0,0 +1,21 @@ +### 🎩 Notable contributions + +The *notable* plugin displays badges of organization where you commited at least once on main branch. + + + +
+ + +
+ +#### â„šī¸ Examples workflows + +[âžĄī¸ Available options for this plugin](metadata.yml) + +```yaml +- uses: lowlighter/metrics@latest + with: + # ... other options + plugin_notable: yes +``` \ No newline at end of file diff --git a/source/plugins/notable/index.mjs b/source/plugins/notable/index.mjs new file mode 100644 index 00000000..43c06408 --- /dev/null +++ b/source/plugins/notable/index.mjs @@ -0,0 +1,48 @@ +//Setup + export default async function({login, q, imports, graphql, data, account, queries}, {enabled = false} = {}) { + //Plugin execution + try { + //Check if plugin is enabled and requirements are met + if ((!enabled)||(!q.notable)) + return null + + //Load inputs + imports.metadata.plugins.notable.inputs({data, account, q}) + + //Initialization + const organizations = new Map() + + //Load organization memberships + try { + const {user:{organizations:{nodes}}} = await graphql(queries.notable.organizations({login})) + nodes.map(({login, avatarUrl}) => organizations.set(login, avatarUrl)) + } + catch (error) { + console.debug(`metrics/compute/${login}/plugins > notable > failed to load organizations memberships: ${error}`) + } + + //Iterate through contributed repositories from organizations + { + let cursor = null + let pushed = 0 + do { + console.debug(`metrics/compute/${login}/plugins > notable > retrieving contributed repositories after ${cursor}`) + const {user:{repositoriesContributedTo:{edges}}} = await graphql(queries.notable.contributions({login, after:cursor ? `after: "${cursor}"` : "", repositories:100})) + cursor = edges?.[edges?.length-1]?.cursor + edges.map(({node}) => node.isInOrganization ? organizations.set(node.owner.login, node.owner.avatarUrl) : null) + pushed = edges.length + } while ((pushed)&&(cursor)) + } + + //Set contributions + const contributions = (await Promise.all([...organizations.entries()].map(async([name, avatarUrl]) => ({name, avatar:await imports.imgb64(avatarUrl)})))).sort((a, b) => a.name.localeCompare(b.name)) + console.debug(`metrics/compute/${login}/plugins > notable > found contributions to ${organizations.length} organizations`) + + //Results + return {contributions} + } + //Handle errors + catch (error) { + throw {error:{message:"An error occured", instance:error}} + } + } \ No newline at end of file diff --git a/source/plugins/notable/metadata.yml b/source/plugins/notable/metadata.yml new file mode 100644 index 00000000..dd901cc7 --- /dev/null +++ b/source/plugins/notable/metadata.yml @@ -0,0 +1,13 @@ +name: "🎩 Notable contributions" +cost: 1 GraphQL request per 100 repositories fetched +categorie: github +index: 18 +supports: + - user +inputs: + + # Enable or disable plugin + plugin_notable: + description: Display notable contributions in organizations + type: boolean + default: no \ No newline at end of file diff --git a/source/plugins/notable/queries/contributions.graphql b/source/plugins/notable/queries/contributions.graphql new file mode 100644 index 00000000..acc10c15 --- /dev/null +++ b/source/plugins/notable/queries/contributions.graphql @@ -0,0 +1,16 @@ +query NotableContributions { + user(login: "$login") { + repositoriesContributedTo($after first: $repositories, contributionTypes: COMMIT) { + edges { + cursor + node { + isInOrganization + owner { + login + avatarUrl + } + } + } + } + } +} \ No newline at end of file diff --git a/source/plugins/notable/queries/organizations.graphql b/source/plugins/notable/queries/organizations.graphql new file mode 100644 index 00000000..c1e5b3fa --- /dev/null +++ b/source/plugins/notable/queries/organizations.graphql @@ -0,0 +1,11 @@ +query NotableOrganizations { + user(login: "$login") { + organizations(first: 100) { + nodes { + login + avatarUrl + } + } + } +} + diff --git a/source/plugins/notable/tests.yml b/source/plugins/notable/tests.yml new file mode 100644 index 00000000..9605e381 --- /dev/null +++ b/source/plugins/notable/tests.yml @@ -0,0 +1,5 @@ +- name: Notable plugin (default) + uses: lowlighter/metrics@latest + with: + token: MOCKED_TOKEN + plugin_notable: yes \ No newline at end of file diff --git a/source/plugins/skyline/metadata.yml b/source/plugins/skyline/metadata.yml index bba5e336..f446fd99 100644 --- a/source/plugins/skyline/metadata.yml +++ b/source/plugins/skyline/metadata.yml @@ -1,7 +1,7 @@ name: "🌇 GitHub Skyline 3D calendar" cost: N/A categorie: github -index: 18 +index: 19 supports: - user inputs: diff --git a/source/templates/classic/partials/_.json b/source/templates/classic/partials/_.json index 98fdc4d0..ae648607 100644 --- a/source/templates/classic/partials/_.json +++ b/source/templates/classic/partials/_.json @@ -5,6 +5,7 @@ "base.repositories", "followup", "languages", + "notable", "projects", "gists", "pagespeed", diff --git a/source/templates/classic/partials/notable.ejs b/source/templates/classic/partials/notable.ejs new file mode 100644 index 00000000..0671aaa1 --- /dev/null +++ b/source/templates/classic/partials/notable.ejs @@ -0,0 +1,27 @@ +<% if (plugins.notable) { %> +
+

+ + Notable contributions +

+ <% if (plugins.notable.error) { %> +
+
+
+ + <%= plugins.notable.error.message %> +
+
+
+ <% } else { %> +
+ <% for (const {name, avatar} of plugins.notable.contributions) { %> +
+ + @<%= name %> +
+ <% } %> +
+ <% } %> +
+<% } %> diff --git a/source/templates/classic/style.css b/source/templates/classic/style.css index 32c88587..6a2c1a3a 100644 --- a/source/templates/classic/style.css +++ b/source/templates/classic/style.css @@ -92,10 +92,34 @@ margin: 0 6px; } - .avatar.organization { + .organization.avatar { border-radius: 15%; } + .organization.name { + white-space: nowrap; + } + + .organization.contributions { + margin: 0 8px; + flex-wrap: wrap; + } + + .contribution.organization { + display: flex; + border: 1px solid #959da5; + border-radius: 6px; + padding: 2px 6px; + padding-left: 0; + margin: 2px; + font-size: 12px; + background-color: #959da520; + } + + .contribution.organization .avatar { + margin: 0 4px; + } + /* Commit calendar */ .calendar.field { margin: 4px 0;