@asyncapi/fluffy-robot
Advanced tools
Comparing version 0.2.0 to 0.3.0
{ | ||
"name": "@asyncapi/fluffy-robot", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"type": "commonjs", | ||
@@ -36,2 +36,3 @@ "description": "The asyncapi command line library to test your app at scale.", | ||
"ajv": "^8.6.0", | ||
"async-mqtt": "^2.6.1", | ||
"chalk": "^4.1.0", | ||
@@ -46,6 +47,6 @@ "commander": "^7.2.0", | ||
"@semantic-release/release-notes-generator": "^9.0.1", | ||
"@types/node": "^14.17.3", | ||
"@types/node": "^14.17.5", | ||
"all-contributors-cli": "^6.14.2", | ||
"conventional-changelog-conventionalcommits": "^4.4.0", | ||
"eslint": "^7.29.0", | ||
"eslint": "^7.31.0", | ||
"eslint-plugin-mocha": "^9.0.0", | ||
@@ -52,0 +53,0 @@ "eslint-plugin-security": "^1.4.0", |
@@ -1,4 +0,8 @@ | ||
# AsyncApi Performance Tester | ||
# AsyncApi Simulator | ||
___ | ||
In development | ||
--- | ||
Ever wondered what it would feel like your application to | ||
@@ -10,5 +14,6 @@ be the center of interest? | ||
#### Define and simulate high traffic | ||
#### scenarios for your app and create statistics. | ||
#### Define and simulate scenarios for your applications and create statistics. | ||
Usage | ||
@@ -21,4 +26,43 @@ | ||
Run sample application by specifying the corresponding | ||
AsyncApi and scenario files. | ||
``` | ||
simulator -f ./example-projects/game-processor/asyncapi.yaml -s ./example-projects/game-processor/scenario.yaml | ||
or | ||
simulator -b ../ -f ./simulatorFolder/example-projects/game-processor/asyncapi.yaml -s ./simulatorFolder/example-projects/game-processor/scenario.yaml | ||
``` | ||
### Cli | ||
``` | ||
Options: | ||
-v AsyncApi simulator cli version. | ||
-f, --filepath <type> The filepath of a AsyncAPI document, as either yaml or json file. | ||
-s, --scenario <type> The filepath of a json or yaml file which defines a scenario based on the spec. | ||
-b, --basedir <type> The basePath from which relative paths are computed. | ||
Defaults to the directory where simulator.sh resides. (default: "./"). | ||
-h, --help Display help for flags and commands. | ||
``` | ||
### Supported Protocols | ||
- mqtt | ||
### AsyncApi File | ||
The file where the api you want to test is defined. By specifying the x-plot: {id} field | ||
under a channel will automatically make the channel available for sending requests. | ||
### Scenario File | ||
Here with the plot-{id} (where id is the same as the x-plot: {id} in the field you specified in the AsyncAPI channel) field you: | ||
- Connect your AsyncApi and scenario File. | ||
- Specify the parameters for each channel and have the options for them to be randomly generated. | ||
- Specify the payload you want to send. | ||
@@ -9,24 +9,51 @@ #!/usr/bin/env node | ||
const {scenarioParserAndConnector} = require('../parser/index'); | ||
const {RequestManager} = require('../RequestHandler/RequestManager'); | ||
const rdInterface = readline.createInterface({ | ||
input: process.stdin, | ||
output: process.stdout | ||
}); | ||
function parseAsyncApi(handlingContext) { | ||
if (handlingContext.ready && handlingContext.scenarioReady) { | ||
handlingContext.ParsedAndFormated = scenarioParserAndConnector(handlingContext.file,handlingContext.scenarioFile); | ||
} else { | ||
console.log('\nUnable to complete AsyncApi File parsing. The file is either non-Existent or there was an unknown Error.\nPress Ctrl + c to terminate'); | ||
const enumerateOptions = (serverNames) => { | ||
let result = ''; | ||
serverNames.forEach((value,i) => { | ||
result += `\n${i}: ${value}`; | ||
}); | ||
return result; | ||
}; | ||
const checkFilepath = (asyncFile,regex,basedir) => { | ||
if (!String(asyncFile).match(regex)) { | ||
console.log('\nError: Filepath provide does not point to a yaml or json file. You must provided either a yaml or json.'); | ||
return false; | ||
} | ||
} | ||
try { | ||
if (!basedir) { | ||
filesystem.accessSync(asyncFile , filesystem.constants.R_OK); | ||
} else { | ||
filesystem.accessSync(path.resolve(basedir, asyncFile) , filesystem.constants.R_OK); | ||
} | ||
} catch (err) { | ||
console.log('Error: Accessing the provided file. Make sure it exists and the user in the terminal session has read access rights.'); | ||
return false; | ||
} | ||
return true; | ||
}; | ||
const inputLoopScenario = (handlingContext) => { | ||
handlingContext.rd.question(`\nEnter a proper ${chalk.blueBright('scenario')} document filepath:`, (scenarioFile) => { | ||
filesystem.access(scenarioFile, 1, (err) => { | ||
if (err) { | ||
console.log(`\nError in accessing provided ${chalk.blueBright('scenario')} file \nDetails:${err}\n\n`); | ||
inputLoopScenario(handlingContext); | ||
} else if (!String(scenarioFile).match(handlingContext.yamlJsonRegex)) { | ||
console.log(`\nPlease provide a proper ${chalk.blueBright('scenario')} filepath ex: ./myAsyncApi.json ./myAsyncApi.yaml:`); | ||
inputLoopScenario(handlingContext); | ||
const inputLoopServer = (availableServers) => { | ||
return new Promise((resolve) => { | ||
rdInterface.question('\nSelect the server you want to target\n' + 'Options' + `\n${enumerateOptions(availableServers)}\nSelect:` , (selectedServer) => { | ||
const questionLoop = (availableServers) => { | ||
rdInterface.question('\nPlease select one server id number from the list\n' + 'Options:' + `\n${enumerateOptions(availableServers)}\nSelect:` , (selectedServer) => { | ||
if (selectedServer < 0 || selectedServer > availableServers.length -1) { | ||
questionLoop(availableServers); | ||
} else { | ||
resolve(availableServers[parseInt(selectedServer, 10)]); | ||
} | ||
}); | ||
}; | ||
if (selectedServer < 0 || selectedServer > availableServers.length -1) { | ||
questionLoop(availableServers); | ||
} else { | ||
handlingContext.scenarioFile = scenarioFile; | ||
handlingContext.scenarioReady = true; | ||
parseAsyncApi(handlingContext); | ||
resolve(availableServers[parseInt(selectedServer, 10)]); | ||
} | ||
@@ -37,52 +64,51 @@ }); | ||
const inputLoopAsyncApi = (handlingContext) => { | ||
handlingContext.rd.question(`\nEnter a proper ${chalk.green('asyncApi')} document filepath:`, (filepath) => { | ||
filesystem.access(filepath, 1, (err) => { | ||
if (err) { | ||
console.log(`\nError in accessing provided file \nDetails:${err}\n\n`); | ||
inputLoopAsyncApi(handlingContext); | ||
} else if (!String(filepath).match(handlingContext.yamlJsonRegex)) { | ||
console.log('\nPlease provide a proper filepath ex: ./myAsyncApi.json ./myAsyncApi.yaml:'); | ||
inputLoopAsyncApi(handlingContext); | ||
} else { | ||
handlingContext.ready = true; | ||
handlingContext.file = filepath; | ||
if (!handlingContext.scenarioFile) { | ||
inputLoopScenario(handlingContext); | ||
} | ||
// eslint-disable-next-line sonarjs/no-identical-functions | ||
filesystem.access(handlingContext.scenarioFile, 1, (err) => { | ||
if (err) { | ||
console.log(`\nError in accessing provided ${chalk.blueBright('scenario')} file \nDetails:${err}\n\n`); | ||
inputLoopScenario(handlingContext); | ||
} else if (!String(handlingContext.scenarioFile).match(handlingContext.yamlJsonRegex)) { | ||
console.log(`\nPlease provide a proper ${chalk.blueBright('scenario')} filepath ex: ./myAsyncApi.json ./myAsyncApi.yaml:`); | ||
inputLoopScenario(handlingContext); | ||
const inputLoopScenario = (rd,scenario,regex,basedir) => { | ||
const isFileValid = checkFilepath(scenario,regex,basedir); | ||
if (!isFileValid) { | ||
return new Promise((resolve) => { | ||
rd.question('\nPlease provide an existent yaml or json file .It should abide by the scenario json schema.\nScenario filepath:',(answer) => { | ||
const inputLoop = (filepath) => { | ||
const isFileValid = checkFilepath(filepath,regex,basedir); | ||
if (!isFileValid) { | ||
rd.question('Please fix errors and provide a correctly formatted and' + | ||
' accessible file in filepath.\nScenario filepath:',inputLoop); | ||
} else { | ||
//handlingContext.scenarioFile = handlingContext.scenarioFile; | ||
handlingContext.scenarioReady = true; | ||
parseAsyncApi(handlingContext); | ||
resolve(path.resolve(basedir,filepath)); | ||
} | ||
} | ||
); | ||
} | ||
}; | ||
inputLoop(answer); | ||
}); | ||
}); | ||
}); | ||
} | ||
return Promise.resolve(path.resolve(basedir,scenario)); | ||
}; | ||
const inputLoopAsyncApi = (rd,asyncFile,regex,basedir) => { | ||
const isFileValid = checkFilepath(asyncFile,regex,basedir); | ||
if (!isFileValid) { | ||
return new Promise((resolve) => { | ||
rd.question('\nPlease provide an existent yaml or json file.It should abide by the asyncApi Spec.\nAsyncApi filepath:',(answer) => { | ||
const inputLoop = (filepath) => { | ||
const isFileValid = checkFilepath(filepath,regex,basedir); | ||
if (!isFileValid) { | ||
rd.question('Please fix errors and provide a correctly formatted and accessible file in filepath.\nAsyncApi Filepath:',inputLoop); | ||
} else resolve(path.resolve(basedir,filepath)); | ||
}; | ||
inputLoop(answer); | ||
}); | ||
}); | ||
} | ||
return Promise.resolve(path.resolve(basedir,asyncFile)); | ||
}; | ||
/** | ||
* Verifies the command line arguments, re-prompts in case of error. Parses file and returns the object representation. | ||
* Verifies the command line arguments, re-prompts in case of error. Parses AsyncApi File and returns the object representation. | ||
* @returns {Promise<*|null>} | ||
* @param rd | ||
* @param file | ||
* @param asyncApiFilepath | ||
* @param scenarioFile | ||
* @param basedir | ||
*/ | ||
const verifyInput_ParseAndLinkFiles = async (rd, file,scenarioFile) => { | ||
const handlingContext = this; | ||
handlingContext.ready = false; | ||
handlingContext.rd = rd; | ||
handlingContext.file = file; | ||
handlingContext.scenarioFile= scenarioFile; | ||
const yamlJsonRegex = RegExp(/^.*\.(json|yaml)$/, 'gm'); | ||
handlingContext.yamlJsonRegex = yamlJsonRegex; | ||
const verifyInputGetData = async (rd, asyncApiFilepath,scenarioFile,basedir) => { | ||
const yamlJsonRegex = new RegExp(/^.*\.(json|yaml)$/, 'gm'); | ||
@@ -94,51 +120,34 @@ console.log(chalk.blueBright(` | ||
if (!!file) { | ||
if (!String(file).match(yamlJsonRegex)) { | ||
console.log('\nPlease provide a correctly formatted filepath ex: ./myAsyncApi.json ./myAsyncApi.yaml:'); | ||
inputLoopAsyncApi(handlingContext); | ||
} else { | ||
handlingContext.file = file; | ||
handlingContext.ready = true; | ||
if (!scenarioFile) { | ||
inputLoopScenario(handlingContext); | ||
} else if (!String(scenarioFile).match(yamlJsonRegex)) { | ||
inputLoopScenario(handlingContext); | ||
} else { | ||
handlingContext.scenarioFile = scenarioFile; | ||
handlingContext.scenarioReady = true; | ||
parseAsyncApi(handlingContext); | ||
} | ||
} | ||
} else { | ||
console.log('\nFilepath not provided'); | ||
inputLoopAsyncApi(handlingContext); | ||
} | ||
asyncApiFilepath = await inputLoopAsyncApi(rd,asyncApiFilepath,yamlJsonRegex,basedir); | ||
return handlingContext.ready ? await handlingContext.ParsedAndFormated : null; | ||
scenarioFile = await inputLoopScenario(rd,scenarioFile,yamlJsonRegex,basedir); | ||
const structuredData = await scenarioParserAndConnector(asyncApiFilepath,scenarioFile); | ||
const availableServers = Object.keys(structuredData.servers); | ||
structuredData.targetedServer = await inputLoopServer(availableServers); | ||
return structuredData; | ||
}; | ||
(async function Main () { | ||
program.version('0.0.1', 'v', 'async-api performance tester cli version'); | ||
program.version('0.0.1', '-v', 'AsyncApi simulator cli version.'); | ||
program | ||
.requiredOption('-f, --filepath <type>', 'The filepath of a async-api specification yaml or json file') | ||
.requiredOption('-s, --scenario <type>', 'The filepath of a file defining a scenario based on the spec.') | ||
.option('-b, --basedir <type>', 'The basepath from which relative paths are computed.\nDefaults to the directory where simulator.sh resides.'); | ||
.requiredOption('-f, --filepath <type>', 'The filepath of a AsyncAPI document, as either yaml or json file.') | ||
.requiredOption('-s, --scenario <type>', 'The filepath of a json or yaml file which defines a scenario based on the spec.') | ||
.option('-b, --basedir <type>', 'The basePath from which relative paths are computed.\nDefaults to the directory where simulator.sh resides.','./'); | ||
program.parse(process.argv); | ||
///Interface , SignalsHandling | ||
const cliInterface = readline.createInterface({ | ||
input: process.stdin, | ||
output: process.stdout | ||
}); | ||
cliInterface.on('SIGINT', () => { | ||
rdInterface.on('SIGINT', () => { | ||
console.log('\nShutting down'); | ||
process.exit(); | ||
}); | ||
cliInterface.on('close', () => { | ||
rdInterface.on('close', () => { | ||
console.log('\nAsync-api performance tester instance closed'); | ||
process.exit(); | ||
}); | ||
cliInterface.on('uncaughtException', (err) => { | ||
rdInterface.on('uncaughtException', (err) => { | ||
console.log(err); | ||
@@ -159,6 +168,17 @@ process.exit(); | ||
}); | ||
let asyncApiPath; | ||
let scenarioPath; | ||
const options = program.opts(); | ||
if (options.basedir) { | ||
asyncApiPath = path.resolve(options.basedir,options.filepath); | ||
scenarioPath = path.resolve(options.basedir,options.scenario); | ||
} else { | ||
asyncApiPath = path.resolve(options.filepath); | ||
scenarioPath = path.resolve(options.scenario); | ||
} | ||
const structuredData = await verifyInputGetData(rdInterface, path.resolve(asyncApiPath),path.resolve(scenarioPath),options.basedir); | ||
const manager = RequestManager(); | ||
await manager.createReqHandler(structuredData); | ||
await manager.startOperations(); | ||
}()); | ||
await verifyInput_ParseAndLinkFiles(cliInterface, path.resolve(options.filepath),path.resolve(options.scenario)); | ||
}()); |
@@ -72,4 +72,6 @@ const parser = require('@asyncapi/parser'); | ||
const eps = value.eps; | ||
const parameters = value.parameters; | ||
const payload = value.payload; | ||
if (parserContext.PublishOperations.soloOps.hasOwnProperty(plotId[0])) { | ||
Object.assign(parserContext.PublishOperations.soloOps[plotId[0]],{eventsPsec: eps}); | ||
Object.assign(parserContext.PublishOperations.soloOps[plotId[0]],{eventsPsec: eps,parameters,payload}); | ||
} | ||
@@ -76,0 +78,0 @@ } |
@@ -21,3 +21,3 @@ { | ||
}, | ||
"^plot-[\\w\\d]+$": { | ||
"^plot-[\\w\\d]+$" : { | ||
"type": "object", | ||
@@ -33,2 +33,18 @@ "additionalProperties": false, | ||
"description": "This determines whether it should be prioritized in case the resources to emit events is insufficient " | ||
}, | ||
"payload": { | ||
"type": "object", | ||
"additionalProperties": true, | ||
"description": "The payload that is going to be sent is currently specifically defined." | ||
}, | ||
"parameters": { | ||
"type": "object", | ||
"description": "Specify the parameter instance variables of the channel, if any are present.", | ||
"additionalProperties": false, | ||
"patternProperties": { | ||
"^[\\w\\d]{1,28}$" : { | ||
"type": "string", | ||
"description": "A parameter instance variable with the corresponding assigned value as string." | ||
} | ||
} | ||
} | ||
@@ -35,0 +51,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
113705
43
1647
67
6
+ Addedasync-mqtt@^2.6.1
+ Addedasync-mqtt@2.6.3(transitive)
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbase64-js@1.5.1(transitive)
+ Addedbl@4.1.0(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedbuffer@5.7.1(transitive)
+ Addedbuffer-from@1.1.2(transitive)
+ Addedcommist@1.1.0(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedconcat-stream@2.0.0(transitive)
+ Addeddebug@4.4.0(transitive)
+ Addedduplexify@4.1.3(transitive)
+ Addedend-of-stream@1.4.4(transitive)
+ Addedfs.realpath@1.0.0(transitive)
+ Addedglob@7.2.3(transitive)
+ Addedhelp-me@3.0.0(transitive)
+ Addedieee754@1.2.1(transitive)
+ Addedinflight@1.0.6(transitive)
+ Addedinherits@2.0.4(transitive)
+ Addedjs-sdsl@4.3.0(transitive)
+ Addedleven@2.1.0(transitive)
+ Addedlru-cache@6.0.0(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedminimist@1.2.8(transitive)
+ Addedmqtt@4.3.8(transitive)
+ Addedmqtt-packet@6.10.0(transitive)
+ Addedms@2.1.3(transitive)
+ Addednumber-allocator@1.0.14(transitive)
+ Addedonce@1.4.0(transitive)
+ Addedpath-is-absolute@1.0.1(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedpump@3.0.2(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedreinterval@1.1.0(transitive)
+ Addedrfdc@1.4.1(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedsplit2@3.2.2(transitive)
+ Addedstream-shift@1.0.3(transitive)
+ Addedstring_decoder@1.3.0(transitive)
+ Addedtypedarray@0.0.6(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addedwrappy@1.0.2(transitive)
+ Addedws@7.5.10(transitive)
+ Addedxtend@4.0.2(transitive)
+ Addedyallist@4.0.0(transitive)