import yargs from 'yargs/yargs' import { hideBin } from 'yargs/helpers' import fs from 'fs' import {RedisConnexion} from '../redisConnexion.js' import riConfig from './riConfig.json' with { type: 'json' } import pkg from 'enquirer'; const { prompt } = pkg; const dialogData = { 'step1' : { 'question': { 'type': 'input', 'name': 'uid', 'message': 'Sender uid ? (Back-ends: backend-marklogic / backend-smed )' }, 'nextStep': 'step2' }, 'step2' : { 'question': { 'type': 'autocomplete', 'name': 'channel', 'message': 'Channel ?', 'limit': 10, 'initial': 2, 'choices': [ '[NEW]', 'dataSync:userRoles', 'system:notifs', 'infraNotifs:httpGateway', 'infraNotifs:midas', 'services:notifications', 'services:pdfs', ] }, 'nextStep': 'step3' }, 'step3': { 'question': { 'type': 'select', 'name': 'type', 'message': 'Type of message ?', 'choices': [ { 'message': 'Event', 'value': 'event' }, { 'message': 'Action', 'value': 'action' }, { 'message': 'Other', 'value': 'other' }, ] }, 'nextStep': 'step4-{{type}}' }, 'step4-event': { 'question': { 'type': 'select', 'name': 'eventType', 'message': 'Type of event ?', 'choices': null, }, 'nextStep': 'step5' }, 'step4-action': { 'question': { 'type': 'select', 'name': 'action', 'message': 'Type of action ?', 'choices': null }, 'nextStep': 'step5' }, 'step4-other': { 'question': { 'type': 'input', 'name': 'payload', 'message': 'Payload (json)' }, 'nextStep': 'step6' }, 'step5': { 'question': { 'type': 'input', 'name': 'payload', 'message': 'Payload (json), [enter for none]' }, 'nextStep': 'step6' }, 'step6': { 'question': { 'type': 'select', 'name': 'confirm', 'message': 'Confirm what you want:', 'initial': 'send', 'choices': [ { 'message': 'Send on the bus', 'value': 'send' }, { 'message': 'Save to file', 'value': 'save' }, { 'message': 'Cancel', 'value': 'cancel' }, ] }, 'nextStep': null }, } const selectors = { 'chan2eventTypes': { 'dataSync:userRoles': ['updated'], 'system:notifs': ['growl'], }, 'chan2actions': { 'infraNotifs:httpGateway': ['TIME', 'RELOADACCESSRIGHTS', 'GETACCESSRIGHTS'], 'infraNotifs:midas': ['TIME', 'LISTPLUGINS', 'RELOADPLUGINS'], 'services:notifications': [ 'notify' ], } } class Dialog { constructor(dialogData, selectors){ this.dialogData = dialogData this.selectors = selectors this.curStep = null this.answers = {} } async start(step){ this.curStep = step while(this.curStep && this.dialogData[this.curStep]){ let latestAnswer = await(this.askStep(this.curStep)) if(latestAnswer[this.dialogData[this.curStep].question.name]=='[NEW]'){ latestAnswer = await prompt({ 'type': 'input', 'name': this.dialogData[this.curStep].question.name, 'message': `(enter your new/custom value) ${this.dialogData[this.curStep].question.message}`, }) } this.curStep = this.dialogData[this.curStep].nextStep Object.assign(this.answers, latestAnswer) if(this.curStep){ this.curStep = this.curStep.replace(/{{(\w+)}}/, (_, token) => (this.answers[token] || '' ) ) } } if(this.curStep) console.warn(`Stopped because cannot find step "${this.curStep}" !`) return(this.answers) } async askStep(step){ // Fills empty selectors let question = this.dialogData[step].question if((question.type=='select') && (question.choices===null)){ switch(question.name){ case 'eventType': question.choices = this.selectors.chan2eventTypes.hasOwnProperty(this.answers.channel) ? ['[NEW]', ...this.selectors.chan2eventTypes[this.answers.channel]] : question.choices = ['[NEW]'] break case 'action': question.choices = this.selectors.chan2actions.hasOwnProperty(this.answers.channel) ? ['[NEW]', ...this.selectors.chan2actions[this.answers.channel]] : question.choices = ['[NEW]'] break default: question.choices = ['[NEW]'] } } return(await prompt(this.dialogData[step].question)) } } async function startRedis(midasConfig) { let REDIScnx = new RedisConnexion({ debug: true, config: midasConfig, }); console.log('Starting REDIS...'); await REDIScnx.redisLogin(); console.log('REDIS Login OK'); return (REDIScnx); } async function inject(rediscnx, chan, packet){ await rediscnx.redisPublish(chan, packet) } async function batchInject(rediscnx, batch, delay=0){ const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)) for(let msg of batch){ if(msg.channel && msg.packet){ await rediscnx.redisPublish(msg.channel, msg.packet) } else console.error('Bad message format, skipped !?', msg) await sleep(delay) } } const argv = yargs(hideBin(process.argv)).command('Rabbit-Injector', 'Injects messages on bus', {}) .options({ 'file': { description: 'Instead of asking question, directly inject from a .dmsg file', alias: 'f', type: 'string' }, }).help().version('1.0').argv if(argv.file){ let fileName = argv.file if(!fileName.endsWith('.bmsg')) fileName+='.bmsg' if(fs.existsSync(fileName)){ let fileData try{ fileData = JSON.parse(fs.readFileSync(fileName)) } catch(err){ console.error('Bad json in file !?') process.exit(1) } let rediscnx = await startRedis(riConfig) if(Array.isArray(fileData)){ await batchInject(rediscnx, fileData) } else { if(Array.isArray(fileData.sequence)){ let batchIter = (parseInt(fileData.sequenceLoop)>1) ? parseInt(fileData.sequenceLoop) : 1 for(let i=0; i0) await batchInject(rediscnx, fileData.sequence, parseInt(fileData.sequenceDelay)) else await batchInject(rediscnx, fileData) } } else if(fileData.channel && fileData.packet){ await inject(rediscnx, fileData.channel, fileData.packet) } else console.error('Bad file format !?', fileData) } await rediscnx.redisClient.disconnect() } } else { let dialog = new Dialog(dialogData, selectors) dialog.start('step1').then(answers => { let packet = {} if(answers.type != 'other') packet[answers.type] = answers[answers.type] packet.sender = answers.uid packet.reqid = crypto.randomUUID() if(answers.payload) packet.payload = answers.payload if(answers.confirm=="send"){ inject(answers.channel, packet) } else if(answers.confirm=="save"){ prompt({'type': 'input', 'name': 'filename', 'message': 'Filename ?'}).then(answer =>{ let fileName = answer.filename if(!fileName.endsWith('.bmsg')) fileName+='.bmsg' fs.writeFileSync(fileName, JSON.stringify({ channel: answers.channel, packet: packet})) }) } }) }