Repository: badges/shields (js)

Issue:
Support for CodeFactor
Can you add support for CodeFactor badges?
The default badges for CodeFactor has round edges but I like the `flat-square` style.

![badge](https://img.shields.io/badge/can_you_please-add_this_feature-green.svg?style=flat-square)

Pull request:
add [CodeFactor] grade badge
Closes #3518 

I've never used CodeFactor before but created an account to check it out as they don't really have any public documentation. There's a variety of metrics it shows/tracks in addition to the Grade (cyclomatic complexity, hotspots, duplication, etc.) so I named the classes/files with the idea that we may add other CodeFactor badges down the road

Patch:
diff --git a/services/codefactor/codefactor-grade.service.js b/services/codefactor/codefactor-grade.service.js
new file mode 100644
index 0000000000..150320f4e9
--- /dev/null
+++ b/services/codefactor/codefactor-grade.service.js
@@ -0,0 +1,60 @@
+'use strict'
+
+const Joi = require('@hapi/joi')
+const { BaseSvgScrapingService } = require('..')
+const { letterGrades } = require('./codefactor-helpers')
+
+const schema = Joi.object({
+  message: Joi.allow(...Object.keys(letterGrades)),
+}).required()
+
+module.exports = class CodeFactorGrade extends BaseSvgScrapingService {
+  static get category() {
+    return 'analysis'
+  }
+
+  static get route() {
+    return {
+      base: 'codefactor/grade',
+      pattern: ':vcsType(github|bitbucket)/:user/:repo/:branch*',
+    }
+  }
+
+  static get examples() {
+    return [
+      {
+        title: 'CodeFactor Grade',
+        namedParams: {
+          vcsType: 'github',
+          user: 'pallets',
+          repo: 'flask',
+          branch: 'master',
+        },
+        staticPreview: this.render({ grade: 'B+' }),
+      },
+    ]
+  }
+
+  static get defaultBadgeData() {
+    return {
+      label: 'code quality',
+    }
+  }
+
+  static render({ grade }) {
+    return {
+      message: grade,
+      color: letterGrades[grade].color,
+    }
+  }
+
+  async handle({ vcsType, user, repo, branch }) {
+    const { message } = await this._requestSvg({
+      schema,
+      url: `https://codefactor.io/repository/${vcsType}/${user}/${repo}/badge/${branch ||
+        ''}`,
+      errorMessages: { 404: 'repo or branch not found' },
+    })
+    return this.constructor.render({ grade: message })
+  }
+}
diff --git a/services/codefactor/codefactor-grade.spec.js b/services/codefactor/codefactor-grade.spec.js
new file mode 100644
index 0000000000..a35b08f61c
--- /dev/null
+++ b/services/codefactor/codefactor-grade.spec.js
@@ -0,0 +1,57 @@
+'use strict'
+
+const { test, given } = require('sazerac')
+const CodeFactorGrade = require('./codefactor-grade.service')
+
+describe('CodeFactorGrade', function() {
+  test(CodeFactorGrade.render, () => {
+    given({ grade: 'A' }).expect({
+      message: 'A',
+      color: 'brightgreen',
+    })
+    given({ grade: 'A-' }).expect({
+      message: 'A-',
+      color: 'green',
+    })
+    given({ grade: 'B+' }).expect({
+      message: 'B+',
+      color: 'yellowgreen',
+    })
+    given({ grade: 'B' }).expect({
+      message: 'B',
+      color: 'yellowgreen',
+    })
+    given({ grade: 'B-' }).expect({
+      message: 'B-',
+      color: 'yellowgreen',
+    })
+    given({ grade: 'C+' }).expect({
+      message: 'C+',
+      color: 'yellow',
+    })
+    given({ grade: 'C' }).expect({
+      message: 'C',
+      color: 'yellow',
+    })
+    given({ grade: 'C-' }).expect({
+      message: 'C-',
+      color: 'yellow',
+    })
+    given({ grade: 'D+' }).expect({
+      message: 'D+',
+      color: 'orange',
+    })
+    given({ grade: 'D' }).expect({
+      message: 'D',
+      color: 'orange',
+    })
+    given({ grade: 'D-' }).expect({
+      message: 'D-',
+      color: 'orange',
+    })
+    given({ grade: 'F' }).expect({
+      message: 'F',
+      color: 'red',
+    })
+  })
+})
diff --git a/services/codefactor/codefactor-helpers.js b/services/codefactor/codefactor-helpers.js
new file mode 100644
index 0000000000..98ef5382cd
--- /dev/null
+++ b/services/codefactor/codefactor-helpers.js
@@ -0,0 +1,43 @@
+'use strict'
+
+// https://support.codefactor.io/i14-glossary
+const letterGrades = {
+  A: {
+    color: 'brightgreen',
+  },
+  'A-': {
+    color: 'green',
+  },
+  'B+': {
+    color: 'yellowgreen',
+  },
+  B: {
+    color: 'yellowgreen',
+  },
+  'B-': {
+    color: 'yellowgreen',
+  },
+  'C+': {
+    color: 'yellow',
+  },
+  C: {
+    color: 'yellow',
+  },
+  'C-': {
+    color: 'yellow',
+  },
+  'D+': {
+    color: 'orange',
+  },
+  D: {
+    color: 'orange',
+  },
+  'D-': {
+    color: 'orange',
+  },
+  F: {
+    color: 'red',
+  },
+}
+
+module.exports = { letterGrades }


Test patch:
diff --git a/services/codefactor/codefactor-grade.tester.js b/services/codefactor/codefactor-grade.tester.js
new file mode 100644
index 0000000000..b578ff1299
--- /dev/null
+++ b/services/codefactor/codefactor-grade.tester.js
@@ -0,0 +1,27 @@
+'use strict'
+
+const Joi = require('@hapi/joi')
+const t = (module.exports = require('../tester').createServiceTester())
+const { letterGrades } = require('./codefactor-helpers')
+const codeFactorGrade = Joi.allow(...Object.keys(letterGrades))
+
+t.create('Grade')
+  .get('/github/google/guava.json')
+  .expectBadge({
+    label: 'code quality',
+    message: codeFactorGrade,
+  })
+
+t.create('Grade (branch)')
+  .get('/github/pallets/flask/master.json')
+  .expectBadge({
+    label: 'code quality',
+    message: codeFactorGrade,
+  })
+
+t.create('Grade (nonexistent repo)')
+  .get('/github/badges/asdfasdfasdfasdfasfae.json')
+  .expectBadge({
+    label: 'code quality',
+    message: 'repo or branch not found',
+  })