TransWikia.com

(Improved) get-release npm module

Code Review Asked on November 25, 2021

Improved version of the original question

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!

GitHub
npm
RunKit

Improved 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} url
 * @return {string}
 */
const getJSON = async (url) => {
    return (await (await fetch(url)).json())
}

module.exports.providerMethods = {
    GITHUB: async ({user, repo, part = ""}) => {
        let json = await getJSON(`https://api.github.com/repos/${user}/${repo}/releases/latest`)
        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))
    },
    BITBUCKET: async ({user, repo, part = ""}) => {
        let json = await getJSON(`https://api.bitbucket.org/2.0/repositories/${user}/${repo}/downloads/`)
        if (json.type === "error") throw "Invalid repository"
        let links = json.values.map(value => value.links.self.href)
        return links.filter(url => url.includes(part))
    }
}

/**
 * @param {string} provider
 * @param {string} user
 * @param {string} repo
 * @param {string} part
 */
module.exports.getRelease = async ({provider, user, repo, part = ""}) => {
    if (!(module.exports.providerMethods[normalize(provider)])) {
        throw "Invalid provider"
    }
    return module.exports.providerMethods[normalize(provider)]({user, repo, part})
}

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)
    })
}

To be called using

/**
 * Options:
 * {
 *     provider: (github | bitbucket),
 *     user: username,
 *     repo: repository,
 *     part: part of release file name - optional
 * }
 *
 */

const { getRelease, providerMethods } = require("get-release")
;(async _ => {
  let url = await getRelease(
      {
          provider: "github",
          user: "phhusson",
          repo: "treble_experimentations",
          part: "arm64-ab-gapps"
      }
  )
  console.log(url[0])
})()

Custom providers can be defined using

const { getRelease, providerMethods } = require("get-release")
providerMethods.GITLAB = async ({user, repo, part = ""}) => {
  // Custom code, should return a string
}

One Answer

Now that you've allowed the users to add custom providers, how about hiding the providerMethods object itself, and only providing a function which allows them to add to it. This would allow you to also validate if the users' want to override behaviour for predefined providers.

You have a process.exit(1) inside the call to usage(). Butm when used from cli, you catch all exceptions, then call the usage function, and against have another process.exit(1).

The call to usage should not be exiting from the program entirely. It is already handled in case of errors. You can then have a --help commandline parameter, which spits out the usage string (without raising an error for the shell).


Most of your if- conditions use the negated equality check, which is somewhat harder to follow for a developer (at least in my experience). When checking for args.length, you have double negation checks. It took me almost a minute to figure out what it is supposed to do. How about applying de Morgan's laws:

if (!(args.length === 3 || args.length === 4))

Or, maybe simply adding a comment?

Also consider:

console.log((result.length === 1) ? result[0] : result)

Answered by hjpotter92 on November 25, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP