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;