ncm-cli
Version:
Command-line tool for NodeSource Certified Modules 2.0
162 lines (140 loc) • 3.6 kB
JavaScript
const readline = require('readline')
const url = require('url')
const pDefer = require('p-defer')
const clientRequest = require('./client-request')
const { setTokens, api, ncmApi, getTokens, popValue } = require('./config')
const {
formatError
} = require('../lib/ncm-style')
const E = console.error
module.exports = {
apiRequest,
formatAPIURL,
graphql,
queryReadline,
queryReadlineHidden,
refreshSession,
reversedSplit
}
function formatAPIURL (pathname, query) {
return api + url.format({
pathname,
query
})
}
async function graphql (uri, query, variables) {
const updatedUri = uri.replace('api.nodesource', 'api.ncm.nodesource')
const body = await apiRequest(
'POST',
updatedUri,
{ query, variables }
)
return body.data
}
async function apiRequest (method, uri, reqbody) {
let { token } = getTokens()
let body
while (!body && token) {
try {
const response = await clientRequest({
method,
uri,
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json'
},
json: true,
body: JSON.stringify(reqbody)
})
body = response.body
} catch (error) {
if (error.statusCode === 401) {
token = await refreshSession()
} else {
throw error
}
}
}
return body
}
async function refreshSession () {
// Just short-circut this if the token was provided via env...
if (process.env.NCM_TOKEN) return process.env.NCM_TOKEN
const refreshToken = popValue('refreshToken')
if (!refreshToken || !refreshToken.trim()) {
return
}
let refreshData
try {
const response = await clientRequest({
method: 'GET',
uri: formatAPIURL('/accounts/auth/refresh'),
json: true,
headers: {
Authorization: `Bearer ${refreshToken}`
}
})
refreshData = response.body
} catch (err) {
E()
E(formatError('Failed to refresh session.', err))
E()
process.exitCode = 1
return
}
setTokens(refreshData)
return refreshData.session
}
function queryReadline (query) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
const deferred = pDefer()
rl.question(query, answer => {
deferred.resolve(answer)
rl.close()
})
return deferred.promise
}
// For passwords
function queryReadlineHidden (query) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
})
const deferred = pDefer()
rl.question(query, answer => {
deferred.resolve(answer)
rl.close()
})
// This is an unpleasant hack.
// This functionality belongs in Node core.
const _writeToOutput = rl._writeToOutput.bind(rl)
rl._writeToOutput = function _writeHiddenToOutput (stringToWrite) {
let newStr = ''
// Do not interfere with a prompt.
if (this._prompt && stringToWrite.startsWith(this._prompt)) {
stringToWrite = stringToWrite.substring(this._prompt.length)
newStr += this._prompt
}
// Iterate and reconstruct word characters (including spaces) to hide.
for (let char of stringToWrite) {
if (/^[\S ]+$/.test(char)) {
char = '*'
}
newStr += char
}
stringToWrite = newStr
_writeToOutput(stringToWrite)
}
return deferred.promise
}
// To be able to replicate regex lookbehinds in Node 8.
function reversedSplit (str, split) {
return reverseStr(str).split(split).map(reverseStr).reverse()
}
function reverseStr (str) {
return str.split('').reverse().join('')
}