first alpha

This commit is contained in:
Vyacheslav.Sviridov
2022-06-09 20:52:09 +06:00
parent 8b3e8ae5c8
commit fc41ae955f
54 changed files with 2416 additions and 1976 deletions

0
LICENSE Normal file → Executable file
View File

0
README.md Normal file → Executable file
View File

72
bin/enm-cli.js Normal file → Executable file
View File

@@ -3,43 +3,59 @@
const { Command, Option } = require('commander')
const pkg = require('../package.json')
const inquirer = require('inquirer')
const path = require('path')
const { isEmpty } = require('../util/validation')
require('dotenv').config()
require('dotenv').config({ path: [...__dirname.split(path.sep).slice(0,-1), '.env'].join(path.sep) })
const AutoProvisioning = require('../lib/components/AutoProvisioning/AutoProvisioning')
const TopologyBrowser = require('../lib/components/TopologyBrowser/TopologyBrowser')
const AutoProvisioning = require('../lib/applications/AutoProvisioning/AutoProvisioning')
const TopologyBrowser = require('../lib/applications/TopologyBrowser/TopologyBrowser')
const logError = require('../util/logError')
const applications = ['tplg', 'prvn']
if (process.env.NODE_ENV === 'development') {
process.on('uncaughtException', function (exception) {
console.log(exception);
})
}
const applications = [
{
id: 'tplg',
appClass: TopologyBrowser,
name: 'Topology Browser',
},
{
id: 'prvn',
appClass: AutoProvisioning,
name: 'Auto Provisioning',
}
]
const appIds = applications.map(item => item.id)
const program = new Command()
program
.version(pkg.version)
// .option('-l, --login <letters>', 'ENM User Login')
// .option('-p, --password <letters>', 'ENM User Password')
.addOption(new Option('-l, --login <letters>', 'ENM User Login').env('LOGIN'))
.addOption(new Option('-p, --password <letters>', 'ENM User Password').env('PASSWORD'))
.addOption(new Option('-a, --application <letters>', 'Start specified application')
.choices(applications)
.default('tplg')
.choices(appIds)
)
.requiredOption('-u, --url <letters>', 'ENM Url')
.requiredOption('-u, --url <valid URL>', 'ENM Url')
.parse(process.argv)
const options = program.opts()
async function promptUsername() {
const input = await inquirer.prompt([
{
type: 'input',
name: 'value',
suffix: chalk.bgGreen('?'),
message: 'Type ENM login',
prefix: '👤',
message: 'Type ENM login:',
validate: isEmpty,
}
])
@@ -52,7 +68,8 @@ async function promptPassword() {
{
type: 'password',
name: 'value',
message: `Type ${options.login}'s ENM password`,
prefix: '🔑',
message: 'Type ENM password:',
validate: isEmpty,
}
])
@@ -62,37 +79,34 @@ async function promptPassword() {
async function selectApplication() {
let selectedApp
if (options.application && options.application in applications) {
if (options.application && appIds.includes(options.application)) {
selectedApp = options.application
} else {
const input = await inquirer.prompt([
{
type: 'list',
name: 'application',
suffix: '💾',
message,
choices: applications
prefix: '💾',
message: 'Select Application:',
choices: applications.map(item => ({name: item.name, value: item.id, short: item.id}))
}])
selectedApp = input.application
}
return {
tplg: TopologyBrowser,
prvn: AutoProvisioning
}[selectedApp]
const { appClass } = applications.find(item => item.id === selectedApp)
const login = options.login || await promptUsername()
const password = options.password || await promptPassword()
return new appClass(login, password, options.url)
}
async function main() {
try {
const app = new selectApplication()(options.login || await promptUsername(), options.password || await promptPassword(), options.url)
const result = await app.login()
const { code } = result
if (code === 'SUCCESS') {
await app.inputHandler()
await app.logout()
}
const app = await selectApplication()
await app.login()
await app.inputHandler()
await app.logout()
} catch (error) {
logError(error)
}
}
; (async () => await main())()
;(async () => await main())()

View File

@@ -1,4 +1,4 @@
const ENM = require('../ENM/ENM')
const ENM = require('../../components/ENM')
const { getProjects, getProjectData, deleteProject, newProject } = require('./projects')
const {
@@ -23,8 +23,7 @@ class AutoProvisioning extends ENM {
this.projectIndex = -1
this.nodes = null
this.nodeIndex = -1
this.commands = null
this.choices = null
this.prompt = ''
this.help = 'No results...'
}

View File

@@ -8,32 +8,32 @@ const { isEmpty } = require('../../../util/validation')
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'))
async function commandOther(prvn, prompt, command) {
async function commandOther(prvn, command) {
const choosedIndex = prvn.choices.indexOf(command)
if (choosedIndex !== -1) {
if (prvn.nodes) {
prvn.nodeIndex = choosedIndex
prompt = prvn.getNode()
prvn.getNode()
} else {
prvn.projectIndex = choosedIndex
prompt = await prvn.getProjectData()
await prvn.getProjectData()
}
}
return prompt
}
async function handleCommand(prvn, prompt, command) {
async function handleCommand(prvn, command) {
const [, cmd] = command.match(/\[(\w+)\]/) || [, command]
switch (cmd) {
case 'exit':
return
prvn.prompt = ''
break
case 'new':
await prvn.newProject()
break
case 'back':
prompt = prvn.nodeIndex ? await prvn.getProjects() : await prvn.getProjectData()
prvn.nodeIndex ? await prvn.getProjects() : await prvn.getProjectData()
break
case 'delete':
prvn.nodeIndex ? prvn.deleteNode() : await prvn.deleteProject()
@@ -62,32 +62,30 @@ async function handleCommand(prvn, prompt, command) {
break
default:
prompt = await commandOther(prvn, prompt, command)
await commandOther(prvn, command)
}
return prompt
}
async function inputHandlerLoop(prvn) {
let prompt = await prvn.getProjects()
let prefix = ''
async function inputHandler() {
await this.getProjects()
while (true) {
try {
const input = await inquirer.prompt([
{
type: 'autocomplete',
name: 'command',
message: chalk.bold.blue(prompt),
message: chalk.bold.blue(this.prompt),
pageSize: 10,
prefix: chalk.bold.grey(prefix),
prefix: '',
suffix: chalk.bold.blue('>'),
validate: isEmpty,
source: async (answers, input) => await prvn.next(input),
emptyText: prvn.help,
source: async (answers, input) => await this.next(input),
emptyText: this.help,
}
])
prompt = await handleCommand(prvn, prompt, input.command)
if (!prompt) break
await handleCommand(this, input.command)
if (!this.prompt) break
} catch (error) {
logError(error)
}
@@ -95,13 +93,4 @@ async function inputHandlerLoop(prvn) {
}
async function inputHandler() {
try {
await inputHandlerLoop(this)
} catch (error) {
logError(error)
}
}
module.exports = inputHandler

View File

@@ -35,7 +35,7 @@ async function getNode() {
this.commands = nodeCommands
this.help = nodeCommandsHelp.join('\n ')
this.choices = [] // this.nodes.filter(item => item !== nodeId)
return `${projectId} (${nodeId}) `
this.prompt = `${projectId} (${nodeId}) `
}

View File

@@ -34,7 +34,7 @@ async function getProjects() {
this.commands = projectsCommands
this.help = projectsCommandsHelp.join('\n ')
this.nodes = null
return `${this.projects.length} projects`
this.prompt = `${this.projects.length} projects`
}
@@ -55,7 +55,7 @@ async function getProjectData() {
this.commands = projectCommands
this.help = projectCommandsHelp.join('\n ')
this.nodeIndex = -1
return projectId
this.prompt = projectId
}

View File

@@ -0,0 +1,154 @@
const logAttributes = require('../../../util/logAttributes')
const inputHandler = require('./inputHandler')
const alarms = require('./commands/alarms')
const sync = require('./commands/sync')
const initialPrompt = require('./commands/initialPrompt')
const nextObjects = require('./commands/nextObjects')
const nextAttributes = require('./commands/nextAttributes')
const setIdByCommand = require('./commands/setIdByCommand')
const show = require('./commands/show')
const up = require('./commands/up')
const config = require('./commands/config')
const end = require('./commands/end')
const setAttribute = require('./commands/setAttribute')
const description = require('./commands/description')
const get = require('./commands/get')
const set = require('./commands/set')
const commit = require('./commands/commit')
const home = require('./commands/home')
const search = require('./commands/search')
const goToFdn = require('./commands/goToFdn')
const createNext = require('../../../util/createNext')
const { mainHelp, configHelp } = require('./help')
const ENM = require('../../components/ENM')
const chalk = require('chalk')
class TopologyBrowser extends ENM {
constructor(username, password, url) {
super(username, password, url)
this.objectUrl = '/persistentObject/'
this.alarmUrl = '/alarmcontroldisplayservice/alarmMonitoring/alarmoperations/'
this.nodeTypesUrl = '/modelInfo/model/nodeTypes/'
this.nodeSearchUrl = '/managedObjects/search/v2'
this.nodePoIdsUrl = '/managedObjects/getPosByPoIds'
this.currentPoId = 0
this.nextPoId = 1
this.fdn = ''
this.childrens = []
this.poIds = []
this.isConfig = false
this.attributes = []
this.nextVariants = null
this.attributesData = []
this.attribute = null
this.networkDetails = null
this.configSet = []
this.includeNonPersistent = false
this.configCommands = ['commit', 'check', 'end', 'exit']
this.help = mainHelp
}
async initialPrompt() {
await initialPrompt.call(this)
}
async next(input) {
await this.nextVariants(input)
return createNext.call(this, input ? input : '')
}
async nextObjects(input) {
return await nextObjects.call(this, input)
}
async nextAttributes(input) {
return await nextAttributes.call(this, input)
}
setIdByCommand(command) {
return setIdByCommand.call(this, command)
}
async show(filter) {
await show.call(this, filter)
}
up() {
return up.call(this)
}
async config(fdn) {
this.help = configHelp
return await config.call(this, fdn)
}
end() {
this.help = mainHelp
end.call(this)
}
setAttribute(attribute) {
return setAttribute.call(this, attribute)
}
description() {
description.call(this)
}
get(fdn) {
get.call(this, fdn)
}
async set() {
await set.call(this)
}
async commit(fdn) {
return await commit.call(this, fdn)
}
check() {
logAttributes(this.fdn.replace(/\((\w+)\)/g, ''), this.configSet)
}
home() {
home.call(this)
}
async search() {
await search.call(this)
}
async goToFdn(fromFdn, targetFdn) {
return await goToFdn.call(this, fromFdn, targetFdn)
}
persistent() {
this.includeNonPersistent = !this.includeNonPersistent
console.log(chalk.yellow(`Include Non Persistent Atrributes Set to: ${this.includeNonPersistent}`))
}
async alarms(fdn) {
await alarms.call(this, fdn)
}
async sync(fdn) {
await sync.call(this, fdn)
}
async inputHandler() {
await inputHandler.call(this)
}
getPrompt() {
if (this.fdn.length >= 67) {
return `...${this.fdn.slice(-65)}`
}
return this.fdn
}
}
module.exports = TopologyBrowser

View File

@@ -1,19 +1,18 @@
const colors = require('colors')
const chalk = require('chalk')
const inquirer = require('inquirer')
const requestWrapper = require('../../util/requestWrapper')
const logAlarm = require('../../util/logAlarm')
const eventTimeToString = require('../../util/eventTimeToString')
const logAlarm = require('../../../../util/logAlarm')
const eventTimeToString = require('../../../../util/eventTimeToString')
const closeAlarms = { name: 'Close Alarms'.yellow, value: -1}
const closeAlarms = { name: chalk.yellow('Close Alarms'), value: -1}
function logTotal(total) {
if (total === 0) {
console.log(`Total Alarms: ${total}`.green)
console.log(chalk.green(`Total Alarms: ${total}`))
return
}
console.log(`Total Alarms: ${total}`.yellow)
console.log(chalk.yellow(`Total Alarms: ${total}`))
}
@@ -36,6 +35,7 @@ function parsePoIdResponse(response) {
async function getPoIds(url, nodes) {
const axiosConfig = {
text: 'Getting Alarms...',
method: 'post',
url,
data: {
@@ -54,7 +54,7 @@ async function getPoIds(url, nodes) {
advFilters: ''
}
}
const response = await requestWrapper(axiosConfig, 'Getting Alarms...')
const response = await this.httpClient.request(axiosConfig)
if (!Array.isArray(response.data)) return
const [total, eventPoIds] = parsePoIdResponse(response)
logTotal(total)
@@ -64,6 +64,7 @@ async function getPoIds(url, nodes) {
async function getFields(url, eventPoIds) {
const axiosConfig = {
text: 'Getting Alarms Data...',
method: 'post',
url,
data: {
@@ -74,12 +75,12 @@ async function getFields(url, eventPoIds) {
category: 'All'
}
}
const response = await requestWrapper(axiosConfig, 'Getting Alarms Data...')
const response = await this.httpClient.request(axiosConfig)
return response.data
}
async function alarmChoices(alarmList, input) {
function alarmChoices(alarmList, input) {
const filter = input ? input : ''
return alarmList
.map(al => {
@@ -102,7 +103,7 @@ async function alarmsLoop(alarmList) {
name: 'alarm',
message: 'Select Alarm:',
pageSize: 10,
source: async (answers, input) => await alarmChoices(alarmList, input)
source: async (answers, input) => alarmChoices(alarmList, input)
}
])
if (input.alarm === closeAlarms.value) break
@@ -111,15 +112,14 @@ async function alarmsLoop(alarmList) {
}
async function alarms(fdn) {
const meContextFind = fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
async function alarms() {
const meContextFind = this.fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
if (!meContextFind) {
console.log('No alarming object in FDN!'.yellow)
return
throw new Error('No alarming object in FDN!')
}
const eventPoIds = await getPoIds(`${this.alarmUrl}eventpoids`, meContextFind[2])
const eventPoIds = await getPoIds.call(this, `${this.alarmUrl}eventpoids`, meContextFind[2])
if (!eventPoIds) return
const alarmList = await getFields(`${this.alarmUrl}getalarmlist/fields`, eventPoIds)
const alarmList = await getFields.call(this, `${this.alarmUrl}getalarmlist/fields`, eventPoIds)
await alarmsLoop(alarmList)
}

View File

@@ -0,0 +1,35 @@
const logAttributes = require('../../../../util/logAttributes')
const logCommit = require('../../../../util/logCommit')
async function commit() {
if (!this.configSet.length) {
throw new Error('Configuration is empty❗')
}
this.fdn = this.fdn.replace(/\((\w+)\)/g, '')
logAttributes(this.fdn, this.configSet)
const axiosConfig = {
text: 'Commiting Config...',
method: 'put',
url: `${this.objectUrl}${this.currentPoId}`,
data: {
poId: this.currentPoId,
fdn: this.fdn,
attributes: this.configSet,
},
headers: {
'Content-Type': 'application/json'
}
}
const response = await this.httpClient.request(axiosConfig)
if (response.data) {
logCommit(response.data)
} else {
throw new Error('No data or response❗')
}
this.configSet.length = 0
this.end()
}
module.exports = commit

View File

@@ -1,10 +1,10 @@
const requestWrapper = require('../../util/requestWrapper')
const logDetails = require('../../util/logDetails')
const logDetails = require('../../../../util/logDetails')
async function config(fdn) {
async function config() {
this.isConfig = true
const axiosConfig = {
text: 'Reading Attributes...',
method: 'get',
url: `${this.objectUrl}${this.currentPoId}`,
params: {
@@ -12,27 +12,26 @@ async function config(fdn) {
stringifyLong: true
}
}
const responseA = await requestWrapper(axiosConfig, 'Reading Attributes...')
const responseA = await this.httpClient.request(axiosConfig)
if (!responseA.data.attributes) {
console.log('Can\'t read attributes'.red)
return fdn
throw new Error('Can\'t read attributes')
}
const { attributes, namespace, namespaceVersion, neType, neVersion, networkDetails, type} = responseA.data
axiosConfig.text = 'Reading Attributes Data...'
axiosConfig.url = `${this.objectUrl}model/${neType}/${neVersion}/${namespace}/${type}/${namespaceVersion}/attributes`
axiosConfig.params = {
includeNonPersistent: this.includeNonPersistent
}
const responseD = await requestWrapper(axiosConfig, 'Reading Attributes Data...')
const responseD = await this.httpClient.request(axiosConfig)
if (!responseD.data.attributes) {
console.log('Can\'t read attributes data'.red)
return fdn
throw new Error('Can\'t read attributes data')
}
this.networkDetails = networkDetails
logDetails(networkDetails)
this.attributes = attributes
this.nextVariants = async (input) => await this.nextAttributes(input)
this.attributesData = responseD.data.attributes
return `${fdn}(config)`
this.fdn = `${this.fdn}(config)`
}

View File

@@ -0,0 +1,21 @@
const chalk = require('chalk')
const logAttributeData = require('../../../../util/logAttributeData')
function description() {
const attributeData = this.attributesData.find(item => item.key === this.attribute)
if (attributeData) {
logAttributeData(attributeData)
if (attributeData.complexRef) {
console.log(`${chalk.magenta(attributeData.type)}
${chalk.cyan(attributeData.complexRef.key)}: ${chalk.grey(attributeData.complexRef.description)}
`)
attributeData.complexRef.attributes.forEach(attr => logAttributeData(attr))
}
} else {
throw new Error('Attribute Not Found❗')
}
}
module.exports = description

View File

@@ -1,7 +1,9 @@
function end() {
this.fdn = this.fdn.replace(/\((\w+)\)/g, '')
this.isConfig = false
this.attribute = null
this.configSet.length = 0
this.configCommands = this.configCommands.filter(cmd => !['get', 'set', 'description'].includes(cmd) )
this.nextVariants = async (input) => await this.nextObjects(input)
}

View File

@@ -0,0 +1,23 @@
const logAttributes = require('../../../../util/logAttributes')
const banner = require('../../../../util/banner')
const chalk = require('chalk')
function get() {
const syncStatus = this.networkDetails.find(item => item.key === 'syncStatus')
if (syncStatus && syncStatus.value === 'UNSYNCHRONIZED') {
console.log(chalk.yellow(`
${syncStatus.key}: ${syncStatus.value}`))
}
const attribute = this.attributes.find(item => item.key === this.attribute)
if (!attribute) {
throw new Error(`Attribute not Found: ${this.attribute}`)
}
const attributeData = this.attributesData.find(item => item.key === this.attribute)
logAttributes(this.fdn, [attribute])
console.log(` ${chalk.green('Type: ') + attributeData['type']} ${attributeData['defaultValue'] ? chalk.yellow('Default: ') + attributeData['defaultValue'] : ''}
`)
if (attributeData.constraints && attributeData.constraints.orderingConstraint) banner(attributeData)
}
module.exports = get

View File

@@ -1,28 +1,29 @@
const requestWrapper = require('../../util/requestWrapper')
async function fdn(fromFdn, targetFdn) {
async function goToFdn(targetFdn) {
if (!targetFdn) {
throw new Error('Valid FDN must be supplied❗')
}
const axiosConfig = {
text: 'Browsing to FDN...',
method: 'get',
url: `${this.objectUrl}fdn/${targetFdn}`,
}
const response1 = await requestWrapper(axiosConfig, 'Browsing to FDN...')
if (!response1.data.fdn) return fromFdn
const response1 = await this.httpClient.request(axiosConfig)
this.poIds.length = 0
const { fdn, poId } = response1.data
this.currentPoId = poId
this.nextPoId = poId
this.nextVariants = async (input) => await this.nextObjects(input)
axiosConfig.text = 'Building FDN path...'
axiosConfig.url = `${this.objectUrl}network/${this.currentPoId}/subTrees`
const response2 = await requestWrapper(axiosConfig, 'Building FDN path...')
const response2 = await this.httpClient.request(axiosConfig)
if (response2.data) {
if (response2.data.treeNodes.length > 1) {
response2.data.treeNodes.slice(0, -1).forEach((node) => {
this.poIds.push(node.poId)
})
}
this.childrens = null
this.childrens = []
}
return fdn
this.fdn = fdn
}
module.exports = fdn
module.exports = goToFdn

View File

@@ -1,4 +1,5 @@
function home() {
this.fdn = this.fdn.split(',', 1)[0]
this.nextPoId = this.poIds.shift()
this.poIds.length = 0
this.nextVariants = async (input) => await this.nextObjects(input)

View File

@@ -1,22 +1,21 @@
const colors = require('colors')
const requestWrapper = require('../../util/requestWrapper')
const chalk = require("chalk")
async function initialPrompt() {
const axiosConfig = {
text: 'Starting Topology Browser...',
method: 'get',
url: `${this.objectUrl}network/-1?relativeDepth=0:-2&childDepth=1`
}
const response = await requestWrapper(axiosConfig, 'Starting Topology Browser...')
const response = await this.httpClient.request(axiosConfig)
if (!response.data.treeNodes) {
console.log('Nothing in initial promt!'.red)
return
throw new Error('Nothing in initial promt‼')
}
const { moType, moName, poId } = response.data.treeNodes[0]
this.currentPoId = poId
this.nextPoId = poId
this.nextVariants = async (input) => await this.nextObjects(input)
return `${moType}=${moName}`
this.fdn = `${moType}=${moName}`
}

View File

@@ -0,0 +1,14 @@
async function nextAttributes(input) {
const filter = input ? input : ''
this.commands = this.configCommands
.filter(item => item.toLowerCase().includes(filter.toLowerCase()))
this.choices = this.attributes
.map(item => item.key)
.filter(item => item.toLowerCase().includes(filter.toLowerCase()))
.sort((a, b) => a > b ? 1 : -1)
}
module.exports = nextAttributes

View File

@@ -0,0 +1,57 @@
const chalk = require("chalk")
const logError = require('../../../../util/logError')
const otherCommands = ['show', 'config', 'up', 'home', 'fdn', 'search', 'persistent', 'alarms', 'sync', 'exit']
function getSyncStatus(child) {
if (child.syncStatus === 'SYNCHRONIZED') return '\t\t✅'
if (child.syncStatus === 'UNSYNCHRONIZED') return '\t\t⏳'
return child.syncStatus ? '\t\t❓' : ''
}
function getRadioAccessTechnology(child) {
return child.radioAccessTechnology ? ' ' + chalk.bold.cyan(child.radioAccessTechnology.join(' ')) : ''
}
async function networkRequest() {
try {
const axiosConfig = {
text: 'Loading network data...',
method: 'get',
url: `${this.objectUrl}network/${this.currentPoId}`
}
const { data: { treeNodes } } = await this.httpClient.request(axiosConfig)
if (treeNodes) {
this.childrens = treeNodes[0].childrens
}
} catch (error) {
logError(error)
}
}
async function nextObjects(input){
const filter = input ? input : ''
if (this.currentPoId !== this.nextPoId || this.childrens.length === 0) {
this.currentPoId = this.nextPoId
this.poIds.push(this.currentPoId)
await networkRequest.call(this)
}
this.commands = otherCommands.filter(cmd => cmd.toLowerCase().includes(filter.toLowerCase()))
this.choices = this.childrens
.map(child => {
const st = getSyncStatus(child)
const rt = getRadioAccessTechnology(child)
const ne = child.neType ? ' ' + chalk.dim.gray(child.neType) : ''
return `${child.moType}=${child.moName}${st}${rt}${ne}`
})
.filter(child => child.toLowerCase().includes(filter.toLowerCase()))
.concat(filter.startsWith('show') ? [filter] : [])
.concat(filter.startsWith('fdn') ? [filter] : [])
}
module.exports = nextObjects

View File

@@ -0,0 +1,127 @@
const chalk = require('chalk')
const inquirer = require('inquirer')
const { isValidNodeName } = require('../../../../util/validation')
async function getNodeTypes() {
const axiosConfig = {
text: 'Getting Node Types...',
method: 'get',
url: this.nodeTypesUrl,
}
const response = await this.httpClient.request(axiosConfig)
return response.data
}
async function getPoIds(query) {
const axiosConfig = {
text: 'Searching Nodes...',
method: 'get',
url: this.nodeSearchUrl,
headers: {
'X-Netex-Scoping-Panel': true,
},
params: {
query,
orderby: 'moName',
orderdirection: 'asc',
}
}
const { data: { objects } } = await this.httpClient.request(axiosConfig)
return objects.map(item => item.id)
}
async function getNodesData(poList) {
const axiosConfig = {
text: 'Getting Nodes Data...',
method: 'post',
url: this.nodePoIdsUrl,
data: {
poList,
defaultMappings: ["syncStatus","managementState","radioAccessTechnology","parentNeType"],
attributeMappings:[{"moType":"MeContext","attributeNames":["neType"]}]
}
}
const response = await this.httpClient.request(axiosConfig)
return response.data
}
function resultOutput(result) {
console.log('')
result.forEach(item => {
const {
fdn,
attributes,
cmSyncStatus,
radioAccessTechnology,
} = item
console.log(`${cmSyncStatus === 'SYNCHRONIZED' ? '✅' : '⏳'} ${fdn} ${chalk.dim.grey(attributes.neType)} ${chalk.bold.cyan(radioAccessTechnology.join(' '))}`)
})
console.log('')
}
function filterTypes(input, nodeTypes){
const filter = input ? input : ''
return nodeTypes.filter(
item => item
.toLowerCase()
.includes(filter.toLowerCase())
)
}
function buildQuery(nodeName, nodeType, rat) {
const ratFilter = rat.length !== 0
? ` filter by radioAccessTechnology ${rat.length === 1 ? 'containing ' : 'contains any of '}${rat.join(',')}`
: ''
const typeFilter = nodeType === 'ALL'
? `with name = ${nodeName}`
: `of type ${nodeType} where name = ${nodeName}`
return `select all nodes ${nodeName && typeFilter}${ratFilter}`
}
async function search() {
const nodeTypes = await getNodeTypes.call(this)
nodeTypes.push('ALL')
const { nodeName, nodeType, rat } = await inquirer.prompt([
{
type: 'autocomplete',
name: 'nodeType',
message: 'Select Node Type (default ALL):',
pageSize: 10,
default: 'ALL',
source: async (answers, input) => filterTypes(input, nodeTypes)
},
{
type: 'checkbox',
name: 'rat',
message: 'Select Radio Access Technologies (default all):',
choices: [
{name: '2G'},
{name: '3G'},
{name: '4G'},
{name: '5G'},
]
},
{
type: 'input',
name: 'nodeName',
message: 'Type a Node Name:',
validate: isValidNodeName
}
])
const poList = await getPoIds.call(this, buildQuery(nodeName, nodeType, rat))
if (poList.length === 0) {
throw new Error('Nodes not found❗')
}
const result = await getNodesData.call(this, poList)
resultOutput(result)
}
module.exports = search

View File

@@ -1,16 +1,14 @@
const colors = require('colors')
const inputByType = require('../inputValue')
async function set() {
const attributeData = this.attributesData.filter(item => item.key === this.attribute)[0]
const attributeData = this.attributesData.find(item => item.key === this.attribute)
if (!attributeData) return
if (attributeData.writeBehavior === 'NOT_ALLOWED' || attributeData.immutable) {
console.log('Attribute Is ReadOnly'.yellow)
return
throw new Error('Attribute Is ReadOnly')
}
if (this.isConfig) {
const found = this.configSet.filter(item => item.key === this.attribute)[0]
const found = this.configSet.find(item => item.key === this.attribute)
const { value } = await inputByType(attributeData)
if (found) {
found.value = value

View File

@@ -0,0 +1,12 @@
function setAttribute(attribute) {
if (!this.attributes) return false
if (!this.attributes.find(item => item.key === attribute)) return false
if (!this.attribute) {
['get', 'set', 'description'].forEach(cmd => this.configCommands.push(cmd))
}
this.attribute = attribute
return true
}
module.exports = setAttribute

View File

@@ -1,5 +1,5 @@
function setIdByCommand(command) {
const nextChild = this.childrens.filter(child => `${child.moType}=${child.moName}` === command)[0]
const nextChild = this.childrens.find(child => `${child.moType}=${child.moName}` === command)
if (nextChild) {
this.nextPoId = nextChild.poId
return true

View File

@@ -1,9 +1,9 @@
const requestWrapper = require('../../util/requestWrapper')
const logAttributes = require('../../util/logAttributes')
const logAttributes = require('../../../../util/logAttributes')
async function show(filter) {
const axiosConfig = {
text: 'Getting Atrributes...',
method: 'get',
url: `${this.objectUrl}${this.currentPoId}`,
params: {
@@ -11,7 +11,7 @@ async function show(filter) {
stringifyLong: true
}
}
const response = await requestWrapper(axiosConfig)
const response = await this.httpClient.request(axiosConfig)
if (!response.data.fdn || !response.data.attributes) return
logAttributes(response.data.fdn, response.data.attributes.filter(item => item.key.match(filter)))
}

View File

@@ -0,0 +1,26 @@
const chalk = require("chalk")
async function sync() {
const meContextFind = this.fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
if (!meContextFind) {
throw new Error('No sync object in FDN!')
}
const actionUrl = `${this.objectUrl}v1/perform-mo-action/NetworkElement=${meContextFind[2]},CmFunction=1?actionName=sync`
const axiosConfig = {
text: 'Initiate Node Sync...',
method: 'post',
url: actionUrl,
headers: {
'Content-Type': 'application/json'
},
}
const response = await this.httpClient.request(axiosConfig)
console.log(`
${chalk.bold(response.data.body)}
${chalk.green(response.data.title)}
`)
}
module.exports = sync

View File

@@ -3,9 +3,10 @@ function up() {
this.poIds.pop()
this.nextPoId = this.poIds.pop()
this.currentPoId = this.poIds[this.poIds.length - 1]
return true
this.fdn = this.fdn.split(',').slice(0, -1).join(',')
return
}
return false
throw new Error('There\'s no way up!')
}

View File

@@ -0,0 +1,25 @@
const mainHelp = [
'',
'show [<valid regex>] - shows current object\'s attributes filtered with regex',
'config - enters config mode',
'up - navigate up one level',
'fdn [<valid FDN>] - navigate to FDN',
'home - navigate to root folder',
'alarms - show alarms',
'sync - initiate node CM synchronization',
'persistent - toggle persistent attributes inclusion',
'exit - logout and exit application',
].join('\n')
const configHelp = [
'',
'set - set attribute\'s value',
'get - get attribute\'s value',
'commit - commiting changes to the network',
'check - view configuration changes',
'end - exit config mode without commiting',
'exit - logout and exit application',
].join('\n')
module.exports = { mainHelp, configHelp }

View File

@@ -0,0 +1,109 @@
const inquirer = require('inquirer')
const chalk = require('chalk')
const logError = require('../../../util/logError')
const { isEmpty } = require('../../../util/validation')
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'))
function commandOther(tplg, command) {
const spl = command.split(/\s+/).find(item => item)
if (tplg.setIdByCommand(spl)) {
tplg.fdn = `${tplg.fdn},${spl}`
} else if (tplg.setAttribute(command)) {
tplg.fdn = tplg.fdn.replace(/\((\w+)\)/g, `(${command})`)
} else {
throw new Error('Command Unrecognized❗')
}
}
async function handleCommand(tplg, command) {
const [cmd, param] = command.split(/\s+/)
const cmdMatch = cmd.match(/\[(\w+)\]/)
const cmdName = cmdMatch ? cmdMatch[1] : cmd
switch (cmdName) {
case 'exit':
tplg.fdn = ''
break
case 'show':
await tplg.show(param ? param.trim() : '')
break
case 'config':
await tplg.config()
break
case 'set':
await tplg.set()
break
case 'commit':
await tplg.commit()
break
case 'up':
tplg.up()
break
case 'get':
tplg.get()
break
case 'check':
tplg.check()
break
case 'end':
tplg.end()
break
case 'home':
tplg.home()
break
case 'search':
await tplg.search()
break
case 'description':
tplg.description()
break
case 'persistent':
tplg.persistent()
break
case 'fdn':
await tplg.goToFdn(param ? param.trim() : '')
break
case 'alarms':
await tplg.alarms()
break
case 'sync':
await tplg.sync()
break
default:
commandOther(tplg, command)
}
}
async function inputHandler() {
await this.initialPrompt()
while (true) {
try {
const input = await inquirer.prompt([
{
type: 'autocomplete',
name: 'command',
message: chalk.blue(this.getPrompt()),
pageSize: 10,
prefix: '',
suffix: this.isConfig ? chalk.blue('#') : chalk.blue('>'),
validate: isEmpty,
source: async (answers, input) => await this.next(input),
emptyText: this.help,
}
])
await handleCommand(this, input.command)
if (!this.fdn) break
} catch (error) {
logError(error)
}
}
}
module.exports = inputHandler

View File

@@ -1,16 +1,16 @@
const colors = require('colors')
const chalk = require('chalk')
const inquirer = require('inquirer')
const banner = require('../util/banner')
const { isValidNumber, isValidString, checkValueRangeConstraints } = require('../util/validation')
const banner = require('../../../util/banner')
const { isValidNumber, isValidString, checkValueRangeConstraints } = require('../../../util/validation')
async function inputInteger(attributeData) {
const message = `${attributeData.key.yellow} { ${attributeData.unit ? attributeData.unit : 'parrots'.gray} } (${attributeData.type}): `
const message = `${chalk.yellow(attributeData.key)} { ${attributeData.unit ? attributeData.unit : chalk.gray('parrots')} } (${attributeData.type}): `
const input = await inquirer.prompt([
{
type: 'input',
name: 'value',
suffix: '?'.green,
suffix: chalk.green('?'),
message,
default: attributeData.defaultValue,
validate: input => isValidNumber(input, attributeData.constraints),
@@ -21,12 +21,12 @@ async function inputInteger(attributeData) {
async function inputEnumRef(attributeData) {
const message = `Select Value For ${attributeData.key.yellow}: `
const message = `Select Value For ${chalk.yellow(attributeData.key)}: `
const input = await inquirer.prompt([
{
type: 'list',
name: 'value',
suffix: '?'.green,
suffix: chalk.green('?'),
message,
choices: attributeData.enumeration.enumMembers.map(item => ({ name: `${item.key} (${item.description})`, value: item.key, short: item.key })),
default: attributeData.defaultValue,
@@ -39,12 +39,12 @@ async function inputEnumRef(attributeData) {
async function inputBoolean(attributeData) {
const variants = ['true', 'false']
if (attributeData.constraints.nullable) variants.push('null')
const message = `Select Value For ${attributeData.key.yellow}:`
const message = `Select Value For ${chalk.yellow(attributeData.key)}:`
const input = await inquirer.prompt([
{
type: 'list',
name: 'value',
suffix: '?'.green,
suffix: chalk.green('?'),
message,
choices: variants,
default: String(attributeData.defaultValue),
@@ -59,12 +59,12 @@ async function inputBoolean(attributeData) {
async function inputString(attributeData) {
const message = `${attributeData.key.yellow} (${attributeData.type}): `
const message = `${chalk.yellow(attributeData.key)} (${attributeData.type}): `
const input = await inquirer.prompt([
{
type: 'input',
name: 'value',
suffix: '?'.green,
suffix: chalk.green('?'),
message,
default: attributeData.defaultValue,
validate: input => isValidString(input, attributeData.constraints),
@@ -75,13 +75,13 @@ async function inputString(attributeData) {
async function inputList(attributeData) {
const message = `${attributeData.key.yellow} List Of (${attributeData.listReference.type}) Size: `
const message = `${chalk.yellow(attributeData.key)} List Of (${attributeData.listReference.type}) Size: `
const result = []
const { value } = await inquirer.prompt([
{
type: 'number',
name: 'value',
suffix: '?'.green,
suffix: chalk.green('?'),
message,
default: 'null',
validate: input => +input > 0 || input === 'null',
@@ -108,7 +108,7 @@ async function inputListValues(attributeData, listSize, result) {
}
if (attributeData.constraints.uniqueMembers) {
if (result.indexOf(input.value) !== -1) {
console.log('>>Array Values Should Be Unique'.red)
console.log(chalk.red('>>Array Values Should Be Unique'))
i--
continue
}
@@ -138,6 +138,7 @@ async function inputByType(typeReference) {
const inputs = {
INTEGER: inputInteger,
SHORT: inputInteger,
LONG: inputInteger,
ENUM_REF: inputEnumRef,
BOOLEAN: inputBoolean,
STRING: inputString,

View File

@@ -1,9 +1,9 @@
const axios = require('axios')
const chalk = require('chalk')
const https = require('https')
const axiosCookieJarSupport = require('axios-cookiejar-support').default
const tough = require('tough-cookie')
const SpinnerWithCounter = require('../../../util/SpinnerWithCounter')
const logError = require('../../util/logError')
const SpinnerWithCounter = require('./SpinnerWithCounter')
axiosCookieJarSupport(axios)
@@ -20,6 +20,10 @@ function beforeRequest(config) {
function errorRequest(error) {
spinner.fail()
if (401 === error.response.status) {
logError(error)
process.exit(1)
}
return Promise.reject(error)
}

5
lib/components/ENM/ENM.js → lib/components/ENM.js Normal file → Executable file
View File

@@ -1,10 +1,11 @@
const chalk = require('chalk')
const axiosHttpClient = require('../AxiosHttpClient/AxiosHttpClient')
const axiosHttpClient = require('./AxiosHttpClient')
class ENM {
constructor(username, password, url) {
this.logoutUrl = '/logout'
this.loginUrl = `/login?IDToken1=${username}&IDToken2=${password}`
this.commands = null
this.choices = null
this.httpClient = axiosHttpClient(url)
}

View File

View File

@@ -1,132 +0,0 @@
const logAttributes = require('../util/logAttributes')
const inputHandler = require('./inputHandler')
const alarms = require('../commands/alarms')
const sync = require('../commands/sync')
const initialPrompt = require('../commands/initialPrompt')
const nextObjects = require('../commands/nextObjects')
const nextAttributes = require('../commands/nextAttributes')
const setIdByCommand = require('../commands/setIdByCommand')
const show = require('../commands/show')
const up = require('../commands/up')
const config = require('../commands/config')
const end = require('../commands/end')
const setAttribute = require('../commands/setAttribute')
const description = require('../commands/description')
const get = require('../commands/get')
const set = require('../commands/set')
const commit = require('../commands/commit')
const home = require('../commands/home')
const fdn = require('../commands/fdn')
const ENM = require('../ENM/ENM')
class TopologyBrowser extends ENM {
constructor(username, password, url) {
super(username, password, url)
this.objectUrl = '/persistentObject/'
this.alarmUrl = '/alarmcontroldisplayservice/alarmMonitoring/alarmoperations/'
this.currentPoId = 0
this.nextPoId = 1
this.childrens = null
this.poIds = []
this.isConfig = false
this.attributes = null
this.nextVariants = null
this.attributesData = null
this.attribute = null
this.networkDetails = null
this.configSet = []
this.includeNonPersistent = false
this.configCommands = ['commit', 'check', 'end', 'persistent', 'exit']
this.help = 'No results...'
}
async initialPrompt() {
return await initialPrompt.call(this)
}
async next(input) {
return await this.nextVariants(input)
}
async nextObjects(input) {
return await nextObjects.call(this, input)
}
async nextAttributes(input) {
return await nextAttributes.call(this, input)
}
setIdByCommand(command) {
return setIdByCommand.call(this, command)
}
async show(filter) {
await show.call(this, filter)
}
up() {
return up.call(this)
}
async config(fdn) {
return await config.call(this, fdn)
}
end() {
end.call(this)
}
setAttribute(attribute) {
return setAttribute.call(this, attribute)
}
description() {
description.call(this)
}
get(fdn) {
get.call(this, fdn)
}
async set() {
await set.call(this)
}
async commit(fdn) {
return await commit.call(this, fdn)
}
check(fdn) {
logAttributes(fdn, this.configSet)
}
home() {
home.call(this)
}
async fdn(fromFdn, targetFdn) {
return await fdn.call(this, fromFdn, targetFdn)
}
persistent() {
this.includeNonPersistent = !this.includeNonPersistent
console.log(`Include Non Persistent Atrributes Set to: ${this.includeNonPersistent}`.yellow)
}
async alarms(fdn) {
await alarms.call(this, fdn)
}
async sync(fdn) {
await sync.call(this, fdn)
}
async inputHandler() {
await inputHandler.call(this)
}
}
module.exports = TopologyBrowser

View File

@@ -1,34 +0,0 @@
const colors = require('colors')
const logAttributes = require('../../util/logAttributes')
const requestWrapper = require('../../util/requestWrapper')
const logCommit = require('../../util/logCommit')
async function commit(fdn) {
logAttributes(fdn, this.configSet)
this.configSet.forEach(item => delete item.from)
const axiosConfig = {
method: 'put',
url: `${this.objectUrl}${this.currentPoId}`,
data: {
poId: this.currentPoId,
fdn,
attributes: this.configSet,
},
headers: {
'Content-Type': 'application/json'
}
}
const response = await requestWrapper(axiosConfig, 'Commiting Config...')
if (response.data) {
logCommit(response.data)
} else {
console.log('No data or response!'.red)
}
this.configSet.length = 0
this.end()
return fdn
}
module.exports = commit

View File

@@ -1,21 +0,0 @@
const colors = require('colors')
const logAttributeData = require('../../util/logAttributeData')
function description() {
const attributeData = this.attributesData.filter(item => item.key === this.attribute)[0]
if (attributeData) {
logAttributeData(attributeData)
if (attributeData.complexRef) {
console.log(`${attributeData.type.magenta}
${attributeData.complexRef.key.cyan}: ${attributeData.complexRef.description.grey}
`)
attributeData.complexRef.attributes.forEach(attr => logAttributeData(attr))
}
} else {
console.log('Attribute Not Found!'.yellow)
}
}
module.exports = description

View File

@@ -1,20 +0,0 @@
const colors = require('colors')
const logAttributes = require('../../util/logAttributes')
const banner = require('../../util/banner')
function get(fdn) {
const syncStatus = this.networkDetails.filter(item => item.key === 'syncStatus')[0]
if (syncStatus && syncStatus.value === 'UNSYNCHRONIZED') {
console.log(`
${syncStatus.key}: ${syncStatus.value}`.yellow)
}
const attribute = this.attributes.filter(item => item.key === this.attribute)[0]
const attributeData = this.attributesData.filter(item => item.key === this.attribute)[0]
logAttributes(fdn, [attribute])
console.log(` ${'Type: '.green + attributeData['type']} ${attributeData['defaultValue'] ? 'Default: '.yellow + attributeData['defaultValue'] : ''}
`)
if (attributeData.constraints && attributeData.constraints.orderingConstraint) banner(attributeData)
}
module.exports = get

View File

@@ -1,14 +0,0 @@
async function nextAttributes(input) {
const filter = input ? input : ''
let result = this.attributes.map(item => item.key).sort((a, b) => a > b ? 1 : -1)
.concat(this.configCommands)
.filter(item => item.toLowerCase().includes(filter.toLowerCase()))
if (result.includes(filter)) {
result = result.filter(item => item !== filter)
result.unshift(filter)
}
return result
}
module.exports = nextAttributes

View File

@@ -1,34 +0,0 @@
const requestWrapper = require('../../util/requestWrapper')
const otherCommands = ['show', 'config', 'up', 'home', 'fdn', 'persistent', 'alarms', 'sync', 'exit']
async function nextObjects(input){
const filter = input ? input : ''
if (this.currentPoId !== this.nextPoId || !this.childrens) {
this.currentPoId = this.nextPoId
this.poIds.push(this.currentPoId)
const axiosConfig = {
method: 'get',
url: `${this.objectUrl}network/${this.currentPoId}`
}
const response = await requestWrapper(axiosConfig)
if (response.data.treeNodes) {
this.childrens = response.data.treeNodes[0].childrens
}
}
let result = this.childrens
.map(child => `${child.moType}=${child.moName}`)
.concat(otherCommands)
.filter(child => child.toLowerCase().includes(filter.toLowerCase()))
.concat(filter.startsWith('show') ? [filter] : [])
.concat(filter.startsWith('fdn') ? [filter] : [])
if (result.includes(filter)) {
result = result.filter(item => item !== filter)
result.unshift(filter)
}
return result
}
module.exports = nextObjects

View File

@@ -1,14 +0,0 @@
function setAttribute(attribute) {
if (!this.attributes) return false
if (!this.attributes.filter(item => item.key === attribute)[0]) return false
if (!this.attribute) {
this.configCommands.push('get')
this.configCommands.push('set')
this.configCommands.push('description')
}
this.attribute = attribute
return true
}
module.exports = setAttribute

View File

@@ -1,30 +0,0 @@
const colors = require('colors')
const requestWrapper = require('../../util/requestWrapper')
async function sync(fdn) {
const meContextFind = fdn.match(/(NetworkElement|MeContext)=([\w-]+),?/)
if (!meContextFind) {
console.log('No sync object in FDN!'.yellow)
return
}
const actionUrl = `${this.objectUrl}v1/perform-mo-action/NetworkElement=${meContextFind[2]},CmFunction=1?actionName=sync`
const axiosConfig = {
method: 'post',
url: actionUrl,
headers: {
'Content-Type': 'application/json'
},
}
const response = await requestWrapper(axiosConfig, 'Initiate Node Sync...')
if (response.status === 200) {
console.log(`
${response.data.body.bold}
${response.data.title.green}
`)
}
}
module.exports = sync

View File

@@ -1,136 +0,0 @@
const inquirer = require('inquirer')
const colors = require('colors')
const logError = require('../util/logError')
const { isEmpty } = require('../util/validation')
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'))
function buildPrompt(fdn) {
if (fdn.length >= 67) {
return { prefix: '...', prompt: fdn.slice(-65) }
}
return { prefix: '', prompt: fdn }
}
function commndUp(tplg, fdn) {
if (tplg.up()) {
fdn = fdn.split(',').slice(0, -1).join(',')
} else {
console.log('There\'s no way up!'.yellow)
}
return fdn
}
function commandOther(tplg, fdn, command) {
if (tplg.setIdByCommand(command)) {
fdn = `${fdn},${command}`
} else if (tplg.setAttribute(command)) {
fdn = fdn.replace(/\((\w+)\)/g, `(${command})`)
} else {
console.log('Command Unrecognized❗'.red)
}
return fdn
}
async function handleCommand(tplg, fdn, command) {
const [cmd, param] = command.split(/\s+/)
switch (cmd) {
case 'exit':
return
case 'show':
await tplg.show(param ? param.trim() : '')
break
case 'config':
fdn = await tplg.config(fdn)
break
case 'set':
await tplg.set()
break
case 'commit':
fdn = await tplg.commit(fdn.replace(/\((\w+)\)/g, ''))
break
case 'up':
fdn = commndUp(tplg, fdn)
break
case 'get':
tplg.get(fdn)
break
case 'check':
tplg.check(fdn.replace(/\((\w+)\)/g, ''))
break
case 'end':
tplg.end()
fdn = fdn.replace(/\((\w+)\)/g, '')
break
case 'home':
tplg.home()
fdn = fdn.split(',', 1)[0]
break
case 'description':
tplg.description()
break
case 'persistent':
tplg.persistent()
break
case 'fdn':
fdn = await tplg.fdn(fdn, param ? param.trim() : '')
break
case 'alarms':
await tplg.alarms(fdn)
break
case 'sync':
await tplg.sync(fdn)
break
default:
fdn = commandOther(tplg, fdn, command)
}
return fdn
}
async function inputHandlerLoop(tplg) {
let prompt = await tplg.initialPrompt()
let fdn = prompt
let prefix = ''
while (true) {
try {
const input = await inquirer.prompt([
{
type: 'autocomplete',
name: 'command',
message: tplg.isConfig ? prompt.blue.underline : prompt.blue,
pageSize: 10,
prefix: prefix.gray,
suffix: tplg.isConfig ? '#'.blue : '>'.blue,
validate: isEmpty,
source: async (answers, input) => await tplg.next(input),
emptyText: prvn.help,
}
])
fdn = await handleCommand(tplg, fdn, input.command)
if (!fdn) break
({ prefix, prompt } = buildPrompt(fdn))
} catch (error) {
logError(error)
}
}
}
async function inputHandler() {
try {
await inputHandlerLoop(this)
} catch (error) {
logError(error)
}
}
module.exports = inputHandler

0
package.json Normal file → Executable file
View File

11
util/banner.js Executable file
View File

@@ -0,0 +1,11 @@
function banner(params) {
console.log(`
If you see this, pls, send me this message:
${JSON.stringify(params, null, 2)}
If you see this, pls, send me this message:
`)
}
module.exports = banner

2
util/createNext.js Normal file → Executable file
View File

@@ -6,10 +6,10 @@ function createNext(filter) {
const commands = this.commands.filter(cmd => cmd.toLowerCase().includes(filter.toLowerCase()))
const choices = this.choices.filter(choice => choice.toLowerCase().includes(filter.toLowerCase()))
return [
...choices,
separator,
...commands.map(cmd => `[${cmd}]`),
separator,
...choices,
]
}

10
util/eventTimeToString.js Executable file
View File

@@ -0,0 +1,10 @@
function eventTimeToString(eventTime) {
if (!eventTime) return ''
return new Date(eventTime).toISOString().slice(0, -1).split('T').join(' ')
// return `${eventDateTime.toLocaleDateString()} ${eventDateTime.toLocaleTimeString()}`
}
module.exports = eventTimeToString

26
util/logAlarm.js Executable file
View File

@@ -0,0 +1,26 @@
const chalk = require('chalk')
const eventTimeToString = require('./eventTimeToString')
const timeValues = [
'eventTime',
'insertTime',
'ceaseTime',
'ackTime',
]
function logAlarm(alarmList, eventPoId) {
const alarm = alarmList.filter(item => item.eventPoIdAsLong === eventPoId)[0]
timeValues.forEach(value => alarm[value] = eventTimeToString(alarm[value]))
console.log(
JSON.stringify(alarm, null, 2)
.replace(/["(){}\[\]]/mg, '')
.replace(/,$/mg, '')
.replace(/^(\s{2}\w+):/mg, chalk.green('$1:'))
)
}
module.exports = logAlarm

84
util/logAttributeData.js Executable file
View File

@@ -0,0 +1,84 @@
const chalk = require('chalk')
function logDefaultValue(value) {
return value ? ` default: ${value}` : ''
}
function logAttribute(key, attribute, output) {
let attrName = key.replace(/([A-Z])/g, ' $1')
if (attribute !== undefined && attribute !== '') {
output.push(`${chalk.blue(attrName.toLocaleUpperCase())}
${attribute}
`)
}
}
function logConstraints(constraints, output) {
output.push(`${chalk.blue(Object.keys({constraints}).pop().toLocaleUpperCase())}`)
if (constraints.valueRangeConstraints) {
constraints.valueRangeConstraints.forEach(item => {
output.push(` ${chalk.yellow('Range')}: ${item.minValue}..${item.maxValue}`)
})
}
['nullable', 'validContentRegex', 'valueResolution'].forEach(key => {
if (Object.keys(constraints).includes(key)) {
output.push(` ${chalk.yellow(key.replace(/([A-Z])/g, ' $1').replace(/^([a-z])/g, (l) => l.toUpperCase()))}: ${constraints[key]}`)
}
})
}
function logEnumeration(enumeration, output) {
output.push(`${chalk.blue(Object.keys({enumeration}).pop().toLocaleUpperCase())}
${chalk.cyan(enumeration.key)}
${enumeration.description}`)
enumeration.enumMembers.forEach(item => output.push(` ${chalk.yellow(item.key)} (${item.value}): -- ${chalk.gray(item.description)}`))
}
function logList(listReference, output) {
output.push(`${chalk.blue(Object.keys({listReference}).pop().toLocaleUpperCase())}
${listReference.type}`)
if (listReference.constraints){
logConstraints(listReference.constraints, output)
}
}
function logAttributeData(attributeData) {
const attributeDataKeys = [
'key',
'type',
'defaultValue',
'description',
'trafficDisturbances',
'unit',
'multiplicationFactor',
'immutable',
'precondition',
'dependencies',
'sideEffects',
'activeChoiceCase',
]
const output = [`
${chalk.yellow.bold(attributeData['key'])}: ${chalk.green(attributeData['type'])} ${logDefaultValue(attributeData['defaultValue'])}
`]
attributeDataKeys.slice(3).forEach((key) => logAttribute(key, attributeData[key], output))
if (attributeData.constraints) {
logConstraints(attributeData.constraints, output)
}
if (attributeData.enumeration) {
logEnumeration(attributeData.enumeration, output)
}
if (attributeData.listReference) {
logList(attributeData.listReference, output)
}
console.log(output.join('\n') + '\n')
}
module.exports = logAttributeData

34
util/logAttributes.js Executable file
View File

@@ -0,0 +1,34 @@
const chalk = require('chalk')
function transformAttributes(element) {
if (Array.isArray(element)) {
return element.map(item => transformAttributes(item))
}
if (Array.isArray(element.value)) {
return { [element.key]: transformAttributes(element.value) }
}
return element.key ? { [element.key]: element.value } : element
}
function colorize(attributes) {
const sorted = attributes.sort ? attributes.sort((a, b) => a.key < b.key ? -1 : 1) : attributes
return JSON.stringify(transformAttributes(sorted), null, 1)
.replace(/["(){}\[\]]/mg, '')
.replace(/^\s*,*\n/mg, '')
.replace(/,$/mg, '')
.replace(/^(\s{2}\w+):/mg, chalk.green('$1:'))
.replace(/^(\s{4}\w+):/mg, chalk.yellow('$1:'))
.replace(/^(\s{5}\w+):/mg, chalk.cyan('$1:'))
}
function logAttributes(fdn, attributes) {
const output = `
${chalk.yellow.bold('FDN')}: ${chalk.bold(fdn)}
${colorize(attributes)}`
console.log(output)
}
module.exports = logAttributes

10
util/logCommit.js Executable file
View File

@@ -0,0 +1,10 @@
const chalk = require('chalk')
function logCommit(commitResult) {
if (commitResult.title === 'Success') {
console.log(chalk.green(commitResult.title))
}
}
module.exports = logCommit

11
util/logDetails.js Executable file
View File

@@ -0,0 +1,11 @@
const chalk = require('chalk')
function logDetails(networkDetails) {
const output = networkDetails.map(details => ` ${chalk.gray(details.key)}: ${details.value === 'UNSYNCHRONIZED' ? '⌛ ' + chalk.yellow(details.value): chalk.gray(details.value)}`)
console.log(`
${output.join('\n')}
`)
}
module.exports = logDetails

66
util/logError.js Normal file → Executable file
View File

@@ -1,27 +1,51 @@
const chalk = require('chalk')
function logError(err) {
if (err.response && err.response.data && err.response.data.errorTitle) {
const {
errorTitle = 'Error',
errorBody = 'No error body',
errorDetails = null
} = err.response.data
console.log(`
⚠️ ${chalk.bold.bgRed(errorTitle)}
${chalk.yellow(errorBody || '')}${errorDetails ? '\n' + errorDetails.toString() : ''}
`)
} else {
const {
name = 'Error',
message = 'No error message',
stack = null,
} = err
console.log(`
${chalk.bold.bgRed(name)}
${chalk.yellow(message)}
${chalk.dim(stack && process.env.NODE_ENV === 'development' ? stack : '')}
`)
try {
if (!err.response) {
const {
name = 'Error',
message = 'No error message',
stack = null,
} = err
console.log(`
${chalk.bold.bgRed(name)}
${chalk.yellow(message)}${chalk.dim(stack && process.env.NODE_ENV === 'development' ? '\n' + stack : '')}
`)
return
}
if (err.response.data) {
const { data } = err.response
// other http error
let errorTitle = `${err.response.status}: ${err.response.statusText}`
let errorBody = data.code
let errorDetails = data.message
if (typeof data !== 'object') {
errorBody = data
}
if (data.errorCode) {
errorBody = `${data.errorCode}: ${data.userMessage.title}`
errorDetails = data.userMessage.body
}
// prvn error
if (data.errorTitle) {
errorTitle = data.errorTitle
errorBody = data.errorBody
errorDetails = data.errorDetails
}
// tplg error
if (data.title) {
errorTitle = `${data.errorCode}: ${data.title}`
errorBody = data.body
errorDetails = data.detail
}
console.log(`
⚠️ ${chalk.bold.bgRed(errorTitle)}
${chalk.yellow(errorBody)}${errorDetails ? '\n' + errorDetails.toString() : ''}
`)
}
} catch (error) {
console.log(error)
}
}

6
util/logNode.js Normal file → Executable file
View File

@@ -3,7 +3,7 @@ const chalk = require('chalk')
function logNodeStatus(statusEntries, indent = 4) {
if (!statusEntries) {
throw new Error('No node status entries!')
throw new Error('No node status entries')
}
console.log('')
statusEntries.forEach(entry => {
@@ -21,7 +21,7 @@ function logNodeStatus(statusEntries, indent = 4) {
function logNodeProperties(attributes, attributeGroups, indent = 4) {
if (!attributeGroups) {
throw new Error('No node attribute groups!')
throw new Error('No node attribute groups')
}
console.log('')
logAttributes(attributes)
@@ -37,7 +37,7 @@ function logNodeProperties(attributes, attributeGroups, indent = 4) {
function logAttributes(attributes, indent = 4) {
if (!attributes) {
throw new Error('No node attributes!')
throw new Error('No node attributes')
}
attributes.forEach(attribute => {
const { name, value } = attribute

0
util/logProject.js Normal file → Executable file
View File

55
util/validation.js Normal file → Executable file
View File

@@ -6,5 +6,58 @@ const isEmpty = input => (input === '' ? chalk.bgRed('Empty Inputs not Allowed')
const isValidHardwareId = input => (input.match(/[A-HJ-NP-Z0-9]{13}/) ? true : chalk.bgRed(`The Ericsson Hardware Serial Number frame consists of 13 alphanumeric characters.
Character set is the letters A-Z with the exception of O and I, and digits 0-9.`))
const isValidNumber = (input, constraints) => {
if (constraints) {
const test = input ? input : ''
if (!constraints.nullable && test === 'null') return chalk.red('Value Can\'t Be a null')
if (constraints.nullable && test === 'null') return true
if (!test.toString().match(/-?[0-9]+/)) return chalk.red('Input Is Not a Number')
const checkResult = checkValueRangeConstraints(+test, constraints)
if (checkResult) return checkResult
}
return true
}
module.exports = { isEmpty, isValidHardwareId }
const isValidString = (input, constraints) => {
if (constraints) {
const test = input ? input : ''
if (!constraints.nullable && test === 'null') return chalk.red('Value Can\'t Be a null')
if (constraints.nullable && test === 'null') return true
if (constraints.validContentRegex && !test.match(constraints.validContentRegex)) return chalk.red('Input Doesn\'t Match RegEx')
const checkResult = checkValueRangeConstraints(test.length, constraints)
if (checkResult) return checkResult
}
return true
}
const checkValueRangeConstraints = (value, constraints) => {
let min
let max
if (constraints.valueRangeConstraints) {
const inRange = constraints.valueRangeConstraints.some(item => {
min = item.minValue
max = item.maxValue
return min <= value && max >= value
})
errStrArr = constraints.valueRangeConstraints.map(item => `${item.minValue}..${item.maxValue}`)
if (!inRange) return chalk.red(`Input is Outside Allowed Range: ${errStrArr.join(', ')}`)
}
}
const isValidNodeName = (input) => {
if (input.match('[a-zA-Z0-9-_.\\/:$]+') || input === '') {
return true
}
return chalk.red('Node Name is Invalid')
}
module.exports = {
isEmpty,
isValidHardwareId,
isValidNumber,
isValidString,
checkValueRangeConstraints,
isValidNodeName,
}