diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 518bf08e89..2ad6e5d56e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -155,6 +155,7 @@ importers: src/api/dependency-discovery: specifiers: + '@octokit/request': 5.6.3 '@senecacdot/eslint-config-telescope': 1.1.0 '@senecacdot/satellite': ^1.27.0 env-cmd: 10.1.0 @@ -163,6 +164,7 @@ importers: query-registry: 2.2.0 supertest: 6.1.6 dependencies: + '@octokit/request': 5.6.3 '@senecacdot/satellite': 1.27.0 query-registry: 2.2.0 supertest: 6.1.6 diff --git a/src/api/dependency-discovery/package.json b/src/api/dependency-discovery/package.json index a1f2062200..c94be098b6 100644 --- a/src/api/dependency-discovery/package.json +++ b/src/api/dependency-discovery/package.json @@ -21,6 +21,7 @@ }, "homepage": "https://github.com/Seneca-CDOT/telescope#readme", "dependencies": { + "@octokit/request": "5.6.3", "@senecacdot/satellite": "^1.27.0", "query-registry": "2.2.0", "supertest": "6.1.6" diff --git a/src/api/dependency-discovery/src/dependency-list.js b/src/api/dependency-discovery/src/dependency-list.js index e3f8485ea9..e0250cbfe5 100644 --- a/src/api/dependency-discovery/src/dependency-list.js +++ b/src/api/dependency-discovery/src/dependency-list.js @@ -2,6 +2,7 @@ const { readFile } = require('fs/promises'); const { join } = require('path'); const { cwd } = require('process'); const { getPackument } = require('query-registry'); +const { requestGitHubInfo } = require('./github'); const getDependencies = (function () { let dependencies = null; @@ -51,8 +52,15 @@ async function getNpmPackageInfo(packageName) { return dependencies[packageName]; } +async function getGitHubIssues(packageName) { + const npmPackage = await getNpmPackageInfo(packageName); + + return await requestGitHubInfo(packageName, npmPackage.gitRepository.url); +} + module.exports = { getDependencyList, getNpmPackageInfo, isPackageDependency, + getGitHubIssues, }; diff --git a/src/api/dependency-discovery/src/github.js b/src/api/dependency-discovery/src/github.js new file mode 100644 index 0000000000..c2f8e82ca9 --- /dev/null +++ b/src/api/dependency-discovery/src/github.js @@ -0,0 +1,64 @@ +const { Redis } = require('@senecacdot/satellite'); +const { request } = require('@octokit/request'); + +const hasGitHubExpired = (dateTime) => Date.now() - dateTime > 60 * 60 * 1000; + +const gitHubInformationRequestor = {}; + +async function constructGitHubRequest(packageName, gitHubUrl) { + const labelsToSearchFor = ['hacktoberfest', 'good first issue', 'help wanted']; + + const [owner, repo] = new URL(gitHubUrl).pathname.substring(1).split('/'); + let issues = []; + + for (const label of labelsToSearchFor) { + const response = await request('GET /repos/{owner}/{repo}/issues{?assignee,state,labels}', { + owner, + repo, + assignee: 'none', + state: 'open', + labels: label, + }); + + if (response.status != 200) { + continue; + } + + issues = issues.concat( + response.data.map((issue) => { + return { + htmlUrl: issue.html_url, + title: issue.title, + body: issue.body, + createdAt: issue.created_at, + }; + }) + ); + } + + return issues; +} + +async function requestGitHubInfo(packageName, gitHubUrl) { + const request = gitHubInformationRequestor[packageName]; + + if (request && !hasGitHubExpired(request.expireAt)) { + return request.request; + } + + const newRequest = constructGitHubRequest(packageName, gitHubUrl); + + const now = new Date(Date.now()); + now.setHours(now.getHours() + 1); + + gitHubInformationRequestor[packageName] = { + expireAt: now, + request: newRequest, + }; + + return await newRequest; +} + +module.exports = { + requestGitHubInfo, +}; diff --git a/src/api/dependency-discovery/src/router.js b/src/api/dependency-discovery/src/router.js index 2b84133d08..b6d7d3b3f8 100644 --- a/src/api/dependency-discovery/src/router.js +++ b/src/api/dependency-discovery/src/router.js @@ -1,5 +1,10 @@ const { Router } = require('@senecacdot/satellite'); -const { getDependencyList, getNpmPackageInfo, isPackageDependency } = require('./dependency-list'); +const { + getDependencyList, + getNpmPackageInfo, + getGitHubIssues, + isPackageDependency, +} = require('./dependency-list'); const router = Router(); @@ -33,4 +38,21 @@ router.get('/projects/:namespace/:name?', async (req, res, next) => { } }); +router.get('/github/:namespace/:name?', async (req, res, next) => { + const { namespace, name } = req.params; + const packageName = name ? `${namespace}/${name}` : namespace; + + try { + if (!(await isPackageDependency(packageName))) { + res.status(404); + next(); + } else { + res.set('Cache-Control', 'max-age=3600'); + res.status(200).json(await getGitHubIssues(packageName)); + } + } catch (err) { + next(err); + } +}); + module.exports = router;