From 6e581b7acfe81ccef1c209acb5d69a47216e95bf Mon Sep 17 00:00:00 2001
From: Fatpandac <i@fatpandac.com>
Date: Thu, 21 Dec 2023 00:28:41 +0800
Subject: [PATCH] Add [Raycast] Badge (#9801)

* feat: add Raycast extension installs Badge

* fix: capitalize the first letter

* fix: change to use the offical public API

* fix: change category to downloads
---
 services/raycast/installs.service.js | 55 ++++++++++++++++++++++++++++
 services/raycast/installs.tester.js  | 30 +++++++++++++++
 2 files changed, 85 insertions(+)
 create mode 100644 services/raycast/installs.service.js
 create mode 100644 services/raycast/installs.tester.js

diff --git a/services/raycast/installs.service.js b/services/raycast/installs.service.js
new file mode 100644
index 0000000000..9bbde6bcf4
--- /dev/null
+++ b/services/raycast/installs.service.js
@@ -0,0 +1,55 @@
+import Joi from 'joi'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseJsonService, pathParams } from '../index.js'
+import { renderDownloadsBadge } from '../downloads.js'
+
+const schema = Joi.object({
+  download_count: nonNegativeInteger,
+}).required()
+
+export default class RaycastInstalls extends BaseJsonService {
+  static category = 'downloads'
+
+  static route = {
+    base: 'raycast/dt',
+    pattern: ':user/:extension',
+  }
+
+  static openApi = {
+    '/raycast/dt/{user}/{extension}': {
+      get: {
+        summary: 'Raycast extension downloads count',
+        parameters: pathParams(
+          { name: 'user', example: 'Fatpandac' },
+          { name: 'extension', example: 'bilibili' },
+        ),
+      },
+    },
+  }
+
+  static render({ downloads }) {
+    return renderDownloadsBadge({ downloads })
+  }
+
+  async fetch({ user, extension }) {
+    return this._requestJson({
+      schema,
+      url: `https://www.raycast.com/api/v1/extensions/${user}/${extension}`,
+      httpErrors: {
+        404: 'user/extension not found',
+      },
+    })
+  }
+
+  transform(json) {
+    const downloads = json.download_count
+
+    return { downloads }
+  }
+
+  async handle({ user, extension }) {
+    const json = await this.fetch({ user, extension })
+    const { downloads } = this.transform(json)
+    return this.constructor.render({ downloads })
+  }
+}
diff --git a/services/raycast/installs.tester.js b/services/raycast/installs.tester.js
new file mode 100644
index 0000000000..e6e8e957f0
--- /dev/null
+++ b/services/raycast/installs.tester.js
@@ -0,0 +1,30 @@
+import { createServiceTester } from '../tester.js'
+import { isMetric } from '../test-validators.js'
+
+export const t = await createServiceTester()
+
+t.create('installs (invalid user)')
+  .get('/fatpandac/bilibili.json')
+  .expectBadge({
+    label: 'downloads',
+    message: 'user/extension not found',
+  })
+
+t.create('installs (not existing extension)')
+  .get('/Fatpandac/safdsaklfhe.json')
+  .expectBadge({
+    label: 'downloads',
+    message: 'user/extension not found',
+  })
+
+t.create('installs (not existing user and extension)')
+  .get('/fatpandac/safdsaklfhe.json')
+  .expectBadge({
+    label: 'downloads',
+    message: 'user/extension not found',
+  })
+
+t.create('installs (valid)').get('/Fatpandac/bilibili.json').expectBadge({
+  label: 'downloads',
+  message: isMetric,
+})
-- 
GitLab