quality-bot
Version:
QBot Online Code Review Application
245 lines (232 loc) • 8.47 kB
JavaScript
const simpleGit = require('simple-git');
const fs = require('fs')
const localStorage = require('localStorage');
const messageService = require('./message-service');
const documentService = require('./document-service');
const qbot = require('./qbot-service');
const Utils = require('./utils');
let commentDetails = [];
const git = simpleGit();
const parseData = async () => {
const nameCommand = 'config --get user.name'.split(' ');
let name = await git.raw(nameCommand);
const arg = process.argv;
let command = 'config --get user.qbot-token-local'.split(' ');
const coreHooksPath = await getExistingCoreHooksPath();
const localCoreHooksPath = await getExistingLocalCoreHooksPath();
const globalCoreHooksPath = await getExistingGlobalCoreHooksPath();
if ((coreHooksPath && localCoreHooksPath && globalCoreHooksPath)
|| (coreHooksPath && localCoreHooksPath && !globalCoreHooksPath)) {
command = 'config --get user.qbot-token-local'.split(' ');
}
else if (coreHooksPath && !localCoreHooksPath && globalCoreHooksPath) {
command = 'config --get user.qbot-token-global'.split(' ');
}
let token = await git.raw(command);
if (arg.length > 3 && arg[3] && arg[3].includes('--token')) {
let splittedArgument = arg[3].split("=");
token = splittedArgument[1];
}
return {
command: arg.length >= 3 ? arg[2] : '',
token: token,
username: name
}
}
const extractWords = (str) => {
const words = [];
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === '@' && str.charAt(i - 1) === '@') {
const stopIndex = str.indexOf('@@', i);
if (stopIndex !== -1)
words.push(str.substring(i + 1, stopIndex));
}
}
return words;
}
const extractName = (str) => {
const words = [];
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === '/' && str.charAt(i - 1) === 'a') {
const stopIndex = str.indexOf('b/', i);
if (stopIndex !== -1)
words.push(str.substring(i + 1, stopIndex));
}
}
return words;
}
const getChangedLineNumber = (data) => {
const finalData = {};
data.forEach(e => {
if (e) {
const fileName = extractName(e)[0] && extractName(e)[0].trim();
if (fileName) {
let list = extractWords(e);
list = list.filter((e, i) => i % 2 === 0);
if (list.length) {
finalData[fileName] = [];
list.map(e => {
let range = e.split('+')[1];
range = range.split(',');
for (let i = 0; i < (range[1] || 1); i++) {
finalData[fileName].push(+range[0] + i);
}
if ((e.includes('-') && e.includes('+')) || e.includes('+')) {
if (finalData[fileName].length === 0) {
finalData[fileName].push(null);
}
}
});
}
}
}
});
return finalData;
}
const review = async (data) => {
if (!data.token) {
messageService.invalidToken();
return;
}
const fileList = await Utils.getFileList();
if (!fileList.length) {
messageService.noFiles();
return;
}
const command = 'diff --unified=0 --staged'.split(' ');
const different = await git.raw(command);
const changedLine = getChangedLineNumber(different.split('diff --git '));
let filesToRemove = [];
let totalNull = [];
if (fileList.length) {
fileList.forEach(file => {
changedLine[file].forEach(lineNumbers => {
if (lineNumbers === null) {
totalNull.push(null);
}
})
if (totalNull.length == changedLine[file].length) {
filesToRemove.push(file);
}
});
}
const finalFileList = fileList.filter(function (x) {
return filesToRemove.indexOf(x) < 0;
});
let comments = 0, i = 0;
let repoDetails = null;
if (finalFileList.length === 0) {
messageService.success();
return [];
}
else {
await getAuthToken(data);
repoDetails = await getRepoDetails(data);
const reviewDetails = await reviewFiles(repoDetails, finalFileList, changedLine, data, comments, i);
return reviewDetails;
}
}
const getExistingCoreHooksPath = async () => {
const command = 'config --get core.hookspath'.split(' ');
const coreHooksPath = await git.raw(command);
return coreHooksPath ? coreHooksPath.trim() : '';
}
const getExistingLocalCoreHooksPath = async () => {
const command = 'config --get --local core.hookspath'.split(' ');
const coreHooksPath = await git.raw(command);
return coreHooksPath ? coreHooksPath.trim() : '';
}
const getExistingGlobalCoreHooksPath = async () => {
const command = 'config --get --global core.hookspath'.split(' ');
const coreHooksPath = await git.raw(command);
return coreHooksPath ? coreHooksPath.trim() : '';
}
const getRepoDetails = async (data) => {
let repoDetails = null;
try {
repoDetails = await qbot.repoDetails(data.token);
}
catch (err) {
messageService.serverError(
(err.response &&
err.response.data) &&
err.response.data.message || err.message);
process.exit(1);
}
return repoDetails;
}
const getAuthToken = async (data) => {
let jwtToken = null;
try {
jwtToken = await qbot.getAuthToken(data.token, data.username);
localStorage.setItem("bearerToken", jwtToken.data.accessToken);
}
catch (err) {
messageService.serverError(
(err.response &&
err.response.data) &&
err.response.data.message || err.message);
process.exit(1);
}
}
const reviewFiles = async (repoDetails, fileList, changedLine, data, comments, i) => {
const nameCommand = 'config --get user.name'.split(' ');
let name = await git.raw(nameCommand);
if (repoDetails.data && repoDetails.data.id && repoDetails.data.organizationId) {
await fileList.forEach(async (e) => {
let fileContent = '';
try {
fileContent = fs.readFileSync(e, 'utf8');
} catch (err) {
messageService.invalidLocation(err.message);
process.exit(1);
}
try {
const res = await qbot.review({
projectId: repoDetails.data.id,
organizationId: repoDetails.data.organizationId,
fileName: e,
fileContent: fileContent,
changedLineNumber: changedLine[e] || []
}, data.token);
if (res.data) {
comments = (res.data.length || 0) + comments;
if (res.data.length) {
res.data.forEach(data => {
commentDetails.push(data);
messageService.comments(data);
});
}
}
// Final Part
i++;
if (fileList.length === i) {
if (comments === 0) {
messageService.success();
} else {
documentService.createReportFile(commentDetails, name, fileList.length, repoDetails);
messageService.commentsInfo(comments, fileList);
}
}
}
catch (err) {
messageService.serverError(
(err.response &&
err.response.data) &&
err.response.data.message || err.message);
process.exit(1);
}
});
} else {
messageService.serverError("Repository is not configured. Please contact your project manager to configure the repository.");
process.exit(1);
}
return { comments, i };
}
module.exports = {
parseData,
review,
getExistingCoreHooksPath,
getExistingLocalCoreHooksPath,
getExistingGlobalCoreHooksPath,
}