From a583ca71057a31eb3e4dfa4c648449705d156343 Mon Sep 17 00:00:00 2001
From: "Eray Erdin (&mut self)" <eraygezer.94@gmail.com>
Date: Sun, 4 Aug 2024 23:25:25 +0300
Subject: [PATCH] [Crates] Implement Dependents Badge (#10438)

* add dependents service

* remove redundant timeout
---
 services/crates/crates-dependents.service.js | 49 ++++++++++++++++++++
 services/crates/crates-dependents.tester.js  | 15 ++++++
 2 files changed, 64 insertions(+)
 create mode 100644 services/crates/crates-dependents.service.js
 create mode 100644 services/crates/crates-dependents.tester.js

diff --git a/services/crates/crates-dependents.service.js b/services/crates/crates-dependents.service.js
new file mode 100644
index 0000000000..dbbfc09392
--- /dev/null
+++ b/services/crates/crates-dependents.service.js
@@ -0,0 +1,49 @@
+import Joi from 'joi'
+import { pathParams } from '../index.js'
+import { metric } from '../text-formatters.js'
+import { nonNegativeInteger } from '../validators.js'
+import { BaseCratesService, description } from './crates-base.js'
+
+const dependentsResponseSchema = Joi.object({
+  meta: Joi.object({
+    total: nonNegativeInteger,
+  }).required(),
+}).required()
+
+export default class CratesDependents extends BaseCratesService {
+  static category = 'other'
+  static route = { base: 'crates/dependents', pattern: ':crate' }
+
+  static openApi = {
+    '/crates/dependents/{crate}': {
+      get: {
+        summary: 'Crates.io Dependents',
+        description,
+        parameters: pathParams({
+          name: 'crate',
+          example: 'tokio',
+        }),
+      },
+    },
+  }
+
+  static render({ dependentCount }) {
+    return {
+      label: 'dependents',
+      message: metric(dependentCount),
+      color: dependentCount === 0 ? 'orange' : 'brightgreen',
+    }
+  }
+
+  async fetch({ crate }) {
+    const url = `https://crates.io/api/v1/crates/${crate}/reverse_dependencies`
+    const schema = dependentsResponseSchema
+    return this._requestJson({ schema, url })
+  }
+
+  async handle({ crate }) {
+    const json = await this.fetch({ crate })
+    const { total: dependentCount } = json.meta
+    return this.constructor.render({ dependentCount })
+  }
+}
diff --git a/services/crates/crates-dependents.tester.js b/services/crates/crates-dependents.tester.js
new file mode 100644
index 0000000000..32109d92db
--- /dev/null
+++ b/services/crates/crates-dependents.tester.js
@@ -0,0 +1,15 @@
+import { isMetric } from '../test-validators.js'
+import { createServiceTester } from '../tester.js'
+export const t = await createServiceTester()
+
+t.create('dependent count').get('/tokio.json').expectBadge({
+  label: 'dependents',
+  message: isMetric,
+})
+
+t.create('dependent count (nonexistent package)')
+  .get('/foobar-is-not-crate.json')
+  .expectBadge({
+    label: 'crates.io',
+    message: 'not found',
+  })
-- 
GitLab