landscapeapp/tools/fetchBestPractices.js

190 lines
5.9 KiB
JavaScript

const colors = require('colors');
const Promise = require('bluebird');
const _ = require('lodash');
const path = require('path');
const debug = require('debug')('bestpractices');
const { errorsReporter } = require('./reporter');
const { requestWithRetry } = require('./requestWithRetry');
const { projectPath } = require('./settings');
const { makeReporter } = require('./progressReporter');
const { retry } = require('./retry');
const { addError } = errorsReporter('bestpractices');
const errorColor = colors.red;
const cacheMiss = colors.green;
async function getLandscapeItems() {
const source = require('js-yaml').load(require('fs').readFileSync(path.resolve(projectPath, 'landscape.yml')));
const traverse = require('traverse');
const tree = traverse(source);
const items = [];
tree.map(function(node) {
if (!node) {
return;
}
if (node.item !== null) {
return;
}
if (node.repo_url === null) {
return;
}
items.push({repo_url: node.url_for_bestpractices || node.repo_url});
});
return items;
}
async function fetchEntriesNoRetry() {
const maxNumber = 300;
const items = await Promise.map(_.range(1, maxNumber), async function(number) {
const result = await requestWithRetry({
url: `https://bestpractices.coreinfrastructure.org/en/projects.json?page=${number}`
});
return result.map(x => ({
id: x.id,
repo_url: x.repo_url,
percentage: x.badge_percentage_0
})).filter(x => !!x.repo_url);
}, {concurrency: 1});
return _.flatten(items);
}
async function fetchEntryNoRetry(url) {
let result = await requestWithRetry({
json: true,
url: `https://bestpractices.coreinfrastructure.org/en/projects.json?pq=${encodeURIComponent(url)}`
});
if (result[0]) {
result = result[0];
}
return {
id: result.id,
repo_url: result.repo_url,
percentage: result.badge_percentage_0
};
}
async function fetchEntries() {
return await retry(fetchEntriesNoRetry, 3);
}
async function fetchEntry(url) {
return await retry(() => fetchEntryNoRetry(url), 3);
}
module.exports.fetchBestPracticeEntriesWithFullScan = async function({cache, preferCache}) {
const items = await getLandscapeItems();
const errors = [];
var fetchedEntries = null;
const reporter = makeReporter();
const result = await Promise.mapSeries(items, async function(item) {
if (!item.repo_url) {
return null;
}
const cachedEntry = _.find(cache, (c) => c.repo_url.toLowerCase() === item.repo_url.toLowerCase());
if (cachedEntry && preferCache) {
debug(`Full scan: Cache found for ${item.repo_url}`);
reporter.write('.');
return cachedEntry;
}
debug(`Full scan: Cache not found for ${item.repo_url}`);
try {
fetchedEntries = fetchedEntries || await fetchEntries();
let repo_url = item.repo_url;
let badge = _.find(fetchedEntries, (x) => x.repo_url.toLowerCase() === item.repo_url.toLowerCase());
if (!badge && item.project_org) {
repo_url = item.project_org;
badge = _.find(fetchedEntries, (x) => x.repo_url.toLowerCase() === item.project_org.toLowerCase());
}
reporter.write(cacheMiss("*"));
return ({
repo_url: repo_url,
badge: badge ? badge.id : false,
percentage: badge ? badge.percentage : null
});
} catch (ex) {
debug(`Full scan: Fetch failed for ${item.repo_url}, attempt to use a cached entry`);
reporter.write(errorColor("E"));
errors.push(`Cannot fetch: ${item.repo_url} `, ex.message.substring(0, 200));
return cachedEntry || null;
}
});
reporter.summary();
_.each(errors, function(e) {
addError(e);
});
return result;
}
module.exports.fetchBestPracticeEntriesWithIndividualUrls = async function({cache, preferCache}) {
const items = await getLandscapeItems();
const errors = [];
const reporter = makeReporter();
const result = await Promise.mapSeries(items, async function(item) {
if (!item.repo_url) {
return null;
}
let cachedEntry = _.find(cache, (c) => c.repo_url.toLowerCase() === item.repo_url.toLowerCase());
if (!cachedEntry && item.project_org) {
cachedEntry = _.find(cache, (c) => c.repo_url.toLowerCase() === item.project_org.toLowerCase());
}
if (cachedEntry && preferCache) {
reporter.write('.');
return cachedEntry;
}
debug(`Individual scan: Cache not found for ${item.repo_url}`);
try {
let repo_url = item.repo_url;
let badge = await fetchEntry(item.repo_url);
if (!badge && item.project_org) {
repo_url = item.project_org;
badge = await fetchEntry(item.project_org);
}
reporter.write(cacheMiss("*"));
return ({
repo_url: repo_url,
badge: badge ? badge.id : false,
percentage: badge ? badge.percentage : null
});
} catch (ex) {
debug(`Individual scan: Fetch failed for ${item.repo_url}, attempt to use a cached entry`);
reporter.write(errorColor("E"));
errors.push(`Cannot fetch: ${item.repo_url} `, ex.message.substring(0, 200));
return cachedEntry || null;
}
});
reporter.summary();
_.each(errors, function(error) {
console.info('error: ', error);
});
return result;
}
module.exports.extractSavedBestPracticeEntries = async function extractSavedBestPracticeEntries() {
const traverse = require('traverse');
let source = [];
try {
source = require('js-yaml').load(require('fs').readFileSync(path.resolve(projectPath, 'processed_landscape.yml')));
} catch(_ex) {
console.info('Cannot extract image entries from the processed_landscape.yml');
}
var entries = [];
const tree = traverse(source);
tree.map(function(node) {
if (!node) {
return;
}
if (node.best_practice_data && node.repo_url) {
entries.push({...node.best_practice_data, repo_url: node.url_for_bestpractices || node.repo_url});
}
});
return _.uniq(entries);
}