From e3c8508ce4df2f8153969ead0fee6b70497eb5ec Mon Sep 17 00:00:00 2001
From: James Cahill <jcahill773@gmail.com>
Date: Sat, 19 Jan 2019 17:24:58 +0000
Subject: [PATCH] Added various [Spiget] badges (#2745)

* Spiget Downloads badge

* Spiget Download Size badge

* Spiget Latest version badge

* Add a base class

* Spiget Rating / Stars badges

* Spiget versions badge

* remove useless regex escape

* use renderVersionBadge for rendering

* misc fixes

* use expectJSON when possible

* use the download count color formatter

* merge service classes

* add keywords

* Add tests for non-star ratings

* Add nock tests

* misc fixes

* chore: minor formatting update on spiget dl
---
 services/spiget/spiget-base.js                | 46 ++++++++++++
 .../spiget/spiget-download-size.service.js    | 47 ++++++++++++
 .../spiget/spiget-download-size.tester.js     | 17 +++++
 services/spiget/spiget-downloads.service.js   | 50 +++++++++++++
 services/spiget/spiget-downloads.tester.js    | 22 ++++++
 .../spiget/spiget-latest-version.service.js   | 53 ++++++++++++++
 .../spiget/spiget-latest-version.tester.js    | 24 +++++++
 services/spiget/spiget-rating.service.js      | 71 +++++++++++++++++++
 services/spiget/spiget-rating.tester.js       | 39 ++++++++++
 .../spiget/spiget-tested-versions.service.js  | 57 +++++++++++++++
 .../spiget/spiget-tested-versions.tester.js   | 71 +++++++++++++++++++
 11 files changed, 497 insertions(+)
 create mode 100644 services/spiget/spiget-base.js
 create mode 100644 services/spiget/spiget-download-size.service.js
 create mode 100644 services/spiget/spiget-download-size.tester.js
 create mode 100644 services/spiget/spiget-downloads.service.js
 create mode 100644 services/spiget/spiget-downloads.tester.js
 create mode 100644 services/spiget/spiget-latest-version.service.js
 create mode 100644 services/spiget/spiget-latest-version.tester.js
 create mode 100644 services/spiget/spiget-rating.service.js
 create mode 100644 services/spiget/spiget-rating.tester.js
 create mode 100644 services/spiget/spiget-tested-versions.service.js
 create mode 100644 services/spiget/spiget-tested-versions.tester.js

diff --git a/services/spiget/spiget-base.js b/services/spiget/spiget-base.js
new file mode 100644
index 0000000000..be2885939a
--- /dev/null
+++ b/services/spiget/spiget-base.js
@@ -0,0 +1,46 @@
+'use strict'
+
+const Joi = require('joi')
+const BaseJsonService = require('../base-json')
+
+const resourceSchema = Joi.object({
+  downloads: Joi.number().required(),
+  file: Joi.object({
+    size: Joi.number().required(),
+    sizeUnit: Joi.string().required(),
+  }).required(),
+  testedVersions: Joi.array(),
+  rating: Joi.object({
+    count: Joi.number().required(),
+    average: Joi.number().required(),
+  }).required(),
+}).required()
+
+const documentation = `
+<p>You can find your resource ID in the url for your resource page.</p>
+<p>Example: <code>https://www.spigotmc.org/resources/essentialsx.9089/</code> - Here the Resource ID is 9089.</p>`
+
+const keywords = ['spigot', 'spigotmc']
+
+class BaseSpigetService extends BaseJsonService {
+  async fetch({
+    resourceid,
+    schema = resourceSchema,
+    url = `https://api.spiget.org/v2/resources/${resourceid}`,
+  }) {
+    return this._requestJson({
+      schema,
+      url,
+    })
+  }
+
+  static get defaultBadgeData() {
+    return { label: 'spiget' }
+  }
+
+  static get category() {
+    return 'other'
+  }
+}
+
+module.exports = { keywords, documentation, BaseSpigetService }
diff --git a/services/spiget/spiget-download-size.service.js b/services/spiget/spiget-download-size.service.js
new file mode 100644
index 0000000000..91fb611e6b
--- /dev/null
+++ b/services/spiget/spiget-download-size.service.js
@@ -0,0 +1,47 @@
+'use strict'
+
+const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+
+module.exports = class SpigetDownloadSize extends BaseSpigetService {
+  static get route() {
+    return {
+      base: 'spiget/download-size',
+      pattern: ':resourceid',
+    }
+  }
+
+  static get defaultBadgeData() {
+    return {
+      label: 'size',
+      color: 'blue',
+    }
+  }
+
+  async handle({ resourceid }) {
+    const { file } = await this.fetch({ resourceid })
+    return this.constructor.render({ size: file.size, unit: file.sizeUnit })
+  }
+
+  static render({ size, unit }) {
+    return {
+      message: `${size} ${unit}`,
+    }
+  }
+
+  static get category() {
+    return 'size'
+  }
+  static get examples() {
+    return [
+      {
+        title: 'Spiget Download Size',
+        namedParams: {
+          resourceid: '9089',
+        },
+        staticPreview: this.render({ size: 2.5, unit: 'MB' }),
+        documentation,
+        keywords,
+      },
+    ]
+  }
+}
diff --git a/services/spiget/spiget-download-size.tester.js b/services/spiget/spiget-download-size.tester.js
new file mode 100644
index 0000000000..d40c256f7d
--- /dev/null
+++ b/services/spiget/spiget-download-size.tester.js
@@ -0,0 +1,17 @@
+'use strict'
+
+const Joi = require('joi')
+const { isFileSize } = require('../test-validators')
+
+const t = (module.exports = require('../create-service-tester')())
+
+t.create('EssentialsX (id 9089)')
+  .get('/9089.json')
+  .expectJSONTypes(Joi.object().keys({ name: 'size', value: isFileSize }))
+
+t.create('Invalid Resource (id 1)')
+  .get('/1.json')
+  .expectJSON({
+    name: 'size',
+    value: 'not found',
+  })
diff --git a/services/spiget/spiget-downloads.service.js b/services/spiget/spiget-downloads.service.js
new file mode 100644
index 0000000000..31580e6661
--- /dev/null
+++ b/services/spiget/spiget-downloads.service.js
@@ -0,0 +1,50 @@
+'use strict'
+
+const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+const { metric } = require('../../lib/text-formatters')
+const { downloadCount } = require('../../lib/color-formatters')
+
+module.exports = class SpigetDownloads extends BaseSpigetService {
+  static get route() {
+    return {
+      base: 'spiget/downloads',
+      pattern: ':resourceid',
+    }
+  }
+
+  static get defaultBadgeData() {
+    return {
+      label: 'downloads',
+    }
+  }
+
+  async handle({ resourceid }) {
+    const { downloads } = await this.fetch({ resourceid })
+    return this.constructor.render({ downloads })
+  }
+
+  static render({ downloads }) {
+    return {
+      message: metric(downloads),
+      color: downloadCount(downloads),
+    }
+  }
+
+  static get category() {
+    return 'downloads'
+  }
+
+  static get examples() {
+    return [
+      {
+        title: 'Spiget Downloads',
+        namedParams: {
+          resourceid: '9089',
+        },
+        staticPreview: this.render({ downloads: 560891 }),
+        documentation,
+        keywords,
+      },
+    ]
+  }
+}
diff --git a/services/spiget/spiget-downloads.tester.js b/services/spiget/spiget-downloads.tester.js
new file mode 100644
index 0000000000..761da8c7e7
--- /dev/null
+++ b/services/spiget/spiget-downloads.tester.js
@@ -0,0 +1,22 @@
+'use strict'
+
+const Joi = require('joi')
+const { isMetric } = require('../test-validators')
+
+const t = (module.exports = require('../create-service-tester')())
+
+t.create('EssentialsX (id 9089)')
+  .get('/9089.json')
+  .expectJSONTypes(
+    Joi.object().keys({
+      name: 'downloads',
+      value: isMetric,
+    })
+  )
+
+t.create('Invalid Resource (id 1)')
+  .get('/1.json')
+  .expectJSON({
+    name: 'downloads',
+    value: 'not found',
+  })
diff --git a/services/spiget/spiget-latest-version.service.js b/services/spiget/spiget-latest-version.service.js
new file mode 100644
index 0000000000..79a43b5426
--- /dev/null
+++ b/services/spiget/spiget-latest-version.service.js
@@ -0,0 +1,53 @@
+'use strict'
+
+const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+
+const { renderVersionBadge } = require('../../lib/version')
+
+const Joi = require('joi')
+const versionSchema = Joi.object({
+  downloads: Joi.number().required(),
+  name: Joi.string().required(),
+}).required()
+
+module.exports = class SpigetLatestVersion extends BaseSpigetService {
+  static get route() {
+    return {
+      base: 'spiget/version',
+      pattern: ':resourceid',
+    }
+  }
+
+  static get defaultBadgeData() {
+    return {
+      label: 'version',
+      color: 'blue',
+    }
+  }
+
+  async handle({ resourceid }) {
+    const { name } = await this.fetch({
+      resourceid,
+      schema: versionSchema,
+      url: `https://api.spiget.org/v2/resources/${resourceid}/versions/latest`,
+    })
+    return renderVersionBadge({ version: name })
+  }
+
+  static get category() {
+    return 'version'
+  }
+  static get examples() {
+    return [
+      {
+        title: 'Spiget Version',
+        namedParams: {
+          resourceid: '9089',
+        },
+        staticPreview: renderVersionBadge({ version: 2.1 }),
+        documentation,
+        keywords,
+      },
+    ]
+  }
+}
diff --git a/services/spiget/spiget-latest-version.tester.js b/services/spiget/spiget-latest-version.tester.js
new file mode 100644
index 0000000000..f484295632
--- /dev/null
+++ b/services/spiget/spiget-latest-version.tester.js
@@ -0,0 +1,24 @@
+'use strict'
+
+const Joi = require('joi')
+const { withRegex } = require('../test-validators')
+
+const t = (module.exports = require('../create-service-tester')())
+
+// Note that Spigot versions can be anything (including just a string), so we'll make sure it's not returning 'not found'
+
+t.create('EssentialsX (id 9089)')
+  .get('/9089.json')
+  .expectJSONTypes(
+    Joi.object().keys({
+      name: 'version',
+      value: withRegex(/^(?!not found$)/),
+    })
+  )
+
+t.create('Invalid Resource (id 1)')
+  .get('/1.json')
+  .expectJSON({
+    name: 'version',
+    value: 'not found',
+  })
diff --git a/services/spiget/spiget-rating.service.js b/services/spiget/spiget-rating.service.js
new file mode 100644
index 0000000000..625e5d010e
--- /dev/null
+++ b/services/spiget/spiget-rating.service.js
@@ -0,0 +1,71 @@
+'use strict'
+
+const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+
+const { starRating, metric } = require('../../lib/text-formatters')
+const { floorCount } = require('../../lib/color-formatters')
+
+module.exports = class SpigetRatings extends BaseSpigetService {
+  static get route() {
+    return {
+      base: 'spiget',
+      pattern: ':format(rating|stars)/:resourceid',
+    }
+  }
+
+  async handle({ format, resourceid }) {
+    const { rating } = await this.fetch({ resourceid })
+    return this.constructor.render({
+      format,
+      total: rating.count,
+      average: rating.average,
+    })
+  }
+
+  static render({ format, total, average }) {
+    const message =
+      format === 'stars'
+        ? starRating(average)
+        : `${average}/5 (${metric(total)})`
+    return {
+      message,
+      color: floorCount(average, 2, 3, 4),
+    }
+  }
+
+  static get category() {
+    return 'rating'
+  }
+
+  static get defaultBadgeData() {
+    return { label: 'rating' }
+  }
+
+  static get examples() {
+    return [
+      {
+        title: 'Spiget Stars',
+        pattern: 'stars/:resourceid',
+        namedParams: {
+          resourceid: '9089',
+        },
+        staticPreview: this.render({
+          format: 'stars',
+          total: 325,
+          average: 4.5,
+        }),
+        documentation,
+      },
+      {
+        title: 'Spiget Rating',
+        pattern: 'rating/:resourceid',
+        namedParams: {
+          resourceid: '9089',
+        },
+        staticPreview: this.render({ total: 325, average: 4.5 }),
+        documentation,
+        keywords,
+      },
+    ]
+  }
+}
diff --git a/services/spiget/spiget-rating.tester.js b/services/spiget/spiget-rating.tester.js
new file mode 100644
index 0000000000..46263caa58
--- /dev/null
+++ b/services/spiget/spiget-rating.tester.js
@@ -0,0 +1,39 @@
+'use strict'
+
+const Joi = require('joi')
+
+const { isStarRating, withRegex } = require('../test-validators')
+
+const t = (module.exports = require('../create-service-tester')())
+
+t.create('Stars - EssentialsX (id 9089)')
+  .get('/stars/9089.json')
+  .expectJSONTypes(
+    Joi.object().keys({
+      name: 'rating',
+      value: isStarRating,
+    })
+  )
+
+t.create('Stars - Invalid Resource (id 1)')
+  .get('/stars/1.json')
+  .expectJSON({
+    name: 'rating',
+    value: 'not found',
+  })
+
+t.create('Rating - EssentialsX (id 9089)')
+  .get('/rating/9089.json')
+  .expectJSONTypes(
+    Joi.object().keys({
+      name: 'rating',
+      value: withRegex(/^(\d*\.\d+)(\/5 \()(\d+)(\))$/),
+    })
+  )
+
+t.create('Rating - Invalid Resource (id 1)')
+  .get('/rating/1.json')
+  .expectJSON({
+    name: 'rating',
+    value: 'not found',
+  })
diff --git a/services/spiget/spiget-tested-versions.service.js b/services/spiget/spiget-tested-versions.service.js
new file mode 100644
index 0000000000..c647d4bf87
--- /dev/null
+++ b/services/spiget/spiget-tested-versions.service.js
@@ -0,0 +1,57 @@
+'use strict'
+
+const { BaseSpigetService, documentation, keywords } = require('./spiget-base')
+
+module.exports = class SpigetTestedVersions extends BaseSpigetService {
+  static get route() {
+    return {
+      base: 'spiget/tested-versions',
+      pattern: ':resourceid',
+    }
+  }
+
+  static get defaultBadgeData() {
+    return {
+      label: 'tested versions',
+      color: 'blue',
+    }
+  }
+
+  async handle({ resourceid }) {
+    const { testedVersions } = await this.fetch({ resourceid })
+    const { versions } = this.transform({ testedVersions })
+    return this.constructor.render({ versions })
+  }
+
+  transform({ testedVersions }) {
+    const earliest = testedVersions[0]
+    const latest = testedVersions.slice(-1)[0]
+    let versions = ''
+    if (earliest === latest) {
+      versions = earliest
+    } else {
+      versions = `${earliest}-${latest}`
+    }
+    return { versions }
+  }
+
+  static render({ versions }) {
+    return {
+      message: versions,
+    }
+  }
+
+  static get examples() {
+    return [
+      {
+        title: 'Spiget Tested Versions',
+        namedParams: {
+          resourceid: '9089',
+        },
+        staticPreview: this.render({ versions: '1.7-1.13' }),
+        documentation,
+        keywords,
+      },
+    ]
+  }
+}
diff --git a/services/spiget/spiget-tested-versions.tester.js b/services/spiget/spiget-tested-versions.tester.js
new file mode 100644
index 0000000000..696bc2d53f
--- /dev/null
+++ b/services/spiget/spiget-tested-versions.tester.js
@@ -0,0 +1,71 @@
+'use strict'
+
+const Joi = require('joi')
+
+const t = (module.exports = require('../create-service-tester')())
+
+const { withRegex } = require('../test-validators')
+
+const multipleVersions = withRegex(/^([+]?\d*\.\d+)(-)([+]?\d*\.\d+)$/)
+
+t.create('EssentialsX - multiple versions supported - (id 9089)')
+  .get('/9089.json')
+  .expectJSONTypes(
+    Joi.object().keys({
+      name: 'tested versions',
+      value: multipleVersions,
+    })
+  )
+
+t.create('Invalid Resource (id 1)')
+  .get('/1.json')
+  .expectJSON({
+    name: 'tested versions',
+    value: 'not found',
+  })
+
+t.create('Nock - single version supported')
+  .get('/1.json')
+  .intercept(nock =>
+    nock('https://api.spiget.org/v2/resources/')
+      .get('/1')
+      .reply(200, {
+        downloads: 1,
+        file: {
+          size: 1,
+          sizeUnit: '1',
+        },
+        testedVersions: ['1.13'],
+        rating: {
+          count: 1,
+          average: 1,
+        },
+      })
+  )
+  .expectJSON({
+    name: 'tested versions',
+    value: '1.13',
+  })
+
+t.create('Nock - multiple versions supported')
+  .get('/1.json')
+  .intercept(nock =>
+    nock('https://api.spiget.org/v2/resources/')
+      .get('/1')
+      .reply(200, {
+        downloads: 1,
+        file: {
+          size: 1,
+          sizeUnit: '1',
+        },
+        testedVersions: ['1.10', '1.11', '1.12', '1.13'],
+        rating: {
+          count: 1,
+          average: 1,
+        },
+      })
+  )
+  .expectJSON({
+    name: 'tested versions',
+    value: '1.10-1.13',
+  })
-- 
GitLab