lib/util.js
'use strict' const cli = require('heroku-cli-util')const debug = require('./debug')const getBastion = require('./bastion').getBastionconst {sortBy} = require('lodash')const printf = require('printf')const URL = require('url').URL function getConfigVarName (configVars) { let connstringVars = configVars.filter((cv) => (cv.endsWith('_URL'))) if (connstringVars.length === 0) throw new Error('Database URL not found for this addon') return connstringVars[0]} exports.getConfigVarName = getConfigVarName function formatAttachment (attachment) { let attName = cli.color.addon(attachment.name) let output = [cli.color.dim('as'), attName] let appInfo = `on ${cli.color.app(attachment.app.name)} app` output.push(cli.color.dim(appInfo)) return output.join(' ')} function renderAttachment (attachment, app, isLast) { let line = isLast ? '└─' : '├─' let attName = formatAttachment(attachment) return printf(' %s %s', cli.color.dim(line), attName)} Function `presentCredentialAttachments` has 36 lines of code (exceeds 25 allowed). Consider refactoring.
Function `presentCredentialAttachments` has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.function presentCredentialAttachments (app, credAttachments, credentials, cred) { let isForeignApp = (attOrAddon) => attOrAddon.app.name !== app let atts = sortBy(credAttachments, isForeignApp, 'name', 'app.name' ) // render each attachment under the credential let attLines = atts.map(function (attachment, idx) { let isLast = (idx === credAttachments.length - 1) return renderAttachment(attachment, app, isLast) }) let rotationLines = [] let credentialStore = credentials.filter(a => a.name === cred)[0] if (credentialStore.state === 'rotating') { let formatted = credentialStore.credentials.map(function (credential, idx) { return { 'user': credential.user, 'state': credential.state, 'connections': credential.connections } }) let connectionInformationAvailable = formatted.some((c) => c.connections != null) if (connectionInformationAvailable) { let prefix = ' ' rotationLines.push(`${prefix}Usernames currently active for this credential:`) cli.table(formatted, { printHeader: false, printLine: function (line) { rotationLines.push(line) }, columns: [ {key: 'user', format: (v, r) => `${prefix}${v}`}, {key: 'state', format: (v, r) => (v === 'revoking') ? 'waiting for no connections to be revoked' : v}, {key: 'connections', format: (v, r) => `${v} connections`} ] }) } } return [cred].concat(attLines).concat(rotationLines).join('\n')} exports.presentCredentialAttachments = presentCredentialAttachments Function `getConnectionDetails` has 26 lines of code (exceeds 25 allowed). Consider refactoring.exports.getConnectionDetails = function (attachment, config) { const url = require('url') const configVars = attachment.config_vars.filter((cv) => { return config[cv] && config[cv].startsWith('postgres://') }) if (configVars.length === 0) { throw new Error(`No config vars found for ${attachment.name}; perhaps they were removed as a side effect of ${cli.color.cmd('heroku rollback')}? Use ${cli.color.cmd('heroku addons:attach')} to create a new attachment and then ${cli.color.cmd('heroku addons:detach')} to remove the current attachment.`) } const connstringVar = getConfigVarName(configVars) // remove _URL from the end of the config var name const baseName = connstringVar.slice(0, -4) // build the default payload for non-bastion dbs debug(`Using "${connstringVar}" to connect to your database…`) const target = url.parse(config[connstringVar]) let [user, password] = target.auth.split(':') let payload = { user, password, database: target.path.split('/', 2)[1], host: target.hostname, port: target.port, attachment, url: target } // If bastion creds exist, graft it into the payload const bastion = getBastion(config, baseName) if (bastion) { Object.assign(payload, bastion) } return payload} exports.starterPlan = a => !!a.plan.name.match(/(dev|basic)$/) exports.legacyPlan = a => !!a.plan.name.match(/^legacy/) Function `configVarNamesFromValue` has a Cognitive Complexity of 10 (exceeds 5 allowed). Consider refactoring.exports.configVarNamesFromValue = (config, value) => { let keys = [] for (let key of Object.keys(config)) { let configVal = config[key] if (configVal === value) { keys.push(key) } else if (configVal.startsWith('postgres://')) { try { let configURL = new URL(configVal) let ourURL = new URL(value) let components = [ 'protocol', 'hostname', 'port', 'pathname' ] if (components.every((component) => ourURL[component] === configURL[component])) { keys.push(key) } } catch (err) { // ignore -- this is not a valid URL so not a matching URL } } } return sortBy(keys, k => k !== 'DATABASE_URL', 'name')} exports.databaseNameFromUrl = (uri, config) => { const url = require('url') let names = exports.configVarNamesFromValue(config, uri) let name = names.pop() while (names.length > 0 && name === 'DATABASE_URL') name = names.pop() if (name) return cli.color.configVar(name.replace(/_URL$/, '')) uri = url.parse(uri) return `${uri.hostname}:${uri.port || 5432}${uri.path}`}