mirror of
https://github.com/vvsviridov/enm-cli.git
synced 2025-10-23 08:22:21 +00:00
bulk import creation
This commit is contained in:
@@ -11,6 +11,7 @@ require('dotenv').config({ path: [...__dirname.split(path.sep).slice(0,-1), '.en
|
||||
|
||||
const AutoProvisioning = require('../lib/applications/AutoProvisioning/AutoProvisioning')
|
||||
const TopologyBrowser = require('../lib/applications/TopologyBrowser/TopologyBrowser')
|
||||
const BulkImport = require('../lib/applications/BulkImport/BulkImport')
|
||||
|
||||
const logError = require('../util/logError')
|
||||
|
||||
@@ -30,7 +31,12 @@ const applications = [
|
||||
id: 'prvn',
|
||||
appClass: AutoProvisioning,
|
||||
name: 'Auto Provisioning',
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'bulk',
|
||||
appClass: BulkImport,
|
||||
name: 'Bulk Import',
|
||||
},
|
||||
]
|
||||
const appIds = applications.map(item => item.id)
|
||||
|
||||
|
@@ -20,9 +20,11 @@ class AutoProvisioning extends ENM {
|
||||
|
||||
this.appUrl = '/auto-provisioning/v1'
|
||||
this.projects = null
|
||||
this.projectIndex = -1
|
||||
this.projectId = null
|
||||
// this.projectIndex = -1
|
||||
this.nodes = null
|
||||
this.nodeIndex = -1
|
||||
// this.nodeIndex = -1
|
||||
this.nodeId = null
|
||||
this.prompt = ''
|
||||
this.help = 'No results...'
|
||||
}
|
||||
@@ -43,6 +45,10 @@ class AutoProvisioning extends ENM {
|
||||
await deleteProject.call(this)
|
||||
}
|
||||
|
||||
async deleteNode() {
|
||||
// await deleteNode.call(this)
|
||||
}
|
||||
|
||||
async getNode() {
|
||||
return await getNode.call(this)
|
||||
}
|
||||
|
@@ -9,22 +9,19 @@ inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'))
|
||||
|
||||
|
||||
async function commandOther(prvn, command) {
|
||||
const choosedIndex = prvn.choices.indexOf(command)
|
||||
if (choosedIndex !== -1) {
|
||||
if (prvn.nodes) {
|
||||
prvn.nodeIndex = choosedIndex
|
||||
prvn.getNode()
|
||||
} else {
|
||||
prvn.projectIndex = choosedIndex
|
||||
await prvn.getProjectData()
|
||||
}
|
||||
if (prvn.nodes) {
|
||||
prvn.nodeId = command
|
||||
prvn.getNode()
|
||||
} else {
|
||||
prvn.projectId = command
|
||||
await prvn.getProjectData()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function handleCommand(prvn, command) {
|
||||
const [, cmd] = command.match(/\[(\w+)\]/) || [, command]
|
||||
switch (cmd) {
|
||||
// const [, cmd] = command.match(/\[(\w+)\]/) || [, command]
|
||||
switch (command) {
|
||||
|
||||
case 'exit':
|
||||
prvn.prompt = ''
|
||||
@@ -36,7 +33,7 @@ async function handleCommand(prvn, command) {
|
||||
prvn.nodeIndex ? await prvn.getProjects() : await prvn.getProjectData()
|
||||
break
|
||||
case 'delete':
|
||||
prvn.nodeIndex ? prvn.deleteNode() : await prvn.deleteProject()
|
||||
prvn.nodeIndex ? await prvn.deleteNode() : await prvn.deleteProject()
|
||||
break
|
||||
|
||||
case 'status':
|
||||
|
@@ -30,22 +30,22 @@ const nodeCommandsHelp = [
|
||||
|
||||
|
||||
async function getNode() {
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
const nodeId = this.nodes[this.nodeIndex]
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
// const nodeId = this.nodes[this.nodeIndex]
|
||||
this.commands = nodeCommands
|
||||
this.help = nodeCommandsHelp.join('\n ')
|
||||
this.choices = [] // this.nodes.filter(item => item !== nodeId)
|
||||
this.prompt = `${projectId} (${nodeId}) `
|
||||
this.prompt = `${this.projectId} (${this.nodeId}) `
|
||||
}
|
||||
|
||||
|
||||
async function getNodeStatus() {
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
const nodeId = this.nodes[this.nodeIndex]
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
// const nodeId = this.nodes[this.nodeIndex]
|
||||
const axiosConfig = {
|
||||
text: `Getting ${nodeId}'s status...`,
|
||||
text: `Getting ${this.nodeId}'s status...`,
|
||||
method: 'get',
|
||||
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}`
|
||||
url: `${this.appUrl}/projects/${this.projectId}/nodes/${this.nodeId}`
|
||||
}
|
||||
const { data: { statusEntries } } = await this.httpClient.request(axiosConfig)
|
||||
logNodeStatus(statusEntries)
|
||||
@@ -53,12 +53,12 @@ async function getNodeStatus() {
|
||||
|
||||
|
||||
async function getNodeProperties() {
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
const nodeId = this.nodes[this.nodeIndex]
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
// const nodeId = this.nodes[this.nodeIndex]
|
||||
const axiosConfig = {
|
||||
text: `Getting ${nodeId}'s properties...`,
|
||||
text: `Getting ${this.nodeId}'s properties...`,
|
||||
method: 'get',
|
||||
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}?filter=properties`
|
||||
url: `${this.appUrl}/projects/${this.projectId}/nodes/${this.nodeId}?filter=properties`
|
||||
}
|
||||
const { data: { attributes, attributeGroups } } = await this.httpClient.request(axiosConfig)
|
||||
logNodeProperties(attributes, attributeGroups)
|
||||
@@ -75,12 +75,12 @@ async function bindNode() {
|
||||
validate: input => isValidHardwareId(input),
|
||||
}
|
||||
])
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
const nodeId = this.nodes[this.nodeIndex]
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
// const nodeId = this.nodes[this.nodeIndex]
|
||||
const axiosConfig = {
|
||||
text: `Binding ${hardwareId} to ${nodeId}...`,
|
||||
text: `Binding ${hardwareId} to ${this.nodeId}...`,
|
||||
method: 'put',
|
||||
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/actions/bind`,
|
||||
url: `${this.appUrl}/projects/${this.projectId}/nodes/${this.nodeId}/actions/bind`,
|
||||
data: {
|
||||
hardwareId,
|
||||
},
|
||||
@@ -91,12 +91,12 @@ async function bindNode() {
|
||||
|
||||
|
||||
async function cancelNode() {
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
const nodeId = this.nodes[this.nodeIndex]
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
// const nodeId = this.nodes[this.nodeIndex]
|
||||
const axiosConfig = {
|
||||
text: `Canceling ${nodeId}...`,
|
||||
text: `Canceling ${this.nodeId}...`,
|
||||
method: 'post',
|
||||
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/actions/cancel`,
|
||||
url: `${this.appUrl}/projects/${this.projectId}/nodes/${this.nodeId}/actions/cancel`,
|
||||
}
|
||||
const { statusText } = await this.httpClient.request(axiosConfig)
|
||||
console.log(chalk.bgGreen(`Canceling ${statusText}`))
|
||||
@@ -104,12 +104,12 @@ async function cancelNode() {
|
||||
|
||||
|
||||
async function resumeNode() {
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
const nodeId = this.nodes[this.nodeIndex]
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
// const nodeId = this.nodes[this.nodeIndex]
|
||||
const axiosConfig = {
|
||||
text: `Resuming ${nodeId}...`,
|
||||
text: `Resuming ${this.nodeId}...`,
|
||||
method: 'post',
|
||||
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/actions/resume`,
|
||||
url: `${this.appUrl}/projects/${this.projectId}/nodes/${this.nodeId}/actions/resume`,
|
||||
}
|
||||
const { statusText } = await this.httpClient.request(axiosConfig)
|
||||
console.log(chalk.bgGreen(`Resuming ${statusText}`))
|
||||
@@ -117,8 +117,8 @@ async function resumeNode() {
|
||||
|
||||
|
||||
async function configurationsNode() {
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
const nodeId = this.nodes[this.nodeIndex]
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
// const nodeId = this.nodes[this.nodeIndex]
|
||||
const fileNameInput = await inquirer.prompt([{
|
||||
type: 'file-tree-selection',
|
||||
name: 'nodeFile',
|
||||
@@ -127,9 +127,9 @@ async function configurationsNode() {
|
||||
const formData = new FormData()
|
||||
formData.append('file', fs.createReadStream(fileNameInput.nodeFile))
|
||||
const axiosConfig = {
|
||||
text: `Uploading ${nodeId} configuration to ${projectId}...`,
|
||||
text: `Uploading ${this.nodeId} configuration to ${this.projectId}...`,
|
||||
method: 'put',
|
||||
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/configurations`,
|
||||
url: `${this.appUrl}/projects/${this.projectId}/nodes/${this.nodeId}/configurations`,
|
||||
headers: formData.getHeaders(),
|
||||
data: formData
|
||||
}
|
||||
@@ -139,13 +139,13 @@ async function configurationsNode() {
|
||||
|
||||
|
||||
async function siteinstallNode() {
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
const nodeId = this.nodes[this.nodeIndex]
|
||||
const saveFileName = path.join(process.cwd(), `Site_Install_${nodeId}.xml`)
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
// const nodeId = this.nodes[this.nodeIndex]
|
||||
const saveFileName = path.join(process.cwd(), `Site_Install_${this.nodeId}.xml`)
|
||||
const axiosConfig = {
|
||||
text: `Downloading site install file ${nodeId}...`,
|
||||
text: `Downloading site install file ${this.nodeId}...`,
|
||||
method: 'get',
|
||||
url: `${this.appUrl}/projects/${projectId}/nodes/${nodeId}/configurations/siteinstall`,
|
||||
url: `${this.appUrl}/projects/${this.projectId}/nodes/${this.nodeId}/configurations/siteinstall`,
|
||||
}
|
||||
const { data } = await this.httpClient.request(axiosConfig)
|
||||
await fsPromises.writeFile(saveFileName, data)
|
||||
|
@@ -39,14 +39,14 @@ async function getProjects() {
|
||||
|
||||
|
||||
async function getProjectData() {
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
const axiosConfig = {
|
||||
text: `Getting ${projectId}'s status...`,
|
||||
text: `Getting ${this.projectId}'s status...`,
|
||||
method: 'get',
|
||||
url: `${this.appUrl}/projects/${projectId}`
|
||||
url: `${this.appUrl}/projects/${this.projectId}`
|
||||
}
|
||||
const { data: { nodeSummary } } = await this.httpClient.request(axiosConfig)
|
||||
axiosConfig.text = `Getting ${projectId}'s properties...`
|
||||
axiosConfig.text = `Getting ${this.projectId}'s properties...`
|
||||
axiosConfig.url += '?filter=properties'
|
||||
const { data } = await this.httpClient.request(axiosConfig)
|
||||
logProject(data, nodeSummary)
|
||||
@@ -55,7 +55,7 @@ async function getProjectData() {
|
||||
this.commands = projectCommands
|
||||
this.help = projectCommandsHelp.join('\n ')
|
||||
this.nodeIndex = -1
|
||||
this.prompt = projectId
|
||||
this.prompt = this.projectId
|
||||
}
|
||||
|
||||
|
||||
@@ -81,11 +81,11 @@ async function newProject() {
|
||||
|
||||
|
||||
async function deleteProject() {
|
||||
const { projectId } = this.projects[this.projectIndex]
|
||||
// const { projectId } = this.projects[this.projectIndex]
|
||||
const axiosConfig = {
|
||||
text: `Deleting project ${projectId}...`,
|
||||
text: `Deleting project ${this.projectId}...`,
|
||||
method: 'delete',
|
||||
url: `${this.appUrl}/projects/${projectId}`
|
||||
url: `${this.appUrl}/projects/${this.projectId}`
|
||||
}
|
||||
const { statusText } = await this.httpClient.request(axiosConfig)
|
||||
console.log(chalk.bgGreen(`Delete ${statusText}`))
|
||||
@@ -114,7 +114,11 @@ function projectsChoices(projects) {
|
||||
failed && failed + '⛔',
|
||||
]
|
||||
const space = ' '.repeat(Math.max(14, projectId.length + 2) - projectId.length)
|
||||
return `${chalk.bold(projectId)}${space}(${numberOfNodes}) ${statusList.filter(item => item).join(' ')}`
|
||||
return {
|
||||
value: projectId,
|
||||
short: projectId,
|
||||
name: `${chalk.bold(projectId)}${space}(${numberOfNodes}) ${statusList.filter(item => item).join(' ')}`,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
81
lib/applications/BulkImport/BulkImport.js
Executable file
81
lib/applications/BulkImport/BulkImport.js
Executable file
@@ -0,0 +1,81 @@
|
||||
const chalk = require('chalk')
|
||||
|
||||
const ENM = require('../../components/ENM')
|
||||
const {
|
||||
newJob,
|
||||
getJobs,
|
||||
pageJobs,
|
||||
} = require('./jobs')
|
||||
const {
|
||||
job,
|
||||
deleteJob,
|
||||
} = require('./job')
|
||||
const { getOperations } = require('./operations')
|
||||
const inputHandler = require('./inputHandler')
|
||||
const createNext = require('../../../util/createNext')
|
||||
|
||||
class BulkImport extends ENM {
|
||||
constructor(username, password, url) {
|
||||
super(username, password, url)
|
||||
|
||||
this.appUrl = '/bulk-configuration/v1/import-jobs/jobs'
|
||||
this.jobs = null
|
||||
this.jobsLinks = null
|
||||
this.jobId = null
|
||||
this.username = username
|
||||
this.onlyMy = false
|
||||
this.jobsOffset = 0
|
||||
this.jobsLimit = 50
|
||||
this.operations = null
|
||||
this.operationsId = null
|
||||
this.operationsOffset = 0
|
||||
this.operationsLimit = 50
|
||||
this.operationsLinks = null
|
||||
this.prompt = ''
|
||||
this.help = 'No results...'
|
||||
}
|
||||
|
||||
async getJobs() {
|
||||
await getJobs.call(this)
|
||||
}
|
||||
|
||||
async pageJobs(page) {
|
||||
await pageJobs.call(this, page)
|
||||
}
|
||||
|
||||
async deleteJob() {
|
||||
await deleteJob.call(this)
|
||||
}
|
||||
|
||||
job(jobId) {
|
||||
job.call(this, jobId)
|
||||
}
|
||||
|
||||
async my(){
|
||||
this.onlyMy = !this.onlyMy
|
||||
console.log(chalk.yellowBright(`Show ${this.onlyMy ? 'only my' : 'all'} jobs❗`))
|
||||
this.jobsOffset = 0
|
||||
this.jobsLimit = 50
|
||||
await this.getJobs()
|
||||
}
|
||||
|
||||
async newJob() {
|
||||
await newJob.call(this)
|
||||
}
|
||||
|
||||
async getOperations() {
|
||||
await getOperations.call(this)
|
||||
}
|
||||
|
||||
async next(input) {
|
||||
return createNext.call(this, input ? input : '')
|
||||
}
|
||||
|
||||
async inputHandler() {
|
||||
await inputHandler.call(this)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
module.exports = BulkImport
|
105
lib/applications/BulkImport/inputHandler.js
Executable file
105
lib/applications/BulkImport/inputHandler.js
Executable file
@@ -0,0 +1,105 @@
|
||||
const inquirer = require('inquirer')
|
||||
const chalk = require('chalk')
|
||||
|
||||
const logError = require('../../../util/logError')
|
||||
const { logOperation } = require('../../../util/logOperation')
|
||||
const { isEmpty } = require('../../../util/validation')
|
||||
|
||||
|
||||
inquirer.registerPrompt('autocomplete', require('inquirer-autocomplete-prompt'))
|
||||
|
||||
|
||||
async function commandOther(bulk, command) {
|
||||
if (bulk.operations) {
|
||||
const operation = bulk.operations.find(i => i.id === command)
|
||||
logOperation(operation)
|
||||
}
|
||||
if (!bulk.jobId) {
|
||||
bulk.job(command)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function handleCommand(bulk, command) {
|
||||
switch (command) {
|
||||
case 'exit':
|
||||
bulk.prompt = ''
|
||||
break
|
||||
case 'next':
|
||||
case 'last':
|
||||
case 'first':
|
||||
case 'prev':
|
||||
await bulk.pageJobs(command)
|
||||
break
|
||||
case 'operations':
|
||||
case 'failures':
|
||||
await bulk.getOperations()
|
||||
break
|
||||
case 'my':
|
||||
await bulk.my()
|
||||
break
|
||||
case 'new':
|
||||
await bulk.newJob()
|
||||
break
|
||||
case 'back':
|
||||
bulk.operations ? await bulk.job() : await bulk.getJobs()
|
||||
break
|
||||
case 'delete':
|
||||
await bulk.deleteJob()
|
||||
break
|
||||
|
||||
// case 'status':
|
||||
// await bulk.getNodeStatus()
|
||||
// break
|
||||
// case 'properties':
|
||||
// await bulk.getNodeProperties()
|
||||
// break
|
||||
// case 'bind':
|
||||
// await bulk.bindNode()
|
||||
// break
|
||||
// case 'cancel':
|
||||
// await bulk.cancelNode()
|
||||
// break
|
||||
// case 'resume':
|
||||
// await bulk.resumeNode()
|
||||
// break
|
||||
// case 'configurations':
|
||||
// await bulk.configurationsNode()
|
||||
// break
|
||||
// case 'siteinstall':
|
||||
// await bulk.siteinstallNode()
|
||||
// break
|
||||
|
||||
default:
|
||||
await commandOther(bulk, command)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function inputHandler() {
|
||||
await this.getJobs()
|
||||
while (true) {
|
||||
try {
|
||||
const input = await inquirer.prompt([
|
||||
{
|
||||
type: 'autocomplete',
|
||||
name: 'command',
|
||||
message: chalk.bold.blue(this.prompt),
|
||||
pageSize: 10,
|
||||
prefix: '',
|
||||
suffix: chalk.bold.blue('>'),
|
||||
validate: isEmpty,
|
||||
source: async (answers, input) => await this.next(input),
|
||||
emptyText: this.help,
|
||||
}
|
||||
])
|
||||
await handleCommand(this, input.command)
|
||||
if (!this.prompt) break
|
||||
} catch (error) {
|
||||
logError(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = inputHandler
|
68
lib/applications/BulkImport/job.js
Executable file
68
lib/applications/BulkImport/job.js
Executable file
@@ -0,0 +1,68 @@
|
||||
const inquirer = require('inquirer')
|
||||
const chalk = require('chalk')
|
||||
const inquirerFileTreeSelection = require('inquirer-file-tree-selection-prompt')
|
||||
const fs = require('fs')
|
||||
const FormData = require('form-data')
|
||||
|
||||
const { logJob } = require('../../../util/logJob')
|
||||
|
||||
inquirer.registerPrompt('file-tree-selection', inquirerFileTreeSelection)
|
||||
|
||||
|
||||
const jobCommands = ['operations', 'failures', 'delete', 'back', 'exit']
|
||||
const jobCommandsHelp = [
|
||||
'[delete] - Deletes import job.',
|
||||
'[back] - Return to jobs.',
|
||||
'[exit] - Exit this app.',
|
||||
'Or choose a node from list...',
|
||||
]
|
||||
|
||||
|
||||
function job(jobId) {
|
||||
const job = this.jobs.find(job => job.id === jobId)
|
||||
if (!job) {
|
||||
throw new Error('Job not Found❗')
|
||||
}
|
||||
this.jobId = jobId
|
||||
this.operations = null
|
||||
logJob(job)
|
||||
this.commands = [...jobCommands]
|
||||
this.help = jobCommandsHelp.join('\n ')
|
||||
this.choices = []
|
||||
this.prompt = job.name
|
||||
}
|
||||
|
||||
|
||||
async function deleteJob() {
|
||||
if (!this.jobId) {
|
||||
throw new Error('Job Is not selected❗')
|
||||
}
|
||||
const { confirm } = await inquirer.prompt([
|
||||
{
|
||||
type: 'confirm',
|
||||
name: 'confirm',
|
||||
message: 'Still want to delete❓',
|
||||
default: true,
|
||||
},
|
||||
])
|
||||
if (!confirm) return
|
||||
const axiosConfig = {
|
||||
text: `Deleting job ${this.jobId} ...`,
|
||||
method: 'delete',
|
||||
url: this.appUrl,
|
||||
params: {
|
||||
jobId: this.jobId,
|
||||
}
|
||||
}
|
||||
const { data } = await this.httpClient.request(axiosConfig)
|
||||
data.errors.forEach(err => {
|
||||
console.log(`${err.type === 'INFO' ? chalk.green(err.message) : chalk.red(err.code + ': ' + err.message)}`)
|
||||
})
|
||||
await this.getJobs()
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
job,
|
||||
deleteJob,
|
||||
}
|
173
lib/applications/BulkImport/jobs.js
Executable file
173
lib/applications/BulkImport/jobs.js
Executable file
@@ -0,0 +1,173 @@
|
||||
const chalk = require('chalk')
|
||||
const inquirer = require('inquirer')
|
||||
|
||||
const { statusPic } = require('../../../util/logJob')
|
||||
const { isEmpty } = require('../../../util/validation')
|
||||
|
||||
const jobsCommands = ['new', 'my', 'exit']
|
||||
const jobsCommandsHelp = [
|
||||
'[new] - Creates a new import job.',
|
||||
'[exit] - Exit this app.',
|
||||
'Or choose a job from list...',
|
||||
]
|
||||
|
||||
|
||||
async function newJob() {
|
||||
const {
|
||||
name,
|
||||
validationPolicy,
|
||||
executionPolicy,
|
||||
unsynchNodes,
|
||||
executionOrder
|
||||
} = await inquirer.prompt([
|
||||
{
|
||||
type: 'input',
|
||||
name: 'name',
|
||||
message: 'Type a job\'s name:',
|
||||
validate: isEmpty,
|
||||
},
|
||||
{
|
||||
type: 'checkbox',
|
||||
name: 'validationPolicy',
|
||||
message: 'Validation Policy:',
|
||||
choices: [
|
||||
{
|
||||
name: 'Skip MO Instance Validation',
|
||||
value: 'no-instance-validation',
|
||||
},
|
||||
{
|
||||
name: 'Skip Node Based Validation',
|
||||
value: 'no-node-based-validation',
|
||||
checked: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'executionPolicy',
|
||||
message: 'Execution Error Handling:',
|
||||
choices: [
|
||||
{
|
||||
name: 'Stop',
|
||||
value: 'stop-on-error',
|
||||
},
|
||||
{
|
||||
name: 'Skip to next node',
|
||||
value: 'continue-on-error-node',
|
||||
},
|
||||
{
|
||||
name: 'Skip to next operation',
|
||||
value: 'continue-on-error-operation',
|
||||
},
|
||||
],
|
||||
default: 'stop-on-error',
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'unsynchNodes',
|
||||
message: 'Unsynchronized Nodes Policy:',
|
||||
choices: [
|
||||
{
|
||||
name: 'Import skips operation execution on unsynchronized nodes. Recommended for faster performance.',
|
||||
value: 'skip-unsync-nodes',
|
||||
},
|
||||
{
|
||||
name: 'Import will be agnostic towards node synchronization state, implying import attempts operation execution on unsychronized nodes as well.',
|
||||
value: 'exec-unsync-nodes',
|
||||
},
|
||||
],
|
||||
default: 'skip-unsync-nodes',
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
name: 'executionOrder',
|
||||
message: 'Execution Order:',
|
||||
choices: [
|
||||
{
|
||||
name: 'Sequential',
|
||||
value: 'sequential',
|
||||
},
|
||||
{
|
||||
name: 'Parallel',
|
||||
value: 'parallel',
|
||||
},
|
||||
],
|
||||
default: 'parallel',
|
||||
when: input => !input.executionPolicy.includes('stop-on-error')
|
||||
},
|
||||
])
|
||||
const axiosConfig = {
|
||||
text: 'Creating job...',
|
||||
method: 'post',
|
||||
url: this.appUrl,
|
||||
data: {
|
||||
name,
|
||||
validationPolicy: validationPolicy.length
|
||||
? validationPolicy
|
||||
: ['instance-validation', 'node-based-validation'],
|
||||
executionPolicy: [
|
||||
executionPolicy,
|
||||
unsynchNodes,
|
||||
executionOrder ?? 'sequential',
|
||||
]
|
||||
},
|
||||
}
|
||||
const { data } = await this.httpClient.request(axiosConfig)
|
||||
this.jobs.unshift(data)
|
||||
this.job(data.id)
|
||||
}
|
||||
|
||||
|
||||
async function getJobs() {
|
||||
const params =new URLSearchParams()
|
||||
params.append('offset', this.jobsOffset)
|
||||
params.append('limit', this.jobsLimit)
|
||||
params.append('expand', 'summary')
|
||||
params.append('expand', 'files')
|
||||
if (this.onlyMy) {
|
||||
params.append('createdBy', this.username)
|
||||
}
|
||||
const axiosConfig = {
|
||||
text: 'Getting jobs...',
|
||||
method: 'get',
|
||||
url: this.appUrl,
|
||||
params,
|
||||
}
|
||||
const { data } = await this.httpClient.request(axiosConfig)
|
||||
this.jobs = data.jobs
|
||||
this.jobId = null
|
||||
this.jobsLinks = data._links
|
||||
this.choices = this.jobs.map(item => ({
|
||||
name: `${statusPic(item.status)} ${item.name} ${chalk.dim(item.userId)}`,
|
||||
value: item.id,
|
||||
short: item.id
|
||||
}))
|
||||
this.commands = [...jobsCommands]
|
||||
Object.values(this.jobsLinks).forEach(item => item.rel !== 'self' && this.commands.push(item.rel))
|
||||
this.help = jobsCommandsHelp.join('\n ')
|
||||
this.prompt = `${data.totalCount} jobs (${this.jobsOffset}-${Math.min(this.jobsOffset + this.jobsLimit, data.totalCount)})`
|
||||
}
|
||||
|
||||
|
||||
function setJobsPagination(href) {
|
||||
const url = new URL(href)
|
||||
const searchParams = new URLSearchParams(url.search)
|
||||
this.jobsLimit = +searchParams.get('limit')
|
||||
this.jobsOffset = +searchParams.get('offset')
|
||||
}
|
||||
|
||||
|
||||
async function pageJobs(page) {
|
||||
if (!this.jobsLinks[page]) {
|
||||
throw new Error(`No ${page} jobs❗`)
|
||||
}
|
||||
setJobsPagination.call(this, this.jobsLinks[page].href)
|
||||
await this.getJobs()
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
newJob,
|
||||
getJobs,
|
||||
pageJobs,
|
||||
}
|
68
lib/applications/BulkImport/operations.js
Executable file
68
lib/applications/BulkImport/operations.js
Executable file
@@ -0,0 +1,68 @@
|
||||
const chalk = require("chalk")
|
||||
|
||||
const operationsCommands = ['print', 'back', 'exit']
|
||||
const operationsCommandsHelp = [
|
||||
'[print] - Print all operations.',
|
||||
'[back] - Return to jobs.',
|
||||
'[exit] - Exit this app.',
|
||||
'Or choose a node from list...',
|
||||
]
|
||||
|
||||
|
||||
async function getOperations() {
|
||||
const params =new URLSearchParams()
|
||||
params.append('offset', this.operationsOffset)
|
||||
params.append('limit', this.operationsLimit)
|
||||
params.append('expand', 'attributes')
|
||||
params.append('expand', 'persistentCurrentAttributes')
|
||||
params.append('expand', 'failures')
|
||||
params.append('expand', 'warnings')
|
||||
// params.append('status', 'invalid')
|
||||
// params.append('status', 'execution-error')
|
||||
const axiosConfig = {
|
||||
text: 'Getting operations...',
|
||||
method: 'get',
|
||||
url: `${this.appUrl}/${this.jobId}/operations/`,
|
||||
params,
|
||||
}
|
||||
const { data } = await this.httpClient.request(axiosConfig)
|
||||
this.operations = data.operations
|
||||
this.operationsId = null
|
||||
this.operationsLinks = data._links
|
||||
this.choices = this.operations.map(item => ({
|
||||
name: `${item.status === 'EXECUTED' ? chalk.green(item.type) : chalk.red(item.type)} ${item.fdn.slice(-79)}`,
|
||||
value: item.id,
|
||||
short: item.id
|
||||
}))
|
||||
this.commands = [...operationsCommands]
|
||||
Object.values(this.operationsLinks).forEach(item => {
|
||||
if (item.rel !== 'self' || item.rel !== 'job') {
|
||||
this.commands.push(item.rel)
|
||||
}
|
||||
})
|
||||
this.help = operationsCommandsHelp.join('\n ')
|
||||
this.prompt = `${data.totalCount} operations (${this.operationsOffset}-${Math.min(this.operationsOffset + this.operationsLimit, data.totalCount)})`
|
||||
}
|
||||
|
||||
|
||||
function setOperationsPagination(href) {
|
||||
const url = new URL(href)
|
||||
const searchParams = new URLSearchParams(url.search)
|
||||
this.operationsLimit = +searchParams.get('limit')
|
||||
this.operationsOffset = +searchParams.get('offset')
|
||||
}
|
||||
|
||||
|
||||
async function pageOperations(page) {
|
||||
if (!this.operationsLinks[page]) {
|
||||
throw new Error(`No ${page} jobs❗`)
|
||||
}
|
||||
setOperationsPagination.call(this, this.operationsLinks[page].href)
|
||||
await this.getOperations()
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
getOperations,
|
||||
pageOperations,
|
||||
}
|
@@ -110,7 +110,7 @@ class TopologyBrowser extends ENM {
|
||||
}
|
||||
|
||||
check() {
|
||||
logAttributes(this.fdn.replace(/\((\w+)\)/g, ''), this.configSet)
|
||||
logAttributes(this.fdn, this.configSet)
|
||||
}
|
||||
|
||||
home() {
|
||||
|
@@ -13,11 +13,11 @@ function get() {
|
||||
if (!attribute) {
|
||||
throw new Error(`Attribute not Found: ${this.attribute}`)
|
||||
}
|
||||
const attributeData = this.attributesData.find(item => item.key === this.attribute)
|
||||
const { constraints, defaultValue, type } = 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'] : ''}
|
||||
console.log(` ${chalk.green('Type: ') + type} ${defaultValue && chalk.yellow('Default: ') + defaultValue}
|
||||
`)
|
||||
if (attributeData.constraints && attributeData.constraints.orderingConstraint) banner(attributeData)
|
||||
if (constraints && constraints.orderingConstraint) banner(constraints)
|
||||
}
|
||||
|
||||
module.exports = get
|
||||
|
@@ -26,4 +26,6 @@ async function goToFdn(targetFdn) {
|
||||
}
|
||||
this.fdn = fdn
|
||||
}
|
||||
|
||||
|
||||
module.exports = goToFdn
|
||||
|
@@ -1,13 +1,18 @@
|
||||
const chalk = require('chalk')
|
||||
|
||||
|
||||
async function nextAttributes(input) {
|
||||
const filter = input ? input : ''
|
||||
// const filter = input ? input : ''
|
||||
this.commands = this.configCommands
|
||||
.filter(item => item.toLowerCase().includes(filter.toLowerCase()))
|
||||
// .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)
|
||||
.map(({ key, value }) => ({
|
||||
value: key,
|
||||
short: key,
|
||||
name: `${key} ${chalk.dim(typeof value !== 'object' || value === null ? value : '...')}`
|
||||
}))
|
||||
// .filter(item => item.name.toLowerCase().includes(filter.toLowerCase()))
|
||||
.sort((a, b) => a.value > b.value ? 1 : -1)
|
||||
}
|
||||
|
||||
|
||||
|
@@ -40,17 +40,21 @@ async function nextObjects(input) {
|
||||
this.poIds.push(this.currentPoId)
|
||||
await networkRequest.call(this)
|
||||
}
|
||||
this.commands = otherCommands.filter(cmd => cmd.toLowerCase().includes(filter.toLowerCase()))
|
||||
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}`
|
||||
return {
|
||||
name: `${child.moType}=${child.moName}${st}${rt}${ne}`,
|
||||
value: `${child.moType}=${child.moName}`,
|
||||
short: `${child.moType}=${child.moName}`,
|
||||
}
|
||||
})
|
||||
.filter(child => child.toLowerCase().includes(filter.toLowerCase()))
|
||||
.concat(filter.startsWith('show') ? [filter] : [])
|
||||
.concat(filter.startsWith('fdn') ? [filter] : [])
|
||||
// .filter(child => child.name.toLowerCase().includes(filter.toLowerCase()))
|
||||
.concat(filter.startsWith('show') ? [{ name: filter, value: filter, short: filter }] : [])
|
||||
.concat(filter.startsWith('fdn') ? [{ name: filter, value: filter, short: filter }] : [])
|
||||
}
|
||||
|
||||
|
||||
|
@@ -21,15 +21,15 @@ function commandOther(tplg, command) {
|
||||
|
||||
|
||||
async function handleCommand(tplg, command) {
|
||||
const [cmd, param] = command.split(/\s+/)
|
||||
const cmdMatch = cmd.match(/\[(\w+)\]/)
|
||||
const cmdName = cmdMatch ? cmdMatch[1] : cmd
|
||||
switch (cmdName) {
|
||||
const [cmd, param = ''] = command.split(/\s+/)
|
||||
// const cmdMatch = cmd.match(/\[(\w+)\]/)
|
||||
// const cmdName = cmdMatch ? cmdMatch[1] : cmd
|
||||
switch (cmd) {
|
||||
case 'exit':
|
||||
tplg.fdn = ''
|
||||
break
|
||||
case 'show':
|
||||
await tplg.show(param ? param.trim() : '')
|
||||
await tplg.show(param)
|
||||
break
|
||||
case 'config':
|
||||
await tplg.config()
|
||||
@@ -65,7 +65,7 @@ async function handleCommand(tplg, command) {
|
||||
tplg.persistent()
|
||||
break
|
||||
case 'fdn':
|
||||
await tplg.goToFdn(param ? param.trim() : '')
|
||||
await tplg.goToFdn(param)
|
||||
break
|
||||
case 'alarms':
|
||||
await tplg.alarms()
|
||||
@@ -91,7 +91,7 @@ async function inputHandler() {
|
||||
message: chalk.blue(this.getPrompt()),
|
||||
pageSize: 10,
|
||||
prefix: '',
|
||||
suffix: this.isConfig ? chalk.blue('#') : chalk.blue('>'),
|
||||
suffix: chalk.blue(this.isConfig ? '#': '>'),
|
||||
validate: isEmpty,
|
||||
source: async (answers, input) => await this.next(input),
|
||||
emptyText: this.help,
|
||||
|
@@ -4,8 +4,8 @@ class ENM {
|
||||
constructor(username, password, url) {
|
||||
this.logoutUrl = '/logout'
|
||||
this.loginUrl = `/login?IDToken1=${username}&IDToken2=${password}`
|
||||
this.commands = null
|
||||
this.choices = null
|
||||
this.commands = []
|
||||
this.choices = []
|
||||
this.httpClient = axiosHttpClient(url)
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,8 @@
|
||||
const ora = require('ora')
|
||||
const { isXMas } = require('../../util/validation')
|
||||
|
||||
const defaultSpinner = process.env.SPINNER || 'clock'
|
||||
|
||||
|
||||
class SpinnerWithCounter {
|
||||
constructor() {
|
||||
@@ -9,6 +13,7 @@ class SpinnerWithCounter {
|
||||
start(text) {
|
||||
if (!this.spinner) {
|
||||
this.spinner = ora(text)
|
||||
this.spinner.spinner = isXMas() ? 'christmas' : defaultSpinner
|
||||
this.spinner.start()
|
||||
}
|
||||
this.counter = ++this.counter
|
||||
|
@@ -4,16 +4,21 @@ const inquirer = require('inquirer')
|
||||
function createNext(filter) {
|
||||
const separator = new inquirer.Separator()
|
||||
const commands = this.commands.filter(cmd => cmd.toLowerCase().includes(filter.toLowerCase()))
|
||||
const choices = this.choices.filter(choice => choice.toLowerCase().includes(filter.toLowerCase()))
|
||||
const choices = this.choices.filter(choice => choice.name.toLowerCase().includes(filter.toLowerCase()))
|
||||
let result = [
|
||||
...choices,
|
||||
separator,
|
||||
...commands.map(cmd => `[${cmd}]`),
|
||||
...commands.map(cmd => ({
|
||||
name: `[${cmd}]`,
|
||||
value: cmd,
|
||||
short: cmd,
|
||||
})),
|
||||
separator,
|
||||
]
|
||||
if (result.includes([`${filter}`])) {
|
||||
result = result.filter(item => item !== [`${filter}`])
|
||||
result.unshift([`${filter}`])
|
||||
const findFilter = result.find(item => item.value === filter)
|
||||
if (findFilter) {
|
||||
result = result.filter(item => item.value !== filter)
|
||||
result.unshift(findFilter)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ function colorize(attributes) {
|
||||
|
||||
function logAttributes(fdn, attributes) {
|
||||
const output = `
|
||||
${chalk.yellow.bold('FDN')}: ${chalk.bold(fdn)}
|
||||
${chalk.yellow.bold('FDN')}: ${chalk.bold(fdn.replace(/\((\w+)\)/g, ''))}
|
||||
${colorize(attributes)}`
|
||||
console.log(output)
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
const chalk = require('chalk')
|
||||
|
||||
function logError(err) {
|
||||
// console.dir(err)
|
||||
try {
|
||||
if (!err.response) {
|
||||
const {
|
||||
@@ -39,6 +40,17 @@ function logError(err) {
|
||||
errorBody = data.body
|
||||
errorDetails = data.detail
|
||||
}
|
||||
//bulk error
|
||||
if (data.errors) {
|
||||
errorBody = `Total Errors Count: ${data.totalCount}`
|
||||
errorDetails = data.errors.map(err => {
|
||||
return `
|
||||
${err.type}: ${err.code}
|
||||
${err.message}
|
||||
${err.parameters && typeof err.parameters === 'object' ? JSON.stringify(err.parameters) : err.parameters}
|
||||
`
|
||||
})
|
||||
}
|
||||
console.log(`
|
||||
⚠️ ${chalk.bold.bgRed(errorTitle)}
|
||||
${chalk.yellow(errorBody)}${errorDetails ? '\n' + errorDetails.toString() : ''}
|
||||
|
113
util/logJob.js
Executable file
113
util/logJob.js
Executable file
@@ -0,0 +1,113 @@
|
||||
const chalk = require('chalk')
|
||||
|
||||
|
||||
function statusPic(status) {
|
||||
switch (status) {
|
||||
case 'CREATED':
|
||||
return '📄'
|
||||
case 'PARSING':
|
||||
return '📂'
|
||||
case 'PARSED':
|
||||
return '📁'
|
||||
case 'VALIDATING':
|
||||
return '🎛'
|
||||
case 'VALIDATED':
|
||||
return '📊'
|
||||
case 'EXECUTING':
|
||||
return '⏳'
|
||||
case 'EXECUTED':
|
||||
return '✅'
|
||||
case 'CANCELLING':
|
||||
return '❌'
|
||||
case 'CANCELLED':
|
||||
return '⛔️'
|
||||
|
||||
default:
|
||||
return '❓'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function statusColor(status) {
|
||||
switch (status) {
|
||||
case 'EXECUTED':
|
||||
return chalk.green(status)
|
||||
case 'CANCELLED':
|
||||
return chalk.red(status)
|
||||
|
||||
default:
|
||||
return chalk.yellow(status)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function validationPolicy(job) {
|
||||
return [
|
||||
`${job.validationPolicy.includes('instance-validation') ? '✅' : '❌'} Instance Validation`,
|
||||
`${job.validationPolicy.includes('node-based-validation') ? '✅' : '❌'} Node Based Validation`,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
function executionPolicy(job) {
|
||||
return [
|
||||
`${job.executionPolicy.includes('stop-on-error') ? '✅' : '❌'} Stop`,
|
||||
`${job.executionPolicy.includes('continue-on-error-node') ? '✅' : '❌'} Skip to Next Node`,
|
||||
`${job.executionPolicy.includes('continue-on-error-operation') ? '✅' : '❌'} Skip to Next Operation`,
|
||||
`${job.executionPolicy.includes('parallel') ? '✅ Parallel' : '✅ Sequential'}`,
|
||||
`${job.executionPolicy.includes('skip-unsync-nodes') ? '✅' : '❌'} Skip Unsync Nodes`,
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
function timeAttributes(job) {
|
||||
Object.entries(job)
|
||||
.forEach(([key, value]) => {
|
||||
if (['created', 'lastValidation', 'lastExecution'].includes(key)) {
|
||||
const title = key.replace(/([A-Z])/g, ' $1').replace(/^./, c => c.toUpperCase())
|
||||
const formattingSpaces = ' '.repeat(18 - title.length)
|
||||
console.log(` ${chalk.yellowBright.bold(title + ':')}${formattingSpaces} ${new Date(value).toLocaleString()}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function optionalAttributes(job) {
|
||||
if (job.failureReason) {
|
||||
console.log(` ${chalk.yellowBright.bold('Failure Reason: ')} ${chalk.red(job.failureReason)}`)
|
||||
}
|
||||
if (job.files && job.files.find(i => i).name) {
|
||||
console.log(` ${chalk.yellowBright.bold('Files: ')}
|
||||
${job.files.map(f => `${chalk.dim.italic(f.format)} ${f.name}`).join('\n ')}`)
|
||||
}
|
||||
if (job.summary) {
|
||||
console.table(job.summary, ['parsed', 'valid', 'invalid', 'executed', 'executionErrors'])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function logJob(job) {
|
||||
if (!job) {
|
||||
throw new Error('No job data❗')
|
||||
}
|
||||
console.log(`
|
||||
${chalk.yellowBright.bold('Job ID: ')} ${chalk.dim(job.id)}
|
||||
${chalk.yellowBright.bold('Job Name: ')} ${job.name}
|
||||
${chalk.yellowBright.bold('User: ')} ${chalk.cyanBright.underline(job.userId)}
|
||||
${chalk.yellowBright.bold('Configuration: ')} ${chalk.greenBright(job.configuration)}
|
||||
${chalk.yellowBright.bold('Status: ')} ${statusColor(job.status)} ${statusPic(job.status)}
|
||||
${chalk.yellowBright.bold('Time Total: ')} ${new Date(job.totalElapsedTime * 1000).toISOString().slice(11, -5)}`)
|
||||
timeAttributes(job)
|
||||
console.log(` ${chalk.yellowBright.bold('Validation Options:')}
|
||||
${chalk.gray(validationPolicy(job).join('\n '))}
|
||||
${chalk.yellowBright.bold('Execution Options:')}
|
||||
${chalk.gray(executionPolicy(job).join('\n '))}`)
|
||||
optionalAttributes(job)
|
||||
console.log('')
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
logJob,
|
||||
statusPic,
|
||||
}
|
64
util/logOperation.js
Executable file
64
util/logOperation.js
Executable file
@@ -0,0 +1,64 @@
|
||||
const chalk = require('chalk')
|
||||
|
||||
|
||||
function statusColor(status) {
|
||||
switch (status) {
|
||||
case 'EXECUTED':
|
||||
return chalk.green(status)
|
||||
case 'INVALID':
|
||||
return chalk.red(status)
|
||||
|
||||
default:
|
||||
return chalk.yellow(status)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function attributesTransform(attributes, currentAttributes) {
|
||||
return attributes.reduce((prev, curr) => {
|
||||
const { currentValue } = currentAttributes.find(i => i.name === curr.name) ?? {}
|
||||
const { name, suppliedValue } = curr
|
||||
return {
|
||||
...prev,
|
||||
[name]: {
|
||||
currentValue: currentValue ? currentValue : '',
|
||||
suppliedValue,
|
||||
}
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
|
||||
|
||||
function logOperation(operation) {
|
||||
if (!operation) {
|
||||
throw new Error('No operation data❗')
|
||||
}
|
||||
const {
|
||||
id,
|
||||
type,
|
||||
status,
|
||||
updateTime,
|
||||
fdn,
|
||||
failures,
|
||||
attributes = [],
|
||||
currentAttributes = [],
|
||||
} = operation
|
||||
console.log(`
|
||||
${chalk.yellowBright.bold('Operation ID:')} ${chalk.dim(id)}
|
||||
${chalk.yellowBright.bold('Type: ')} ${type}
|
||||
${chalk.yellowBright.bold('Status: ')} ${statusColor(status)}
|
||||
${chalk.yellowBright.bold('Update Time: ')} ${new Date(updateTime).toLocaleString()}
|
||||
${chalk.yellowBright.bold('FDN: ')} ${chalk.cyanBright.underline(fdn)}`)
|
||||
if (failures) {
|
||||
console.log(` ${chalk.yellowBright.bold('Failures: ')}`)
|
||||
failures.forEach(({ failureReason }) => console.log(` ❌ ${chalk.redBright(failureReason)}`))
|
||||
}
|
||||
if (attributes.length > 0) {
|
||||
console.table(attributesTransform(attributes, currentAttributes))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
logOperation,
|
||||
}
|
@@ -3,7 +3,7 @@ const chalk = require('chalk')
|
||||
|
||||
function logProject(data, nodeSummary) {
|
||||
if (!data || !nodeSummary) {
|
||||
throw new Error('No project data or node summary!')
|
||||
throw new Error('No project data or node summary❗')
|
||||
}
|
||||
const {
|
||||
id: projectId,
|
||||
|
@@ -53,6 +53,14 @@ const isValidNodeName = (input) => {
|
||||
}
|
||||
|
||||
|
||||
const isXMas = () => {
|
||||
const date = new Date()
|
||||
if (date.getMonth() === 11 || date.getMonth() === 0) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
isEmpty,
|
||||
isValidHardwareId,
|
||||
@@ -60,4 +68,5 @@ module.exports = {
|
||||
isValidString,
|
||||
checkValueRangeConstraints,
|
||||
isValidNodeName,
|
||||
isXMas,
|
||||
}
|
Reference in New Issue
Block a user