diff --git a/services/polymart/polymart-base.js b/services/polymart/polymart-base.js new file mode 100644 index 0000000000000..9eec32824368c --- /dev/null +++ b/services/polymart/polymart-base.js @@ -0,0 +1,51 @@ +import Joi from 'joi' +import { BaseJsonService } from '../index.js' + +const resourceSchema = Joi.object({ + response: Joi.object({ + resource: Joi.object({ + price: Joi.number().required(), + downloads: Joi.string().required(), + reviews: Joi.object({ + count: Joi.number().required(), + stars: Joi.number().required(), + }).required(), + updates: Joi.object({ + latest: Joi.object({ + version: Joi.string().required(), + }).required(), + }).required(), + }).required(), + }).required(), +}).required() + +const notFoundResourceSchema = Joi.object({ + response: Joi.object({ + success: Joi.boolean().required(), + errors: Joi.object().required(), + }).required(), +}) + +const resourceFoundOrNotSchema = Joi.alternatives( + resourceSchema, + notFoundResourceSchema +) + +const documentation = ` +

You can find your resource ID in the url for your resource page.

+

Example: https://polymart.org/resource/polymart-plugin.323 - Here the Resource ID is 323.

` + +class BasePolymartService extends BaseJsonService { + async fetch({ + resourceId, + schema = resourceFoundOrNotSchema, + url = `https://api.polymart.org/v1/getResourceInfo/?resource_id=${resourceId}`, + }) { + return this._requestJson({ + schema, + url, + }) + } +} + +export { documentation, BasePolymartService } diff --git a/services/polymart/polymart-downloads.service.js b/services/polymart/polymart-downloads.service.js new file mode 100644 index 0000000000000..fe2d4f641ccea --- /dev/null +++ b/services/polymart/polymart-downloads.service.js @@ -0,0 +1,35 @@ +import { NotFound } from '../../core/base-service/errors.js' +import { renderDownloadsBadge } from '../downloads.js' +import { BasePolymartService, documentation } from './polymart-base.js' + +export default class PolymartDownloads extends BasePolymartService { + static category = 'downloads' + + static route = { + base: 'polymart/downloads', + pattern: ':resourceId', + } + + static examples = [ + { + title: 'Polymart Downloads', + namedParams: { + resourceId: '323', + }, + staticPreview: renderDownloadsBadge({ downloads: 655 }), + documentation, + }, + ] + + static defaultBadgeData = { + label: 'downloads', + } + + async handle({ resourceId }) { + const { response } = await this.fetch({ resourceId }) + if (!response.resource) { + throw new NotFound() + } + return renderDownloadsBadge({ downloads: response.resource.downloads }) + } +} diff --git a/services/polymart/polymart-downloads.tester.js b/services/polymart/polymart-downloads.tester.js new file mode 100644 index 0000000000000..4bc7f3c356646 --- /dev/null +++ b/services/polymart/polymart-downloads.tester.js @@ -0,0 +1,13 @@ +import { isMetric } from '../test-validators.js' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('Polymart Plugin (id 323)').get('/323.json').expectBadge({ + label: 'downloads', + message: isMetric, +}) + +t.create('Invalid Resource (id 0)').get('/0.json').expectBadge({ + label: 'downloads', + message: 'not found', +}) diff --git a/services/polymart/polymart-latest-version.service.js b/services/polymart/polymart-latest-version.service.js new file mode 100644 index 0000000000000..aa6083374e049 --- /dev/null +++ b/services/polymart/polymart-latest-version.service.js @@ -0,0 +1,38 @@ +import { NotFound } from '../../core/base-service/errors.js' +import { renderVersionBadge } from '../version.js' +import { BasePolymartService, documentation } from './polymart-base.js' +export default class PolymartLatestVersion extends BasePolymartService { + static category = 'version' + + static route = { + base: 'polymart/version', + pattern: ':resourceId', + } + + static examples = [ + { + title: 'Polymart Version', + namedParams: { + resourceId: '323', + }, + staticPreview: renderVersionBadge({ + version: 'v1.2.9', + }), + documentation, + }, + ] + + static defaultBadgeData = { + label: 'polymart', + } + + async handle({ resourceId }) { + const { response } = await this.fetch({ resourceId }) + if (!response.resource) { + throw new NotFound() + } + return renderVersionBadge({ + version: response.resource.updates.latest.version, + }) + } +} diff --git a/services/polymart/polymart-latest-version.tester.js b/services/polymart/polymart-latest-version.tester.js new file mode 100644 index 0000000000000..bff2a8e3c37f6 --- /dev/null +++ b/services/polymart/polymart-latest-version.tester.js @@ -0,0 +1,13 @@ +import { isVPlusDottedVersionNClauses } from '../test-validators.js' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('Polymart Plugin (id 323)').get('/323.json').expectBadge({ + label: 'polymart', + message: isVPlusDottedVersionNClauses, +}) + +t.create('Invalid Resource (id 0)').get('/0.json').expectBadge({ + label: 'polymart', + message: 'not found', +}) diff --git a/services/polymart/polymart-rating.service.js b/services/polymart/polymart-rating.service.js new file mode 100644 index 0000000000000..fa067c5861d60 --- /dev/null +++ b/services/polymart/polymart-rating.service.js @@ -0,0 +1,65 @@ +import { starRating, metric } from '../text-formatters.js' +import { floorCount } from '../color-formatters.js' +import { NotFound } from '../../core/base-service/errors.js' +import { BasePolymartService, documentation } from './polymart-base.js' + +export default class PolymartRatings extends BasePolymartService { + static category = 'rating' + + static route = { + base: 'polymart', + pattern: ':format(rating|stars)/:resourceId', + } + + static examples = [ + { + title: 'Polymart Stars', + pattern: 'stars/:resourceId', + namedParams: { + resourceId: '323', + }, + staticPreview: this.render({ + format: 'stars', + total: 14, + average: 5, + }), + documentation, + }, + { + title: 'Polymart Rating', + pattern: 'rating/:resourceId', + namedParams: { + resourceId: '323', + }, + staticPreview: this.render({ total: 14, average: 5 }), + documentation, + }, + ] + + static defaultBadgeData = { + label: 'rating', + } + + static render({ format, total, average }) { + const message = + format === 'stars' + ? starRating(average) + : `${average}/5 (${metric(total)})` + return { + message, + color: floorCount(average, 2, 3, 4), + } + } + + async handle({ format, resourceId }) { + const { response } = await this.fetch({ resourceId }) + if (!response.resource) { + throw new NotFound() + } + return this.constructor.render({ + format, + total: response.resource.reviews.count, + average: response.resource.reviews.stars.toFixed(2), + }) + } +} diff --git a/services/polymart/polymart-rating.tester.js b/services/polymart/polymart-rating.tester.js new file mode 100644 index 0000000000000..547d0d988f6c3 --- /dev/null +++ b/services/polymart/polymart-rating.tester.js @@ -0,0 +1,27 @@ +import { isStarRating, withRegex } from '../test-validators.js' +import { createServiceTester } from '../tester.js' +export const t = await createServiceTester() + +t.create('Stars - Polymart Plugin (id 323)') + .get('/stars/323.json') + .expectBadge({ + label: 'rating', + message: isStarRating, + }) + +t.create('Stars - Invalid Resource (id 0)').get('/stars/0.json').expectBadge({ + label: 'rating', + message: 'not found', +}) + +t.create('Rating - Polymart Plugin (id 323)') + .get('/rating/323.json') + .expectBadge({ + label: 'rating', + message: withRegex(/^(\d*\.\d+)(\/5 \()(\d+)(\))$/), + }) + +t.create('Rating - Invalid Resource (id 0)').get('/rating/0.json').expectBadge({ + label: 'rating', + message: 'not found', +})