Skip to content

Commit

Permalink
Add grade badge for [lgtm] (#1681)
Browse files Browse the repository at this point in the history
* Add LGTM Grade badge

* Add new tests for LGTM Grade badge

* Add lgtm grade example to homepage
  • Loading branch information
s0 authored and chris48s committed May 12, 2018
1 parent b5635bf commit 4f8414c
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 9 deletions.
4 changes: 4 additions & 0 deletions lib/all-badge-examples.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,10 @@ const allBadgeExamples = [
title: 'LGTM Alerts',
previewUri: '/lgtm/alerts/g/apache/cloudstack.svg'
},
{
title: 'LGTM Grade',
previewUri: '/lgtm/grade/java/g/apache/cloudstack.svg'
},
{
title: 'HHVM',
previewUri: '/hhvm/symfony/symfony.svg'
Expand Down
61 changes: 61 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -1098,6 +1098,67 @@ cache(function(data, match, sendBadge, request) {
});
}));

// LGTM grades integration
camp.route(/^\/lgtm\/grade\/([^/]+)\/(.+)\.(svg|png|gif|jpg|json)$/,
cache(function(data, match, sendBadge, request) {
const language = match[1]; // eg, `java`
const projectId = match[2]; // eg, `g/apache/cloudstack`
const format = match[3];
const url = 'https://lgtm.com/api/v0.1/project/' + projectId + '/details';
const languageLabel = (() => {
switch(language) {
case 'cpp':
return 'c/c++';
case 'csharp':
return 'c#';
// Javascript analysis on LGTM also includes TypeScript
case 'javascript':
return 'js/ts';
default:
return language;
}
})();
const badgeData = getBadgeData('code quality: ' + languageLabel, data);
request(url, function(err, res, buffer) {
if (checkErrorResponse(badgeData, err, res, 'project not found')) {
sendBadge(format, badgeData);
return;
}
try {
const data = JSON.parse(buffer);
if (!('languages' in data))
throw new Error("Invalid data");
for (const languageData of data.languages) {
if (languageData.lang === language && 'grade' in languageData) {
// Pretty label for the language
badgeData.text[1] = languageData.grade;
// Pick colour based on grade
if (languageData.grade === 'A+') {
badgeData.colorscheme = 'brightgreen';
} else if (languageData.grade === 'A') {
badgeData.colorscheme = 'green';
} else if (languageData.grade === 'B') {
badgeData.colorscheme = 'yellowgreen';
} else if (languageData.grade === 'C') {
badgeData.colorscheme = 'yellow';
} else if (languageData.grade === 'D') {
badgeData.colorscheme = 'orange';
} else {
badgeData.colorscheme = 'red';
}
sendBadge(format, badgeData);
return;
}
}
badgeData.text[1] = 'no data for language';
sendBadge(format, badgeData);
} catch(e) {
badgeData.text[1] = 'invalid';
sendBadge(format, badgeData);
}
});
}));

// Gratipay integration.
camp.route(/^\/(?:gittip|gratipay(\/user|\/team|\/project)?)\/(.*)\.(svg|png|gif|jpg|json)$/,
cache(function(queryParams, match, sendBadge, request) {
Expand Down
123 changes: 114 additions & 9 deletions services/lgtm/lgtm.tester.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,56 +6,161 @@ const ServiceTester = require('../service-tester');
const t = new ServiceTester({ id: 'lgtm', title: 'LGTM' })
module.exports = t;

t.create('total alerts for a project')
// Alerts Badge

t.create('alerts: total alerts for a project')
.get('/alerts/g/apache/cloudstack.json')
.expectJSONTypes(Joi.object().keys({
name: 'lgtm',
value: Joi.string().regex(/^[0-9kM.]+ alerts?$/)
}));

t.create('missing project')
t.create('alerts: missing project')
.get('/alerts/g/some-org/this-project-doesnt-exist.json')
.expectJSON({
name: 'lgtm',
value: 'project not found'
});

t.create('no alerts')
t.create('alerts: no alerts')
.get('/alerts/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, {alerts: 0}))
.expectJSON({ name: 'lgtm', value: '0 alerts' });

t.create('single alert')
t.create('alerts: single alert')
.get('/alerts/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, {alerts: 1}))
.expectJSON({ name: 'lgtm', value: '1 alert' });

t.create('multiple alerts')
t.create('alerts: multiple alerts')
.get('/alerts/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, {alerts: 123}))
.expectJSON({ name: 'lgtm', value: '123 alerts' });

t.create('json missing alerts')
t.create('alerts: json missing alerts')
.get('/alerts/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, {}))
.expectJSON({ name: 'lgtm', value: 'invalid' });

t.create('invalid json')
t.create('alerts: invalid json')
.get('/alerts/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, 'not a json string'))
.expectJSON({ name: 'lgtm', value: 'invalid' });

t.create('lgtm inaccessible')
t.create('alerts: lgtm inaccessible')
.get('/alerts/g/apache/cloudstack.json')
.networkOff()
.expectJSON({ name: 'lgtm', value: 'inaccessible' });
.expectJSON({ name: 'lgtm', value: 'inaccessible' });

// Grade Badge

t.create('grade: missing project')
.get('/grade/java/g/some-org/this-project-doesnt-exist.json')
.expectJSON({
name: 'code quality: java',
value: 'project not found'
});

t.create('grade: lgtm inaccessible')
.get('/grade/java/g/apache/cloudstack.json')
.networkOff()
.expectJSON({ name: 'code quality: java', value: 'inaccessible' });

t.create('grade: invalid json')
.get('/grade/java/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, 'not a json string'))
.expectJSON({ name: 'code quality: java', value: 'invalid' });

t.create('grade: json missing languages')
.get('/grade/java/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, {}))
.expectJSON({ name: 'code quality: java', value: 'invalid' });

t.create('grade: grade for a project (java)')
.get('/grade/java/g/apache/cloudstack.json')
.expectJSONTypes(Joi.object().keys({
name: 'code quality: java',
value: Joi.string().regex(/^(?:A\+)|A|B|C|D|E$/)
}));

t.create('grade: grade for missing language')
.get('/grade/foo/g/apache/cloudstack.json')
.expectJSON({
name: 'code quality: foo',
value: 'no data for language'
});

// Test display of languages

const data = {languages: [
{lang: 'cpp', grade: 'A+'},
{lang: 'javascript', grade: 'A'},
{lang: 'java', grade: 'B'},
{lang: 'python', grade: 'C'},
{lang: 'csharp', grade: 'D'},
{lang: 'other', grade: 'E'},
{lang: 'foo'}
]}

t.create('grade: cpp')
.get('/grade/cpp/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data))
.expectJSON({ name: 'code quality: c/c++', value: 'A+' });

t.create('grade: javascript')
.get('/grade/javascript/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data))
.expectJSON({ name: 'code quality: js/ts', value: 'A' });

t.create('grade: java')
.get('/grade/java/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data))
.expectJSON({ name: 'code quality: java', value: 'B' });

t.create('grade: python')
.get('/grade/python/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data))
.expectJSON({ name: 'code quality: python', value: 'C' });

t.create('grade: csharp')
.get('/grade/csharp/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data))
.expectJSON({ name: 'code quality: c#', value: 'D' });

t.create('grade: other')
.get('/grade/other/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data))
.expectJSON({ name: 'code quality: other', value: 'E' });

t.create('grade: foo (no grade for valid language)')
.get('/grade/foo/g/apache/cloudstack.json')
.intercept(nock => nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data))
.expectJSON({ name: 'code quality: foo', value: 'no data for language' });

0 comments on commit 4f8414c

Please sign in to comment.