diff --git a/lib/all-badge-examples.js b/lib/all-badge-examples.js index ed423ac137e1b..aca5f5e478c67 100644 --- a/lib/all-badge-examples.js +++ b/lib/all-badge-examples.js @@ -1035,6 +1035,22 @@ const allBadgeExamples = [ title: 'Gratipay', previewUri: '/gratipay/project/shields.svg' }, + { + title: 'Liberapay receiving', + previewUri: '/liberapay/receives/Changaco.svg' + }, + { + title: 'Liberapay giving', + previewUri: '/liberapay/gives/Changaco.svg' + }, + { + title: 'Liberapay patrons', + previewUri: '/liberapay/patrons/Changaco.svg' + }, + { + title: 'Liberapay goal progress', + previewUri: '/liberapay/goal/Changaco.svg' + }, { title: 'Bountysource', previewUri: '/bountysource/team/mozilla-core/activity.svg' diff --git a/server.js b/server.js index 718513ff380d8..07febd6e22364 100644 --- a/server.js +++ b/server.js @@ -40,7 +40,8 @@ const { downloadCount: downloadCountColor, floorCount: floorCountColor, version: versionColor, - age: ageColor + age: ageColor, + colorScale } = require('./lib/color-formatters'); const { makeColorB, @@ -1072,6 +1073,75 @@ cache(function(data, match, sendBadge, request) { }); })); +// Liberapay integration. +camp.route(/^\/liberapay\/(receives|gives|patrons|goal)\/(.*)\.(svg|png|gif|jpg|json)$/, +cache(function(data, match, sendBadge, request) { + var type = match[1]; // e.g., 'gives' + var entity = match[2]; // e.g., 'Changaco' + var format = match[3]; + var apiUrl = 'https://liberapay.com/' + entity + '/public.json'; + // Lock down type + const label = { + 'receives': 'receives', + 'gives': 'gives', + 'patrons': 'patrons', + 'goal': 'goal progress', + }[type]; + const badgeData = getBadgeData(label, data); + if (badgeData.template === 'social') { + badgeData.logo = getLogo('liberapay', data); + } + request(apiUrl, function dealWithData(err, res, buffer) { + if (err != null) { + badgeData.text[1] = 'inaccessible'; + sendBadge(format, badgeData); + return; + } + try { + var data = JSON.parse(buffer); + var value; + var currency; + switch(type) { + case 'receives': + if (data.receiving) { + value = data.receiving.amount; + currency = data.receiving.currency; + badgeData.text[1] = `${metric(value)} ${currency}/week`; + } + break; + case 'gives': + if (data.giving) { + value = data.giving.amount; + currency = data.giving.currency; + badgeData.text[1] = `${metric(value)} ${currency}/week`; + } + break; + case 'patrons': + value = data.npatrons; + badgeData.text[1] = metric(value); + break; + case 'goal': + if (data.goal) { + value = Math.round(data.receiving.amount/data.goal.amount*100); + badgeData.text[1] = `${value}%`; + } + break; + } + if (value != null) { + badgeData.colorscheme = colorScale([0, 10, 100])(value); + sendBadge(format, badgeData); + } else { + badgeData.text[1] = 'anonymous'; + badgeData.colorscheme = 'blue'; + sendBadge(format, badgeData); + } + } catch(e) { + badgeData.text[1] = 'invalid'; + sendBadge(format, badgeData); + } + }); +})); + // Libscore integration. camp.route(/^\/libscore\/s\/(.*)\.(svg|png|gif|jpg|json)$/, cache(function(data, match, sendBadge, request) { diff --git a/service-tests/liberapay.js b/service-tests/liberapay.js new file mode 100644 index 0000000000000..9ba3a210ae2c4 --- /dev/null +++ b/service-tests/liberapay.js @@ -0,0 +1,53 @@ +'use strict'; + +const Joi = require('joi'); +const ServiceTester = require('./runner/service-tester'); +const isLiberapayTestValues = + Joi.string().regex(/^([0-9]*[1-9][0-9]*(\.[0-9]+)?|[0]+\.[0-9]*[1-9][0-9]*)[ A-Za-z]{4}\/week/); //values must be greater than zero +const {isMetric} = require('./helpers/validators'); +const t = new ServiceTester({ id: 'liberapay', title: 'Liberapay' }); +module.exports = t; + +t.create('Receiving') + .get('/receives/Liberapay.json') + .expectJSONTypes(Joi.object().keys({ + name: 'receives', + value: isLiberapayTestValues + })); + +t.create('Giving') + .get('/gives/Changaco.json') + .expectJSONTypes(Joi.object().keys({ + name: 'gives', + value: isLiberapayTestValues + })); + +t.create('Patrons') + .get('/patrons/Liberapay.json') + .expectJSONTypes(Joi.object().keys({ + name: 'patrons', + value: isMetric + })); + +t.create('Goal Progress') + .get('/goal/Liberapay.json') + .expectJSONTypes(Joi.object().keys({ + name: 'goal progress', + value: Joi.string().regex(/^[0-9]+%/) + })); + +t.create('No Goal') + .get('/goal/Liberapay.json') + .intercept(nock => nock('https://liberapay.com') + .get('/Liberapay/public.json') + .reply(200, { goal: null }) + ) + .expectJSON({ name: 'goal progress', value: 'anonymous'}); + +t.create('Empty') + .get('/receives/Liberapay.json') + .intercept(nock => nock('https://liberapay.com') + .get('/Liberapay/public.json') + .reply(200, { receiving: 0.00 }) + ) + .expectJSON({ name: 'receives', value: 'anonymous'});