Refactor [Jetbrains] service; affects [eclipse-marketplace] (#2232)
* Refactor Jetbrains * Test that custom parser options are provided * Code refactoring * Code refactoring * url -> route * package-lock.json updated
This commit is contained in:
@@ -141,11 +141,6 @@ const allBadgeExamples = [
|
|||||||
previewUrl: '/vscode-marketplace/d/ritwickdey.LiveServer.svg',
|
previewUrl: '/vscode-marketplace/d/ritwickdey.LiveServer.svg',
|
||||||
keywords: ['vscode-marketplace'],
|
keywords: ['vscode-marketplace'],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'JetBrains IntelliJ plugins',
|
|
||||||
previewUrl: '/jetbrains/plugin/d/1347-scala.svg',
|
|
||||||
keywords: ['jetbrains', 'plugin'],
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'JetBrains ReSharper plugins',
|
title: 'JetBrains ReSharper plugins',
|
||||||
previewUrl: '/resharper/dt/ReSharper.Nuke.svg',
|
previewUrl: '/resharper/dt/ReSharper.Nuke.svg',
|
||||||
@@ -590,10 +585,6 @@ const allBadgeExamples = [
|
|||||||
keywords: ['vscode-marketplace'],
|
keywords: ['vscode-marketplace'],
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
|
||||||
title: 'JetBrains IntelliJ Plugins',
|
|
||||||
previewUrl: '/jetbrains/plugin/v/9630-a8translate.svg',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
title: 'JetBrains ReSharper Plugins',
|
title: 'JetBrains ReSharper Plugins',
|
||||||
previewUrl: '/resharper/v/ReSharper.Nuke.svg',
|
previewUrl: '/resharper/v/ReSharper.Nuke.svg',
|
||||||
|
|||||||
42
package-lock.json
generated
42
package-lock.json
generated
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "gh-badges",
|
"name": "gh-badges",
|
||||||
"version": "2.0.0-beta1",
|
"version": "2.0.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -1762,13 +1762,13 @@
|
|||||||
},
|
},
|
||||||
"babel-plugin-react-require": {
|
"babel-plugin-react-require": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-react-require/-/babel-plugin-react-require-3.0.0.tgz",
|
"resolved": "http://registry.npmjs.org/babel-plugin-react-require/-/babel-plugin-react-require-3.0.0.tgz",
|
||||||
"integrity": "sha1-Lk57RJa5OmVKHIAEInbeTk7rIOM=",
|
"integrity": "sha1-Lk57RJa5OmVKHIAEInbeTk7rIOM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"babel-plugin-syntax-jsx": {
|
"babel-plugin-syntax-jsx": {
|
||||||
"version": "6.18.0",
|
"version": "6.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
|
"resolved": "http://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
|
||||||
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
|
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@@ -2143,7 +2143,7 @@
|
|||||||
},
|
},
|
||||||
"browserify-rsa": {
|
"browserify-rsa": {
|
||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
"resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
|
||||||
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
|
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -2202,7 +2202,7 @@
|
|||||||
},
|
},
|
||||||
"buffer": {
|
"buffer": {
|
||||||
"version": "4.9.1",
|
"version": "4.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
"resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
|
||||||
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
"integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -3500,7 +3500,7 @@
|
|||||||
},
|
},
|
||||||
"d": {
|
"d": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz",
|
"resolved": "http://registry.npmjs.org/d/-/d-1.0.0.tgz",
|
||||||
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
|
"integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -4849,7 +4849,7 @@
|
|||||||
},
|
},
|
||||||
"events": {
|
"events": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
"resolved": "http://registry.npmjs.org/events/-/events-1.1.1.tgz",
|
||||||
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
|
"integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@@ -5184,7 +5184,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
|
"resolved": "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
|
||||||
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
|
"integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@@ -5475,7 +5475,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": {
|
"chalk": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -6606,7 +6606,7 @@
|
|||||||
},
|
},
|
||||||
"htmlescape": {
|
"htmlescape": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
|
"resolved": "http://registry.npmjs.org/htmlescape/-/htmlescape-1.1.1.tgz",
|
||||||
"integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=",
|
"integrity": "sha1-OgPtwiFLyjtmQko+eVk0lQnLA1E=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@@ -7683,7 +7683,7 @@
|
|||||||
},
|
},
|
||||||
"json5": {
|
"json5": {
|
||||||
"version": "0.5.1",
|
"version": "0.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
"resolved": "http://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
||||||
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
|
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@@ -9916,7 +9916,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pify": {
|
"pify": {
|
||||||
"version": "2.3.0",
|
"version": "2.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
"resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
|
||||||
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
@@ -10519,7 +10519,7 @@
|
|||||||
},
|
},
|
||||||
"convert-source-map": {
|
"convert-source-map": {
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": false,
|
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz",
|
||||||
"integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
|
"integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -10538,7 +10538,7 @@
|
|||||||
},
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
|
"resolved": false,
|
||||||
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
"integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -10659,7 +10659,7 @@
|
|||||||
},
|
},
|
||||||
"glob": {
|
"glob": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.3",
|
||||||
"resolved": false,
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
|
||||||
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
"integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -10796,7 +10796,7 @@
|
|||||||
},
|
},
|
||||||
"istanbul-lib-report": {
|
"istanbul-lib-report": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": false,
|
"resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.2.tgz",
|
||||||
"integrity": "sha512-rJ8uR3peeIrwAxoDEbK4dJ7cqqtxBisZKCuwkMtMv0xYzaAnsAi3AHrHPAAtNXzG/bcCgZZ3OJVqm1DTi9ap2Q==",
|
"integrity": "sha512-rJ8uR3peeIrwAxoDEbK4dJ7cqqtxBisZKCuwkMtMv0xYzaAnsAi3AHrHPAAtNXzG/bcCgZZ3OJVqm1DTi9ap2Q==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -10828,7 +10828,7 @@
|
|||||||
},
|
},
|
||||||
"istanbul-reports": {
|
"istanbul-reports": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": false,
|
"resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.0.1.tgz",
|
||||||
"integrity": "sha512-CT0QgMBJqs6NJLF678ZHcquUAZIoBIUNzdJrRJfpkI9OnzG6MkUfHxbJC3ln981dMswC7/B1mfX3LNkhgJxsuw==",
|
"integrity": "sha512-CT0QgMBJqs6NJLF678ZHcquUAZIoBIUNzdJrRJfpkI9OnzG6MkUfHxbJC3ln981dMswC7/B1mfX3LNkhgJxsuw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -11000,7 +11000,7 @@
|
|||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
"resolved": false,
|
||||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@@ -11242,7 +11242,7 @@
|
|||||||
},
|
},
|
||||||
"safe-buffer": {
|
"safe-buffer": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": false,
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
@@ -15541,7 +15541,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "3.10.0",
|
"version": "3.10.0",
|
||||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
|
"resolved": "http://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
|
||||||
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
|
"integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
@@ -15782,7 +15782,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": {
|
"chalk": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
"resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
|
|||||||
@@ -8,7 +8,13 @@ const trace = require('./trace')
|
|||||||
const { InvalidResponse } = require('./errors')
|
const { InvalidResponse } = require('./errors')
|
||||||
|
|
||||||
class BaseXmlService extends BaseService {
|
class BaseXmlService extends BaseService {
|
||||||
async _requestXml({ schema, url, options = {}, errorMessages = {} }) {
|
async _requestXml({
|
||||||
|
schema,
|
||||||
|
url,
|
||||||
|
options = {},
|
||||||
|
errorMessages = {},
|
||||||
|
parserOptions = {},
|
||||||
|
}) {
|
||||||
const logTrace = (...args) => trace.logTrace('fetch', ...args)
|
const logTrace = (...args) => trace.logTrace('fetch', ...args)
|
||||||
const mergedOptions = {
|
const mergedOptions = {
|
||||||
...{ headers: { Accept: 'application/xml, text/xml' } },
|
...{ headers: { Accept: 'application/xml, text/xml' } },
|
||||||
@@ -26,7 +32,7 @@ class BaseXmlService extends BaseService {
|
|||||||
underlyingError: validateResult.err,
|
underlyingError: validateResult.err,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
const xml = fastXmlParser.parse(buffer)
|
const xml = fastXmlParser.parse(buffer, parserOptions)
|
||||||
logTrace(emojic.dart, 'Response XML (before validation)', xml, {
|
logTrace(emojic.dart, 'Response XML (before validation)', xml, {
|
||||||
deep: true,
|
deep: true,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -101,6 +101,35 @@ describe('BaseXmlService', function() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('parses XML response with custom parser options', async function() {
|
||||||
|
const customParserOption = { trimValues: false }
|
||||||
|
class DummyXmlServiceWithParserOption extends DummyXmlService {
|
||||||
|
async handle() {
|
||||||
|
const { requiredString } = await this._requestXml({
|
||||||
|
schema: dummySchema,
|
||||||
|
url: 'http://example.com/foo.xml',
|
||||||
|
parserOptions: customParserOption,
|
||||||
|
})
|
||||||
|
return { message: requiredString }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const sendAndCacheRequest = async () => ({
|
||||||
|
buffer:
|
||||||
|
'<requiredString>some-string with trailing whitespace </requiredString>',
|
||||||
|
res: { statusCode: 200 },
|
||||||
|
})
|
||||||
|
const serviceInstance = new DummyXmlServiceWithParserOption(
|
||||||
|
{ sendAndCacheRequest },
|
||||||
|
{ handleInternalErrors: false }
|
||||||
|
)
|
||||||
|
|
||||||
|
const serviceData = await serviceInstance.invokeHandler({}, {})
|
||||||
|
|
||||||
|
expect(serviceData).to.deep.equal({
|
||||||
|
message: 'some-string with trailing whitespace ',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
it('handles xml responses which do not match the schema', async function() {
|
it('handles xml responses which do not match the schema', async function() {
|
||||||
const sendAndCacheRequest = async () => ({
|
const sendAndCacheRequest = async () => ({
|
||||||
buffer: '<unexpectedAttribute>some-string</unexpectedAttribute>',
|
buffer: '<unexpectedAttribute>some-string</unexpectedAttribute>',
|
||||||
|
|||||||
1
services/hackage/1.json
Normal file
1
services/hackage/1.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{ "plugin-repository": "" }
|
||||||
37
services/jetbrains/jetbrains-base.js
Normal file
37
services/jetbrains/jetbrains-base.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const BaseXmlService = require('../base-xml')
|
||||||
|
const { NotFound } = require('../errors')
|
||||||
|
|
||||||
|
module.exports = class JetbrainsBase extends BaseXmlService {
|
||||||
|
static buildUrl(base) {
|
||||||
|
return {
|
||||||
|
base,
|
||||||
|
format: '(.+)',
|
||||||
|
capture: ['pluginId'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static _validate(data, schema) {
|
||||||
|
if (data['plugin-repository'] === '') {
|
||||||
|
// Note the 'not found' response from JetBrains Plugins Repository is:
|
||||||
|
// status code = 200,
|
||||||
|
// body = <?xml version="1.0" encoding="UTF-8"?><plugin-repository></plugin-repository>
|
||||||
|
// which is parsed to object = { 'plugin-repository': '' }
|
||||||
|
throw new NotFound()
|
||||||
|
}
|
||||||
|
return super._validate(data, schema)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchPackageData({ pluginId, schema }) {
|
||||||
|
const parserOptions = {
|
||||||
|
parseNodeValue: false,
|
||||||
|
ignoreAttributes: false,
|
||||||
|
}
|
||||||
|
return this._requestXml({
|
||||||
|
schema,
|
||||||
|
url: `https://plugins.jetbrains.com/plugins/list?pluginId=${pluginId}`,
|
||||||
|
parserOptions,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
60
services/jetbrains/jetbrains-downloads.service.js
Normal file
60
services/jetbrains/jetbrains-downloads.service.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Joi = require('joi')
|
||||||
|
const JetbrainsBase = require('./jetbrains-base')
|
||||||
|
const { metric } = require('../../lib/text-formatters')
|
||||||
|
const {
|
||||||
|
downloadCount: downloadCountColor,
|
||||||
|
} = require('../../lib/color-formatters')
|
||||||
|
const { nonNegativeInteger } = require('../validators')
|
||||||
|
|
||||||
|
const schema = Joi.object({
|
||||||
|
'plugin-repository': Joi.object({
|
||||||
|
category: Joi.object({
|
||||||
|
'idea-plugin': Joi.array()
|
||||||
|
.min(1)
|
||||||
|
.items(
|
||||||
|
Joi.object({
|
||||||
|
'@_downloads': nonNegativeInteger,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.single()
|
||||||
|
.required(),
|
||||||
|
}),
|
||||||
|
}).required(),
|
||||||
|
}).required()
|
||||||
|
|
||||||
|
module.exports = class JetbrainsDownloads extends JetbrainsBase {
|
||||||
|
static get category() {
|
||||||
|
return 'downloads'
|
||||||
|
}
|
||||||
|
|
||||||
|
static get examples() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'JetBrains IntelliJ plugins',
|
||||||
|
exampleUrl: '1347-scala',
|
||||||
|
urlPattern: ':pluginId',
|
||||||
|
staticExample: this.render({ downloads: 10200000 }),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
static get route() {
|
||||||
|
return this.buildUrl('jetbrains/plugin/d')
|
||||||
|
}
|
||||||
|
|
||||||
|
static render({ downloads }) {
|
||||||
|
return {
|
||||||
|
message: `${metric(downloads)}`,
|
||||||
|
color: downloadCountColor(downloads),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle({ pluginId }) {
|
||||||
|
const pluginData = await this.fetchPackageData({ pluginId, schema })
|
||||||
|
const downloads =
|
||||||
|
pluginData['plugin-repository'].category['idea-plugin'][0]['@_downloads']
|
||||||
|
return this.constructor.render({ downloads })
|
||||||
|
}
|
||||||
|
}
|
||||||
57
services/jetbrains/jetbrains-version.service.js
Normal file
57
services/jetbrains/jetbrains-version.service.js
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const Joi = require('joi')
|
||||||
|
const JetbrainsBase = require('./jetbrains-base')
|
||||||
|
const { renderVersionBadge } = require('../../lib/version')
|
||||||
|
|
||||||
|
const schema = Joi.object({
|
||||||
|
'plugin-repository': Joi.object({
|
||||||
|
category: Joi.object({
|
||||||
|
'idea-plugin': Joi.array()
|
||||||
|
.min(1)
|
||||||
|
.items(
|
||||||
|
Joi.object({
|
||||||
|
version: Joi.string().required(),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.single()
|
||||||
|
.required(),
|
||||||
|
}),
|
||||||
|
}).required(),
|
||||||
|
}).required()
|
||||||
|
|
||||||
|
module.exports = class JetbrainsDownloads extends JetbrainsBase {
|
||||||
|
static get category() {
|
||||||
|
return 'version'
|
||||||
|
}
|
||||||
|
|
||||||
|
static get defaultBadgeData() {
|
||||||
|
return { label: 'jetbrains plugin' }
|
||||||
|
}
|
||||||
|
|
||||||
|
static get examples() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
title: 'JetBrains IntelliJ Plugins',
|
||||||
|
exampleUrl: '9630-a8translate',
|
||||||
|
urlPattern: ':pluginId',
|
||||||
|
staticExample: this.render({ version: 'v1.7' }),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
static get route() {
|
||||||
|
return this.buildUrl('jetbrains/plugin/v')
|
||||||
|
}
|
||||||
|
|
||||||
|
static render({ version }) {
|
||||||
|
return renderVersionBadge({ version })
|
||||||
|
}
|
||||||
|
|
||||||
|
async handle({ pluginId }) {
|
||||||
|
const pluginData = await this.fetchPackageData({ pluginId, schema })
|
||||||
|
const version =
|
||||||
|
pluginData['plugin-repository'].category['idea-plugin'][0]['version']
|
||||||
|
return this.constructor.render({ version })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
'use strict'
|
|
||||||
|
|
||||||
const xml2js = require('xml2js')
|
|
||||||
const LegacyService = require('../legacy-service')
|
|
||||||
const { makeBadgeData: getBadgeData } = require('../../lib/badge-data')
|
|
||||||
const { metric } = require('../../lib/text-formatters')
|
|
||||||
const {
|
|
||||||
downloadCount: downloadCountColor,
|
|
||||||
} = require('../../lib/color-formatters')
|
|
||||||
const { addv: versionText } = require('../../lib/text-formatters')
|
|
||||||
const { version: versionColor } = require('../../lib/color-formatters')
|
|
||||||
|
|
||||||
// JetBrains Plugins repository integration
|
|
||||||
module.exports = class JetBrains extends LegacyService {
|
|
||||||
static registerLegacyRouteHandler({ camp, cache }) {
|
|
||||||
camp.route(
|
|
||||||
/^\/jetbrains\/plugin\/(d|v)\/([^/]+)\.(svg|png|gif|jpg|json)$/,
|
|
||||||
cache((data, match, sendBadge, request) => {
|
|
||||||
const pluginId = match[2]
|
|
||||||
const type = match[1]
|
|
||||||
const format = match[3]
|
|
||||||
const leftText = type === 'v' ? 'jetbrains plugin' : 'downloads'
|
|
||||||
const badgeData = getBadgeData(leftText, data)
|
|
||||||
const url = `https://plugins.jetbrains.com/plugins/list?pluginId=${pluginId}`
|
|
||||||
|
|
||||||
request(url, (err, res, buffer) => {
|
|
||||||
if (err || res.statusCode !== 200) {
|
|
||||||
badgeData.text[1] = 'inaccessible'
|
|
||||||
return sendBadge(format, badgeData)
|
|
||||||
}
|
|
||||||
|
|
||||||
xml2js.parseString(buffer.toString(), (err, data) => {
|
|
||||||
if (err) {
|
|
||||||
badgeData.text[1] = 'invalid'
|
|
||||||
return sendBadge(format, badgeData)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const plugin = data['plugin-repository'].category
|
|
||||||
if (!plugin) {
|
|
||||||
badgeData.text[1] = 'not found'
|
|
||||||
return sendBadge(format, badgeData)
|
|
||||||
}
|
|
||||||
switch (type) {
|
|
||||||
case 'd': {
|
|
||||||
const downloads = parseInt(
|
|
||||||
data['plugin-repository'].category[0]['idea-plugin'][0]['$']
|
|
||||||
.downloads,
|
|
||||||
10
|
|
||||||
)
|
|
||||||
if (isNaN(downloads)) {
|
|
||||||
badgeData.text[1] = 'invalid'
|
|
||||||
return sendBadge(format, badgeData)
|
|
||||||
}
|
|
||||||
badgeData.text[1] = metric(downloads)
|
|
||||||
badgeData.colorscheme = downloadCountColor(downloads)
|
|
||||||
return sendBadge(format, badgeData)
|
|
||||||
}
|
|
||||||
case 'v': {
|
|
||||||
const version =
|
|
||||||
data['plugin-repository'].category[0]['idea-plugin'][0]
|
|
||||||
.version[0]
|
|
||||||
badgeData.text[1] = versionText(version)
|
|
||||||
badgeData.colorscheme = versionColor(version)
|
|
||||||
return sendBadge(format, badgeData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
badgeData.text[1] = 'invalid'
|
|
||||||
return sendBadge(format, badgeData)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,27 @@ t.create('downloads (user friendly plugin id)')
|
|||||||
.get('/plugin/d/1347-scala.json')
|
.get('/plugin/d/1347-scala.json')
|
||||||
.expectJSONTypes(Joi.object().keys({ name: 'downloads', value: isMetric }))
|
.expectJSONTypes(Joi.object().keys({ name: 'downloads', value: isMetric }))
|
||||||
|
|
||||||
|
t.create('downloads (mocked)')
|
||||||
|
.get('/plugin/d/9435.json')
|
||||||
|
.intercept(
|
||||||
|
nock =>
|
||||||
|
nock('https://plugins.jetbrains.com')
|
||||||
|
.get('/plugins/list?pluginId=9435')
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<plugin-repository>
|
||||||
|
<category name="Code editing">
|
||||||
|
<idea-plugin downloads="2" size="13159" date="1485601807000" url=""></idea-plugin>
|
||||||
|
</category>
|
||||||
|
</plugin-repository>`
|
||||||
|
),
|
||||||
|
{
|
||||||
|
'Content-Type': 'text/xml;charset=UTF-8',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.expectJSON({ name: 'downloads', value: '2' })
|
||||||
|
|
||||||
t.create('unknown plugin')
|
t.create('unknown plugin')
|
||||||
.get('/plugin/d/unknown-plugin.json')
|
.get('/plugin/d/unknown-plugin.json')
|
||||||
.expectJSON({ name: 'downloads', value: 'not found' })
|
.expectJSON({ name: 'downloads', value: 'not found' })
|
||||||
@@ -44,7 +65,7 @@ t.create('empty response')
|
|||||||
.get('/plugins/list?pluginId=7495')
|
.get('/plugins/list?pluginId=7495')
|
||||||
.reply(200, '')
|
.reply(200, '')
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'downloads', value: 'invalid' })
|
.expectJSON({ name: 'downloads', value: 'unparseable xml response' })
|
||||||
|
|
||||||
t.create('incorrect response format (JSON instead of XML)')
|
t.create('incorrect response format (JSON instead of XML)')
|
||||||
.get('/plugin/d/7495.json')
|
.get('/plugin/d/7495.json')
|
||||||
@@ -53,7 +74,7 @@ t.create('incorrect response format (JSON instead of XML)')
|
|||||||
.get('/plugins/list?pluginId=7495')
|
.get('/plugins/list?pluginId=7495')
|
||||||
.reply(200, { downloads: 2 })
|
.reply(200, { downloads: 2 })
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'downloads', value: 'invalid' })
|
.expectJSON({ name: 'downloads', value: 'unparseable xml response' })
|
||||||
|
|
||||||
t.create('missing required XML element')
|
t.create('missing required XML element')
|
||||||
.get('/plugin/d/9435.json')
|
.get('/plugin/d/9435.json')
|
||||||
@@ -75,7 +96,7 @@ t.create('missing required XML element')
|
|||||||
'Content-Type': 'text/xml;charset=UTF-8',
|
'Content-Type': 'text/xml;charset=UTF-8',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'downloads', value: 'invalid' })
|
.expectJSON({ name: 'downloads', value: 'invalid response data' })
|
||||||
|
|
||||||
t.create('missing required XML attribute')
|
t.create('missing required XML attribute')
|
||||||
.get('/plugin/d/9435.json')
|
.get('/plugin/d/9435.json')
|
||||||
@@ -108,7 +129,7 @@ t.create('missing required XML attribute')
|
|||||||
'Content-Type': 'text/xml;charset=UTF-8',
|
'Content-Type': 'text/xml;charset=UTF-8',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'downloads', value: 'invalid' })
|
.expectJSON({ name: 'downloads', value: 'invalid response data' })
|
||||||
|
|
||||||
t.create('empty XML')
|
t.create('empty XML')
|
||||||
.get('/plugin/d/9435.json')
|
.get('/plugin/d/9435.json')
|
||||||
@@ -121,7 +142,20 @@ t.create('empty XML')
|
|||||||
'Content-Type': 'text/xml;charset=UTF-8',
|
'Content-Type': 'text/xml;charset=UTF-8',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'downloads', value: 'invalid' })
|
.expectJSON({ name: 'downloads', value: 'unparseable xml response' })
|
||||||
|
|
||||||
|
t.create('XML with unknown root')
|
||||||
|
.get('/plugin/d/9435.json')
|
||||||
|
.intercept(
|
||||||
|
nock =>
|
||||||
|
nock('https://plugins.jetbrains.com')
|
||||||
|
.get('/plugins/list?pluginId=9435')
|
||||||
|
.reply(200, '<?xml version="1.0" encoding="UTF-8"?><plugin />'),
|
||||||
|
{
|
||||||
|
'Content-Type': 'text/xml;charset=UTF-8',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.expectJSON({ name: 'downloads', value: 'invalid response data' })
|
||||||
|
|
||||||
t.create('404 status code')
|
t.create('404 status code')
|
||||||
.get('/plugin/d/7495.json')
|
.get('/plugin/d/7495.json')
|
||||||
@@ -130,7 +164,7 @@ t.create('404 status code')
|
|||||||
.get('/plugins/list?pluginId=7495')
|
.get('/plugins/list?pluginId=7495')
|
||||||
.reply(404)
|
.reply(404)
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'downloads', value: 'inaccessible' })
|
.expectJSON({ name: 'downloads', value: 'not found' })
|
||||||
|
|
||||||
t.create('empty XML(v)')
|
t.create('empty XML(v)')
|
||||||
.get('/plugin/v/9435.json')
|
.get('/plugin/v/9435.json')
|
||||||
@@ -143,7 +177,7 @@ t.create('empty XML(v)')
|
|||||||
'Content-Type': 'text/xml;charset=UTF-8',
|
'Content-Type': 'text/xml;charset=UTF-8',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'jetbrains plugin', value: 'invalid' })
|
.expectJSON({ name: 'jetbrains plugin', value: 'unparseable xml response' })
|
||||||
|
|
||||||
t.create('404 status code(v)')
|
t.create('404 status code(v)')
|
||||||
.get('/plugin/v/7495.json')
|
.get('/plugin/v/7495.json')
|
||||||
@@ -152,7 +186,7 @@ t.create('404 status code(v)')
|
|||||||
.get('/plugins/list?pluginId=7495')
|
.get('/plugins/list?pluginId=7495')
|
||||||
.reply(404)
|
.reply(404)
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'jetbrains plugin', value: 'inaccessible' })
|
.expectJSON({ name: 'jetbrains plugin', value: 'not found' })
|
||||||
|
|
||||||
t.create('missing required XML element(v)')
|
t.create('missing required XML element(v)')
|
||||||
.get('/plugin/v/9435.json')
|
.get('/plugin/v/9435.json')
|
||||||
@@ -174,7 +208,7 @@ t.create('missing required XML element(v)')
|
|||||||
'Content-Type': 'text/xml;charset=UTF-8',
|
'Content-Type': 'text/xml;charset=UTF-8',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'jetbrains plugin', value: 'invalid' })
|
.expectJSON({ name: 'jetbrains plugin', value: 'invalid response data' })
|
||||||
|
|
||||||
t.create('incorrect response format (JSON instead of XML)(v)')
|
t.create('incorrect response format (JSON instead of XML)(v)')
|
||||||
.get('/plugin/v/7495.json')
|
.get('/plugin/v/7495.json')
|
||||||
@@ -183,7 +217,7 @@ t.create('incorrect response format (JSON instead of XML)(v)')
|
|||||||
.get('/plugins/list?pluginId=7495')
|
.get('/plugins/list?pluginId=7495')
|
||||||
.reply(200, { version: 2.0 })
|
.reply(200, { version: 2.0 })
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'jetbrains plugin', value: 'invalid' })
|
.expectJSON({ name: 'jetbrains plugin', value: 'unparseable xml response' })
|
||||||
|
|
||||||
t.create('empty response(v)')
|
t.create('empty response(v)')
|
||||||
.get('/plugin/v/7495.json')
|
.get('/plugin/v/7495.json')
|
||||||
@@ -192,7 +226,7 @@ t.create('empty response(v)')
|
|||||||
.get('/plugins/list?pluginId=7495')
|
.get('/plugins/list?pluginId=7495')
|
||||||
.reply(200, '')
|
.reply(200, '')
|
||||||
)
|
)
|
||||||
.expectJSON({ name: 'jetbrains plugin', value: 'invalid' })
|
.expectJSON({ name: 'jetbrains plugin', value: 'unparseable xml response' })
|
||||||
|
|
||||||
t.create('server error(v)')
|
t.create('server error(v)')
|
||||||
.get('/plugin/v/7495.json')
|
.get('/plugin/v/7495.json')
|
||||||
@@ -238,3 +272,39 @@ t.create('version (number as a plugin id)')
|
|||||||
value: isVPlusDottedVersionNClauses,
|
value: isVPlusDottedVersionNClauses,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
t.create('version (mocked)')
|
||||||
|
.get('/plugin/v/9435.json')
|
||||||
|
.intercept(
|
||||||
|
nock =>
|
||||||
|
nock('https://plugins.jetbrains.com')
|
||||||
|
.get('/plugins/list?pluginId=9435')
|
||||||
|
.reply(
|
||||||
|
200,
|
||||||
|
`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<plugin-repository>
|
||||||
|
<category name="Code editing">
|
||||||
|
<idea-plugin downloads="2" size="13159" date="1485601807000" url="">
|
||||||
|
<version>1.0</version>
|
||||||
|
</idea-plugin>
|
||||||
|
</category>
|
||||||
|
</plugin-repository>`
|
||||||
|
),
|
||||||
|
{
|
||||||
|
'Content-Type': 'text/xml;charset=UTF-8',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.expectJSON({ name: 'jetbrains plugin', value: 'v1.0' })
|
||||||
|
|
||||||
|
t.create('XML with unknown root (v)')
|
||||||
|
.get('/plugin/v/9435.json')
|
||||||
|
.intercept(
|
||||||
|
nock =>
|
||||||
|
nock('https://plugins.jetbrains.com')
|
||||||
|
.get('/plugins/list?pluginId=9435')
|
||||||
|
.reply(200, '<?xml version="1.0" encoding="UTF-8"?><plugin />'),
|
||||||
|
{
|
||||||
|
'Content-Type': 'text/xml;charset=UTF-8',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.expectJSON({ name: 'jetbrains plugin', value: 'invalid response data' })
|
||||||
|
|||||||
Reference in New Issue
Block a user