Skip to content
This repository has been archived by the owner on Jan 10, 2024. It is now read-only.

Commit

Permalink
Code refactor to prepare for supporting multiple providers
Browse files Browse the repository at this point in the history
  • Loading branch information
zillemarco committed Nov 8, 2023
1 parent bfb2562 commit 619a1ef
Show file tree
Hide file tree
Showing 11 changed files with 492 additions and 338 deletions.
7 changes: 7 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ DB_PASSWORD=rootpwd
DB_HOST=127.0.0.1
DB_DIALECT=mysql
PORT=8000
EMAIL_HOST=
EMAIL_PORT=
EMAIL_SECURE=
EMAIL_USERNAME=
EMAIL_PASSWORD=
GITHUB_APP_CLIENT_ID=
GITHUB_APP_CLIENT_SECRET=
20 changes: 5 additions & 15 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
const express = require("express");
require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` });
const bodyParser = require("body-parser");

const cors = require("cors");
const routes = require("./src/routes/routes.js");
const dbconnect = require("./database/helpers/dbconnect");
const express = require("express");
const bodyParser = require("body-parser");
const routes = require("./src/routes/index.js");

const app = express();
app.use(express.static("public"));
Expand All @@ -17,18 +18,7 @@ app.use(
})
);

// ROUTING
app.get("/api/login", routes.login);

if (process.env.NODE_ENV === "production") {
app.post("/api/callback", routes.productionCallback);
} else if (process.env.NODE_ENV === "development") {
app.get("/api/callback", routes.developmentCallback);
}

app.post("/api/repos-to-badge", routes.reposToBadge);

app.get("/api/badgedRepos", routes.badgedRepos);
routes.setupRoutes(app);

(async () => {
try {
Expand Down
39 changes: 0 additions & 39 deletions src/badges/badges.js

This file was deleted.

3 changes: 1 addition & 2 deletions src/badges/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
const badges = require("./badges");
const bronzeBadge = require("./bronzeBadge");
const silverBadge = require("./silverBadge");
const goldBadge = require("./goldBadge");
const platinumBadge = require("./platinumBadge");

module.exports = { badges, bronzeBadge, silverBadge, goldBadge, platinumBadge };
module.exports = { bronzeBadge, silverBadge, goldBadge, platinumBadge };
5 changes: 5 additions & 0 deletions src/helpers/augurAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ const axios = require("axios");
require("dotenv").config({ path: `.env.${process.env.NODE_ENV}` });

const augurAPI = async (id, level, url) => {
if (!process.env.AUGUR_API_KEY) {
console.error("AUGUR_API_KEY not provided");
return;
}

try {
const apiUrl = "https://projectbadge.chaoss.io/api/unstable/dei/repo/add";
const apiKey = process.env.AUGUR_API_KEY;
Expand Down
281 changes: 281 additions & 0 deletions src/helpers/github.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
const { Octokit } = require("@octokit/rest");
const axios = require("axios");
const Repo = require("../../database/models/repo.model.js");
const bronzeBadge = require("../badges/bronzeBadge.js");
const mailer = require("../helpers/mailer.js");

/**
* Starts the authorization process with the GitHub OAuth system
* @param {*} res Response to send back to the caller
*/
const authorizeApplication = (res) => {
if (!process.env.GITHUB_APP_CLIENT_ID) {
res.status(500).send("GitHub provider is not configured");
return;
}

const scopes = ["user", "repo"];
const url = `https://github.com/login/oauth/authorize?client_id=${
process.env.GITHUB_APP_CLIENT_ID
}&scope=${scopes.join(",")}`;

res.redirect(url);
};

/**
* Calls the GitHub API to get an access token from the OAuth code.
* @param {*} code Code returned by the GitHub OAuth authorization API
* @returns A json object with `access_token` and `errors`
*/
const requestAccessToken = async (code) => {
try {
const {
data: { access_token },
} = await axios.post(
"https://github.com/login/oauth/access_token",
{
client_id: process.env.GITHUB_APP_CLIENT_ID,
client_secret: process.env.GITHUB_APP_CLIENT_SECRET,
code,
},
{
headers: {
Accept: "application/json",
},
}
);

return {
access_token,
errors: [],
};
} catch (error) {
return {
access_token: "",
errors: [error.message],
};
}
};

/**
* Calls the GitHub API to get the user info.
* @param {*} octokit Octokit instance with autorization already set up
* @returns A json object with `user_info` and `errors`
*/
const getUserInfo = async (octokit) => {
try {
// Authenticated user details
const response = await octokit.users.getAuthenticated();
const {
data: { login, name, email, id },
} = response;

return {
user_info: {
login,
name,
email,
id,
},
errors: [],
};
} catch (error) {
return {
user_info: null,
errors: [error.message],
};
}
};

/**
* Calls the GitHub API to get the user public repositories.
* @param {*} octokit Octokit instance with autorization already set up
* @returns A json object with `repositories` and `errors`
*/
const getUserRepositories = async (octokit) => {
try {
// Public repos they maintain, administer, or own
let repos = [];
let page = 1;
let response = await octokit.repos.listForAuthenticatedUser({
visibility: "public",
per_page: 100,
page,
});

while (response.data.length > 0) {
repos = [...repos, ...response.data];
page++;
response = await octokit.repos.listForAuthenticatedUser({
visibility: "public",
per_page: 100,
page,
});
}

return {
repositories: repos.map((repo) => repo.full_name),
errors: [],
};
} catch (error) {
return {
repositories: null,
errors: [error.message],
};
}
};

/**
* Get the id and url of the provided repository path
* @param {*} octokit An Octokit instance
* @param {*} owner The (username) owner of the repository
* @param {*} repositoryPath The path to the repository, without the owner prefix
* @returns A json object with `info` (the repository infos) and `errors`
*/
const getRepositoryInfo = async (octokit, owner, repositoryPath) => {
try {
const {
data: { id, html_url },
} = await octokit.repos.get({ owner, repo: repositoryPath });

return {
info: {
id,
url: html_url,
},
errors: [],
};
} catch (error) {
return {
info: null,
errors: [error.message],
};
}
};

/**
* Get the content and commit SHA of a file inside a repository
* @param {*} octokit An Octokit instance
* @param {*} owner The (username) owner of the repository
* @param {*} repositoryPath The path to the repository, without the owner prefix
* @param {*} filePath The path to the file inside the repository
* @returns A json object with `file` (SHA and content) and `errors`
*/
const getFileContentAndSHA = async (
octokit,
owner,
repositoryPath,
filePath
) => {
try {
const {
data: { sha, content },
} = await octokit.repos.getContent({
owner,
repo: repositoryPath,
path: filePath,
});

return {
file: {
sha,
content: Buffer.from(content, "base64").toString(),
},
errors: [],
};
} catch (error) {
return {
file: null,
errors: [error.message],
};
}
};

/**
* Scans a list of repositories to try and apply for a badge
* @param {*} name Full name of the user
* @param {*} email User email used to send them emails with the results
* @param {*} repositories List of repositories to scan
*/
const scanRepositories = async (name, email, repositories) => {
const octokit = new Octokit();
let results = [];

try {
for (const repository of repositories) {
const owner = repository.split("/")[0];
const repositoryPath = repository.split("/")[1];

const { info, errors: info_errors } = await getRepositoryInfo(
octokit,
owner,
repositoryPath
);
if (info_errors.length > 0) {
console.error(info_errors);
continue;
}

const { file, errors: file_errors } = await getFileContentAndSHA(
octokit,
owner,
repositoryPath,
"DEI.md"
);
if (file_errors.length > 0) {
results.push(`${info.url} does not have a DEI.md file`);
continue;
}

try {
// Check if the repo was badged before
const existingRepo = await Repo.findOne({
where: { githubRepoId: info.id },
});

if (file.content) {
if (existingRepo) {
// Compare the DEICommitSHA with the existing repo's DEICommitSHA
if (existingRepo.DEICommitSHA !== file.sha) {
bronzeBadge(
name,
email,
info.id,
info.url,
file.content,
file.sha
);
} else {
// Handle case when DEI.md file is not changed
results.push(`${info.url} was already badged`);
}
} else {
// Repo not badged before, badge it
bronzeBadge(name, email, info.id, info.url, file.content, file.sha);
}
}
} catch (error) {
console.error(error.message);
}
}

// Send one single email for generic errors while processing repositories
// The `bronzeBadge` function will handle sending email for each project
// with wether success or error messages
if (results.length > 0) {
mailer(email, name, "Bronze", null, null, results.join("\n"));
}
} catch (error) {
console.error("Error: ", error.message);
}

return results;
};

module.exports = {
authorizeApplication,
requestAccessToken,
getUserInfo,
getUserRepositories,
scanRepositories,
};
Loading

0 comments on commit 619a1ef

Please sign in to comment.