diff --git a/services/poeditor/poeditor.service.js b/services/poeditor/poeditor.service.js new file mode 100644 index 0000000000..e5bc71d794 --- /dev/null +++ b/services/poeditor/poeditor.service.js @@ -0,0 +1,113 @@ +'use strict' + +const Joi = require('@hapi/joi') +const { nonNegativeInteger } = require('../validators') +const { coveragePercentage } = require('../color-formatters') +const { BaseJsonService, InvalidResponse } = require('..') + +const documentation = ` +

+ You must specify the read-only API token from the POEditor account to which the project belongs. +

+

+ As per the POEditor API documentation, + all requests to the API must contain the parameter api_token. You can get a read-only key from your POEditor account. + You'll find it in My Account > API Access. +

+` + +const schema = Joi.object({ + response: Joi.object({ + code: nonNegativeInteger, + message: Joi.string().required(), + }).required(), + result: Joi.object({ + languages: Joi.array() + .items({ + name: Joi.string().required(), + code: Joi.string().required(), + percentage: Joi.number() + .min(0) + .max(100) + .required(), + }) + .required(), + }), +}).required() + +const queryParamSchema = Joi.object({ + token: Joi.string().required(), +}).required() + +module.exports = class POEditor extends BaseJsonService { + static get category() { + return 'other' + } + + static get route() { + return { + base: 'poeditor', + pattern: 'progress/:projectId/:languageCode', + queryParamSchema, + } + } + + static get examples() { + return [ + { + title: 'POEditor', + namedParams: { projectId: '323337', languageCode: 'fr' }, + queryParams: { token: 'abc123def456' }, + staticPreview: this.render({ + code: 200, + message: 'OK', + language: { percentage: 93, code: 'fr', name: 'French' }, + }), + keywords: ['l10n'], + documentation, + }, + ] + } + + static render({ code, message, language }) { + if (code !== 200) { + throw new InvalidResponse({ prettyMessage: message }) + } + + if (language === undefined) { + throw new InvalidResponse({ prettyMessage: 'Language not in project' }) + } + + return { + label: language.name, + message: `${language.percentage.toFixed(0)}%`, + color: coveragePercentage(language.percentage), + } + } + + async fetch({ projectId, token }) { + return this._requestJson({ + schema, + url: 'https://api.poeditor.com/v2/languages/list', + options: { + method: 'POST', + form: { + api_token: token, + id: projectId, + }, + }, + }) + } + + async handle({ projectId, languageCode }, { token }) { + const { + response: { code, message }, + result: { languages } = { languages: [] }, + } = await this.fetch({ projectId, token }) + return this.constructor.render({ + code, + message, + language: languages.find(lang => lang.code === languageCode), + }) + } +} diff --git a/services/poeditor/poeditor.tester.js b/services/poeditor/poeditor.tester.js new file mode 100644 index 0000000000..78db3b1657 --- /dev/null +++ b/services/poeditor/poeditor.tester.js @@ -0,0 +1,96 @@ +'use strict' + +const { isIntegerPercentage } = require('../test-validators') +const t = (module.exports = require('../tester').createServiceTester()) + +t.create('gets POEditor progress online') + .get('/progress/323337/de.json?token=7a666b44c0985d16a7b59748f488275c') + .expectBadge({ + label: 'German', + message: isIntegerPercentage, + }) + +t.create('gets POEditor progress online') + .get('/progress/1/zh.json?token=7a666b44c0985d16a7b59748f488275c') + .expectBadge({ + label: 'other', + message: "You don't have permission to access this resource", + }) + +// https:/.com/docs/api#languages_list_response +const apiResponse = { + response: { + status: 'success', + code: '200', + message: 'OK', + }, + result: { + languages: [ + { + name: 'English', + code: 'en', + translations: 13, + percentage: 12.5, + updated: '2015-05-04T14:21:41+0000', + }, + { + name: 'French', + code: 'fr', + translations: 70, + percentage: 68.75, + updated: '2015-04-30T08:59:34+0000', + }, + ], + }, +} + +t.create('gets mock POEditor progress') + .get('/progress/1234/fr.json?token=abc123def456') + .intercept(nock => + nock('https://api.poeditor.com') + .post('/v2/languages/list', { + id: '1234', + api_token: 'abc123def456', + }) + .reply(200, apiResponse) + ) + .expectBadge({ + label: 'French', + message: '69%', + }) + +t.create('handles requests for missing languages') + .get('/progress/1234/zh.json?token=abc123def456') + .intercept(nock => + nock('https://api.poeditor.com') + .post('/v2/languages/list', { + id: '1234', + api_token: 'abc123def456', + }) + .reply(200, apiResponse) + ) + .expectBadge({ + label: 'other', + message: 'Language not in project', + }) + +t.create('handles requests for wrong keys') + .get('/progress/1234/fr.json?token=abc123def456') + .intercept(nock => + nock('https://api.poeditor.com') + .post('/v2/languages/list', { + id: '1234', + api_token: 'abc123def456', + }) + .reply(200, { + response: { + status: 'fail', + code: '403', + message: "You don't have permission to access this resource", + }, + }) + ) + .expectBadge({ + label: 'other', + message: "You don't have permission to access this resource", + })