Skip to content
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

fix: fixed dashboard scripts #3304

Merged
merged 10 commits into from
Oct 23, 2024
8 changes: 4 additions & 4 deletions .github/workflows/regenerate-meetings-and-videos.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: List everyday latest list of AsyncAPI Meetings, Newsroom Videos and Dashboard data.

on:
on:
workflow_dispatch:
schedule:
#every day at midnight
Expand All @@ -23,7 +23,7 @@ jobs:
- name: Check package-lock version
uses: asyncapi/.github/.github/actions/get-node-version-from-package-lock@master
id: lockversion

- name: Use Node.js
uses: actions/setup-node@v3
with:
Expand All @@ -45,7 +45,7 @@ jobs:
committer: asyncapi-bot <[email protected]>
author: asyncapi-bot <[email protected]>
title: 'chore: update meetings.json and newsrooom_videos.json'
branch: update-meetings/${{ github.job }}
branch: update-meetings/${{ github.sha }}
- if: failure() # Only, on failure, send a message on the 94_bot-failing-ci slack channel
name: Report workflow run status to Slack
uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #using https://github.com/8398a7/action-slack/releases/tag/v3.15.1
Expand All @@ -54,4 +54,4 @@ jobs:
fields: repo,action,workflow
text: 'AsyncAPI Meetings and Videos workflow failed'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CI_FAIL_NOTIFY }}
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_CI_FAIL_NOTIFY }}
216 changes: 108 additions & 108 deletions scripts/dashboard/build-dashboard.js
Original file line number Diff line number Diff line change
@@ -1,108 +1,22 @@
const { writeFileSync } = require('fs');
const { resolve } = require('path');
const { graphql } = require('@octokit/graphql');
const { Promise } = require('node-fetch');
const { Queries } = require('./issue-queries');

async function getHotDiscussions(discussions) {
const result = await Promise.all(
discussions.map(async (discussion) => {
try {
const isPR = discussion.__typename === 'PullRequest';
if (discussion.comments.pageInfo.hasNextPage) {
let fetchedDiscussion = await getDiscussionByID(isPR, discussion.id);
discussion = fetchedDiscussion.node;
}

const interactionsCount =
discussion.reactions.totalCount +
discussion.comments.totalCount +
discussion.comments.nodes.reduce(
(acc, curr) => acc + curr.reactions.totalCount,
0
);

const finalInteractionsCount = isPR
? interactionsCount +
discussion.reviews.totalCount +
discussion.reviews.nodes.reduce(
(acc, curr) => acc + curr.comments.totalCount,
0
)
: interactionsCount;
return {
id: discussion.id,
isPR,
isAssigned: !!discussion.assignees.totalCount,
title: discussion.title,
author: discussion.author ? discussion.author.login : '',
resourcePath: discussion.resourcePath,
repo: 'asyncapi/' + discussion.repository.name,
labels: discussion.labels ? discussion.labels.nodes : [],
score:
finalInteractionsCount /
Math.pow(monthsSince(discussion.timelineItems.updatedAt) + 2, 1.8),
};
} catch (e) {
console.error(
`there was some issues while parsing this item: ${JSON.stringify(
discussion
)}`
);
throw e;
}
})
);
result.sort((ElemA, ElemB) => ElemB.score - ElemA.score);
const filteredResult = result.filter(issue => issue.author !== 'asyncapi-bot');
return filteredResult.slice(0, 12);
}
async function writeToFile(content) {
writeFileSync(
resolve(__dirname, '..', '..', 'dashboard.json'),
JSON.stringify(content, null, ' ')
);
}
async function mapGoodFirstIssues(issues) {
return issues.map((issue) => ({
id: issue.id,
title: issue.title,
isAssigned: !!issue.assignees.totalCount,
resourcePath: issue.resourcePath,
repo: 'asyncapi/' + issue.repository.name,
author: issue.author.login,
area: getLabel(issue, 'area/') || 'Unknown',
labels: issue.labels.nodes.filter(
(label) =>
!label.name.startsWith('area/') &&
!label.name.startsWith('good first issue')
),
}));
}

function getLabel(issue, filter) {
const result = issue.labels.nodes.find((label) =>
label.name.startsWith(filter)
);
return result && result.name.split('/')[1];
}


function monthsSince(date) {
const seconds = Math.floor((new Date() - new Date(date)) / 1000);
// 2592000 = number of seconds in a month = 30 * 24 * 60 * 60
const months = seconds / 2592000;
return Math.floor(months);
async function pause(ms) {
return new Promise((res) => {
setTimeout(res, ms);

Check warning on line 8 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L7-L8

Added lines #L7 - L8 were not covered by tests
});
}

async function getDiscussions(query, pageSize, endCursor = null) {
try {
let result = await graphql(query, {
const result = await graphql(query, {

Check warning on line 14 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L14

Added line #L14 was not covered by tests
first: pageSize,
after: endCursor,
headers: {
authorization: `token ${process.env.GITHUB_TOKEN}`,
},
authorization: `token ${process.env.GITHUB_TOKEN}`
}
});

if (result.rateLimit.remaining <= 100) {
Expand All @@ -112,55 +26,141 @@
`limit = ${result.rateLimit.limit}`,
`remaining = ${result.rateLimit.remaining}`,
`resetAt = ${result.rateLimit.resetAt}`
)
);
}

const hasNextPage = result.search.pageInfo.hasNextPage;
await pause(500);

Check warning on line 32 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L32

Added line #L32 was not covered by tests

const { hasNextPage } = result.search.pageInfo;

Check warning on line 34 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L34

Added line #L34 was not covered by tests

if (!hasNextPage) {
return result.search.nodes;
} else {
return result.search.nodes.concat(
await getDiscussions(query, pageSize, result.search.pageInfo.endCursor)
);
}
return result.search.nodes.concat(await getDiscussions(query, pageSize, result.search.pageInfo.endCursor));

Check warning on line 39 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L39

Added line #L39 was not covered by tests
} catch (e) {
console.error(e);

return Promise.reject(e);

Check warning on line 43 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L43

Added line #L43 was not covered by tests
Copy link

@coderabbitai coderabbitai bot Oct 20, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Simplify Error Handling by Throwing Errors Directly

In async functions, instead of returning Promise.reject(e);, it's cleaner to throw the error directly using throw e;. This makes the code more idiomatic and easier to read.

Apply this diff:

// In the getDiscussions function
-    return Promise.reject(e);
+    throw e;

// In the getDiscussionByID function
-    return Promise.reject(e);
+    throw e;

Also applies to: 59-59

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 43-43: scripts/dashboard/build-dashboard.js#L43
Added line #L43 was not covered by tests

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will improve this readiness of the code in another PR.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akshatnema, understood. Would you like me to open a new GitHub issue to track this improvement?

🐰😔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure 👍 . Please do it.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@akshatnema, I've opened a new GitHub issue to track this improvement: Issue #3305.

🐰😊

🔗 Analysis chain
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash

# Create a new GitHub issue to track the requested improvement.
gh issue create \
  --title "Simplify error handling in build-dashboard.js" \
  --body "As discussed in [PR #3304](https://github.com/asyncapi/website/pull/3304) ([comment](https://github.com/asyncapi/website/pull/3304#discussion_r1807819750)), @akshatnema agreed to improve the error handling by throwing errors directly instead of using \`Promise.reject(e);\` in the \`build-dashboard.js\` script.

We should update the error handling code to throw errors directly, making the asynchronous functions more idiomatic and easier to read." \
  --assignee akshatnema

Length of output: 597

}
}
async function getDiscussionByID(isPR, id) {
try {
let result = await graphql(isPR ? Queries.pullRequestById : Queries.issueById, {
const result = await graphql(isPR ? Queries.pullRequestById : Queries.issueById, {

Check warning on line 48 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L48

Added line #L48 was not covered by tests
id,
headers: {
authorization: `token ${process.env.GITHUB_TOKEN}`,
},
authorization: `token ${process.env.GITHUB_TOKEN}`
}
});

}
);
return result;
} catch (e) {
console.error(e);

return Promise.reject(e);

Check warning on line 59 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L59

Added line #L59 was not covered by tests
}
}

async function getHotDiscussions(discussions) {
const result = [];

Check warning on line 64 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L64

Added line #L64 was not covered by tests

for (let i = 0; i < discussions.length; i += 5) {
const batch = discussions.slice(i, i + 5);

Check warning on line 67 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L66-L67

Added lines #L66 - L67 were not covered by tests
// eslint-disable-next-line no-await-in-loop
const batchResults = await Promise.all(

Check warning on line 69 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L69

Added line #L69 was not covered by tests
batch.map(async (discussion) => {
try {

Check warning on line 71 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L71

Added line #L71 was not covered by tests
// eslint-disable-next-line no-underscore-dangle
const isPR = discussion.__typename === 'PullRequest';
if (discussion.comments.pageInfo.hasNextPage) {
const fetchedDiscussion = await getDiscussionByID(isPR, discussion.id);
discussion = fetchedDiscussion.node;

Check warning on line 76 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L73-L76

Added lines #L73 - L76 were not covered by tests
}

const interactionsCount =
discussion.reactions.totalCount +

Check warning on line 80 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L80

Added line #L80 was not covered by tests
discussion.comments.totalCount +
discussion.comments.nodes.reduce((acc, curr) => acc + curr.reactions.totalCount, 0);

Check warning on line 82 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L82

Added line #L82 was not covered by tests

const finalInteractionsCount = isPR

Check warning on line 84 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L84

Added line #L84 was not covered by tests
? interactionsCount +
discussion.reviews.totalCount +
discussion.reviews.nodes.reduce((acc, curr) => acc + curr.comments.totalCount, 0)

Check warning on line 87 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L87

Added line #L87 was not covered by tests
: interactionsCount;
return {

Check warning on line 89 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L89

Added line #L89 was not covered by tests
id: discussion.id,
isPR,
isAssigned: !!discussion.assignees.totalCount,
title: discussion.title,
author: discussion.author ? discussion.author.login : '',
resourcePath: discussion.resourcePath,
repo: `asyncapi/${discussion.repository.name}`,
labels: discussion.labels ? discussion.labels.nodes : [],
score: finalInteractionsCount / (monthsSince(discussion.timelineItems.updatedAt) + 2) ** 1.8
};
} catch (e) {
console.error(`there was some issues while parsing this item: ${JSON.stringify(discussion)}`);
throw e;

Check warning on line 102 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L101-L102

Added lines #L101 - L102 were not covered by tests
}
})
);

// eslint-disable-next-line no-await-in-loop
await pause(1000);

Check warning on line 108 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L108

Added line #L108 was not covered by tests

result.push(...batchResults);

Check warning on line 110 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L110

Added line #L110 was not covered by tests
}
result.sort((ElemA, ElemB) => ElemB.score - ElemA.score);
const filteredResult = result.filter((issue) => issue.author !== 'asyncapi-bot');
return filteredResult.slice(0, 12);

Check warning on line 114 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L112-L114

Added lines #L112 - L114 were not covered by tests
}
async function writeToFile(content) {
writeFileSync(resolve(__dirname, '..', '..', 'dashboard.json'), JSON.stringify(content, null, ' '));

Check warning on line 117 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L117

Added line #L117 was not covered by tests
}
async function mapGoodFirstIssues(issues) {
return issues.map((issue) => ({

Check warning on line 120 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L120

Added line #L120 was not covered by tests
id: issue.id,
title: issue.title,
isAssigned: !!issue.assignees.totalCount,
resourcePath: issue.resourcePath,
repo: `asyncapi/${issue.repository.name}`,
author: issue.author.login,
area: getLabel(issue, 'area/') || 'Unknown',
labels: issue.labels.nodes.filter(
(label) => !label.name.startsWith('area/') && !label.name.startsWith('good first issue')

Check warning on line 129 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L129

Added line #L129 was not covered by tests
)
}));
}

function getLabel(issue, filter) {
const result = issue.labels.nodes.find((label) => label.name.startsWith(filter));
return result?.name.split('/')[1];

Check warning on line 136 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L135-L136

Added lines #L135 - L136 were not covered by tests
}

function monthsSince(date) {
const seconds = Math.floor((new Date() - new Date(date)) / 1000);

Check warning on line 140 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L140

Added line #L140 was not covered by tests
// 2592000 = number of seconds in a month = 30 * 24 * 60 * 60
const months = seconds / 2592000;
return Math.floor(months);

Check warning on line 143 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L142-L143

Added lines #L142 - L143 were not covered by tests
}

async function start() {
try {
const [issues, PRs, rawGoodFirstIssues] = await Promise.all([
getDiscussions(Queries.hotDiscussionsIssues, 20),
getDiscussions(Queries.hotDiscussionsPullRequests, 20),
getDiscussions(Queries.goodFirstIssues, 20),
getDiscussions(Queries.goodFirstIssues, 20)
]);
const discussions = issues.concat(PRs);
const [hotDiscussions, goodFirstIssues] = await Promise.all([
getHotDiscussions(discussions),
mapGoodFirstIssues(rawGoodFirstIssues),
mapGoodFirstIssues(rawGoodFirstIssues)
]);
writeToFile({ hotDiscussions, goodFirstIssues });
} catch (e) {
console.log('There were some issues parsing data from github.')
console.log('There were some issues parsing data from github.');

Check warning on line 160 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L160

Added line #L160 was not covered by tests
console.log(e);
}
}
start();

module.exports = { getLabel, monthsSince, mapGoodFirstIssues, getHotDiscussions, getDiscussionByID }
module.exports = { getLabel, monthsSince, mapGoodFirstIssues, getHotDiscussions, getDiscussionByID };

Check warning on line 166 in scripts/dashboard/build-dashboard.js

View check run for this annotation

Codecov / codecov/patch

scripts/dashboard/build-dashboard.js#L166

Added line #L166 was not covered by tests
Loading