8
\$\begingroup\$

Solved, question with improved code

I've created a simple npm module and CLI program to get the latest GitHub and Bitbucket release from their APIs using Node.js. Please tell me if there's anything to improve!

Module code:

#!/usr/bin/env node
const fetch = require("node-fetch");

/**
 * @param {string} string
 * @return {string}
 */
const normalize = (string) => {
    try {
        return string.toUpperCase().trim()
    } catch (e) {
        return string
    }
}

/**
 * @param {string} provider
 * @param {string} user
 * @param {string} repo
 * @param {string} part
 */
module.exports.getRelease = async ({provider, user, repo, part = ""}) => {
    if (normalize(provider) === normalize("github")) {
        let json = (await (await fetch(`https://api.github.com/repos/${user}/${repo}/releases/latest`)).json())
        if (json.message === "Not Found") throw "Invalid repository"
        if (!("assets" in json)) throw "Rate limit exceeded"
        let browser_download_urls = json.assets.map(asset => asset.browser_download_url)
        return browser_download_urls.filter(url => url.includes(part))
    } else if (normalize(provider) === normalize("bitbucket")) {
        let json = (await (await fetch(`https://api.bitbucket.org/2.0/repositories/${user}/${repo}/downloads/`)).json())
        if (json.type === "error") throw "Invalid repository"
        let links = json.values.map(value => value.links.self.href)
        return links.filter(url => url.includes(part))
    } else {
        throw "Invalid provider"
    }
}

const usage = _ => {
    console.log(
        `Usage: get-release (github|bitbucket) user repo [partofreleasefile]
   Ex: get-release github phhusson treble_experimentations
       get-release github phhusson treble_experimentations arm64-ab-gapps
       get-release bitbucket JesusFreke smali
       get-release bitbucket JesusFreke smali baksmali`
    )
    process.exit(1)
}

// If via CLI
if (require.main === module) {
    let args = process.argv.slice(2)
    if (args.length !== 3 && args.length !== 4) {
        usage()
    }
    module.exports.getRelease({
        provider: args[0],
        user: args[1],
        repo: args[2],
        part: args[3]
    }).then(result => {
        if (result.length !== 1) {
            console.log(result)
        } else {
            console.log(result[0])
        }
    }).catch(error => {
        console.log(error)
        usage()
        process.exit(1)
    })
}

Called using:

const { getRelease } = require("get-release")
;(async _ => {
  let url = await getRelease(
      {
          provider: "github",
          user: "phhusson",
          repo: "treble_experimentations",
          part: "arm64-ab-gapps"
      }
  )
  console.log(url[0])
})()
\$\endgroup\$
2
  • \$\begingroup\$ Why the double await in (await (await fetch(, does it not work with a single await? \$\endgroup\$
    – konijn
    Jul 22 '20 at 6:49
  • \$\begingroup\$ @konijn Nope, it doesn't work without the double \$\endgroup\$ Jul 22 '20 at 12:54
2
\$\begingroup\$

Consider using a hashmap of provider -> process. This will enable you to register new providers with relative ease.

async function githubRelease({user, repo, part = ""}) {
  let json = (await (await fetch(`https://api.github.com/repos/${user}/${repo}/releases/latest`)).json())
  if (json.message === "Not Found") throw "Invalid repository"
  if (!("assets" in json)) throw "Rate limit exceeded"
  let browser_download_urls = json.assets.map(asset => asset.browser_download_url)
  return browser_download_urls.filter(url => url.includes(part))
}

It will then be mapped inside the getRelease function as follows:

module.exports.getRelease = async ({provider, user, repo, part = ""}) => {
    if !(providerMethods[normalise(provider)]) {
        throw "Invalid provider"
    }
    return providerMethods[normalise(provider)](user, repo, part)
}

where providerMethods would be:

let providerMethods = {
    normalise("github"): githubReleases
}

This will enable users of your module to register new (or private) registry providers. They can add a new provider to the providerMethods object (gitlab etc) and don't have to wait for you to update the library first for supporting any extra providers (you can add providers if popular).

The await (await fetch()).json() can be extracted to another function. Although, I'd prefer using the promise.then(r => r.json()).catch() chain over multiple awaits, it is entirely dependent on your comfort with the language.

\$\endgroup\$
1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.