From 07c5f47a739416979fac4c71bbd2693a186f82de Mon Sep 17 00:00:00 2001 From: Paul Melnikow Date: Wed, 31 Oct 2018 17:19:14 -0400 Subject: [PATCH] Rework [suggest] code using async/await (#2029) --- lib/suggest.js | 221 +++++++++++++++++++++++-------------------------- 1 file changed, 102 insertions(+), 119 deletions(-) diff --git a/lib/suggest.js b/lib/suggest.js index c86d5d2934985..933f353205d83 100644 --- a/lib/suggest.js +++ b/lib/suggest.js @@ -1,46 +1,83 @@ // Suggestion API // // eg. /$suggest/v1?url=https://github.com/badges/shields +// +// Tests for this endpoint are in services/suggest/suggest.spec.js. The +// endpoint is called from frontend/components/suggestion-and-search.js. 'use strict' const nodeUrl = require('url') const request = require('request') -// data: {url}, JSON-serializable object. -// end: function(json), with json of the form: -// - badges: list of objects of the form: -// - link: target as a string URL. -// - badge: shields image URL. -// - name: string -function suggest(allowedOrigin, githubApiProvider, data, end, ask) { - // The typical dev and production setups are cross-origin. However, in - // Heroku deploys and some self-hosted deploys these requests may come from - // the same host. - const origin = ask.req.headers.origin - if (origin) { - if (allowedOrigin.includes(origin)) { - ask.res.setHeader('Access-Control-Allow-Origin', origin) - } else { - ask.res.setHeader('Access-Control-Allow-Origin', 'null') - end({ err: 'Disallowed' }) - return - } +function twitterPage(url) { + if (url.protocol === null) { + return null + } + + const schema = url.protocol.slice(0, -1) + const host = url.host + const path = url.path + return { + name: 'Twitter', + link: `https://twitter.com/intent/tweet?text=Wow:&url=${encodeURIComponent( + url.href + )}`, + badge: `https://img.shields.io/twitter/url/${schema}/${host}${path}.svg?style=social`, + } +} + +function githubIssues(user, repo) { + const repoSlug = `${user}/${repo}` + return { + name: 'GitHub issues', + link: `https://github.com/${repoSlug}/issues`, + badge: `https://img.shields.io/github/issues/${repoSlug}.svg`, } +} - let url +function githubForks(user, repo) { + const repoSlug = `${user}/${repo}` + return { + name: 'GitHub forks', + link: `https://github.com/${repoSlug}/network`, + badge: `https://img.shields.io/github/forks/${repoSlug}.svg`, + } +} + +function githubStars(user, repo) { + const repoSlug = `${user}/${repo}` + return { + name: 'GitHub stars', + link: `https://github.com/${repoSlug}/stargazers`, + badge: `https://img.shields.io/github/stars/${repoSlug}.svg`, + } +} + +async function githubLicense(githubApiProvider, user, repo) { + const repoSlug = `${user}/${repo}` + + let link = `https://github.com/${repoSlug}` + + const { buffer } = await githubApiProvider.requestAsPromise( + request, + `/repos/${repoSlug}/license` + ) try { - url = nodeUrl.parse(data.url) - } catch (e) { - end({ err: '' + e }) - return + const data = JSON.parse(buffer) + if ('html_url' in data) { + link = data.html_url + } + } catch (e) {} + + return { + name: 'GitHub license', + badge: `https://img.shields.io/github/license/${repoSlug}.svg`, + link, } - findSuggestions(githubApiProvider, url, end) } -// url: string -// cb: function({badges}) -function findSuggestions(githubApiProvider, url, cb) { +async function findSuggestions(githubApiProvider, url) { let promises = [] if (url.hostname === 'github.com') { const userRepo = url.pathname.slice(1).split('/') @@ -54,106 +91,52 @@ function findSuggestions(githubApiProvider, url, cb) { ]) } promises.push(twitterPage(url)) - Promise.all(promises) - .then(badges => { - // eslint-disable-next-line standard/no-callback-literal - cb({ - badges: badges.filter(b => b != null), - }) - }) - .catch(err => { - // eslint-disable-next-line standard/no-callback-literal - cb({ badges: [], err: err }) - }) -} -function twitterPage(url) { - if (url.protocol === null) { - return Promise.resolve(null) - } - const schema = url.protocol.slice(0, -1) - const host = url.host - const path = url.path - return Promise.resolve({ - name: 'Twitter', - link: - 'https://twitter.com/intent/tweet?text=Wow:&url=' + - encodeURIComponent(url.href), - badge: - 'https://img.shields.io/twitter/url/' + - schema + - '/' + - host + - path + - '.svg?style=social', - }) -} + const suggestions = await Promise.all(promises) -function githubIssues(user, repo) { - const userRepo = user + '/' + repo - return Promise.resolve({ - name: 'GitHub issues', - link: 'https://github.com/' + userRepo + '/issues', - badge: 'https://img.shields.io/github/issues/' + userRepo + '.svg', - }) + return suggestions.filter(b => b != null) } -function githubForks(user, repo) { - const userRepo = user + '/' + repo - return Promise.resolve({ - name: 'GitHub forks', - link: 'https://github.com/' + userRepo + '/network', - badge: 'https://img.shields.io/github/forks/' + userRepo + '.svg', - }) -} - -function githubStars(user, repo) { - const userRepo = user + '/' + repo - return Promise.resolve({ - name: 'GitHub stars', - link: 'https://github.com/' + userRepo + '/stargazers', - badge: 'https://img.shields.io/github/stars/' + userRepo + '.svg', - }) -} - -function githubLicense(githubApiProvider, user, repo) { - return new Promise(resolve => { - const apiUrl = `/repos/${user}/${repo}/license` - githubApiProvider.request(request, apiUrl, {}, (err, res, buffer) => { - if (err !== null) { - resolve(null) +// data: {url}, JSON-serializable object. +// end: function(json), with json of the form: +// - badges: list of objects of the form: +// - link: target as a string URL. +// - badge: shields image URL. +// - name: string +function setRoutes(allowedOrigin, githubApiProvider, server) { + server.ajax.on('suggest/v1', (data, end, ask) => { + // The typical dev and production setups are cross-origin. However, in + // Heroku deploys and some self-hosted deploys these requests may come from + // the same host. + const origin = ask.req.headers.origin + if (origin) { + if (allowedOrigin.includes(origin)) { + ask.res.setHeader('Access-Control-Allow-Origin', origin) + } else { + ask.res.setHeader('Access-Control-Allow-Origin', 'null') + end({ err: 'Disallowed' }) return } - const defaultBadge = { - name: 'GitHub license', - link: `https://github.com/${user}/${repo}`, - badge: `https://img.shields.io/github/license/${user}/${repo}.svg`, - } - if (res.statusCode !== 200) { - resolve(defaultBadge) - } - try { - const data = JSON.parse(buffer) - if (data.html_url) { - defaultBadge.link = data.html_url - resolve(defaultBadge) - } else { - resolve(defaultBadge) - } - } catch (e) { - resolve(defaultBadge) - } - }) - }) -} + } -function setRoutes(allowedOrigin, githubApiProvider, server) { - server.ajax.on('suggest/v1', (data, end, ask) => - suggest(allowedOrigin, githubApiProvider, data, end, ask) - ) + let url + try { + url = nodeUrl.parse(data.url) + } catch (e) { + end({ err: '' + e }) + return + } + + findSuggestions(githubApiProvider, url) + .then(badges => { + end({ badges }) + }) + .catch(err => { + end({ badges: [], err }) + }) + }) } module.exports = { - suggest, setRoutes, }