Skip to content

Commit

Permalink
docs: update project readme and template readme with updated instruct…
Browse files Browse the repository at this point in the history
…ions (#269)

Co-authored-by: derberg <[email protected]>
  • Loading branch information
kaushik-rishi and derberg authored Apr 29, 2024
1 parent 09a745a commit 6a0ff0b
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 126 deletions.
136 changes: 29 additions & 107 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
- [Specification requirements](#specification-requirements)
- [Supported protocols](#supported-protocols)
- [How to use the template](#how-to-use-the-template)
* [CLI](#cli)
* [Adding custom code / handlers](#adding-custom-code--handlers)
- [CLI](#cli)
- [Adding custom code / handlers](#adding-custom-code--handlers)
- [Template configuration](#template-configuration)
- [Development](#development)
- [Contributors](#contributors)
Expand Down Expand Up @@ -58,47 +58,58 @@ Since you can have multiple different security schemes, to use the one of X509 t
### CLI

```bash
# Install the AsyncAPI Generator
$ npm install -g @asyncapi/generator
# Install the AsyncAPI CLI
# Follow https://www.asyncapi.com/docs/tools/generator/installation-guide for more installation options
$ npm install -g @asyncapi/cli

# Run generation
# Run generation from the root of the template to use tes MQTT example
# To use the template
$ ag https://bit.ly/asyncapi @asyncapi/nodejs-template -o output -p server=production
$ asyncapi generate fromTemplate test/mocks/mqtt/asyncapi.yml @asyncapi/nodejs-template -o test/output -p server=production

# OR

# To test your local changes
$ ag https://bit.ly/asyncapi ./ -o output -p server=production
# To test your local changes provided inside template
$ asyncapi generate fromTemplate test/mocks/mqtt/asyncapi.yml ./ -o test/output -p server=production

##
## Start the server
## Install dependencies of generated library
##

# Go to the generated server
$ cd output
$ cd test/output

# Build generated application
$ npm i

# Start server
# To enable production settings start the server with "NODE_ENV=production npm start"
$ npm start
##
## Start an example script that uses generated library
##

# Go back to root
$ cd ../..

# Start the script
# It is located in test/example/script.js
$ npm run test:example

##
## Start the client
## Install MQTT client locally to start simulating some traffic on the broker, to test if sample code works
##

#for testing your server you can use mqtt client. open a new terminal and install it using:
# for testing your server you can use mqtt client. Open a new terminal and install it using:
$ npm install mqtt -g

#You should see the sent message in the logs of the started example.
#Notice that the server automatically validates incoming messages and logs out validation errors

#publish an invalid message.
$ mqtt pub -t 'smartylighting/streetlights/1/0/event/123/lighting/measured' -h 'test.mosquitto.org' -m '{"id": 1, "lumens": "3", "sentAt": "2017-06-07T12:34:32.000Z"}'

#publish a valid message
$ mqtt pub -t 'smartylighting/streetlights/1/0/event/123/lighting/measured' -h 'test.mosquitto.org' -m '{"id": 1, "lumens": 3, "sentAt": "2017-06-07T12:34:32.000Z"}'

#You should see the sent message in the logs of the previously started server.
#Notice that the server automatically validates incoming messages and logs out validation errors
#you can also restart the example app that on startup produces some messages as well. Before you do it, subscribe with MQTT client to selected topic, to see actuall messages comming in from the broker
$ mqtt sub -t 'smartylighting/streetlights/1/0/action/1/turn/on' -h 'test.mosquitto.org'
```

### Adding custom code / handlers
Expand All @@ -111,98 +122,9 @@ To avoid this, user code remains external to the generated code, functioning as

Facilitating this separation involves creating handlers and associating them with their respective routes. These handlers can then be seamlessly integrated into the template's workflow by importing the appropriate methods to register the handlers. In doing so, the template's `client.register<operationId>Middleware` method becomes the bridge between the user-written handlers and the generated code. This can be used to register middlewares for specific methods on specific channels.

> The AsyncAPI file used for the example is [here](https://bit.ly/asyncapi)
```js
// output refers to the generated template folder
// You require the generated server. Running this code starts the server
// App exposes API to send messages
const { client } = require("./output");

// to start the app
client.init();

// Generated handlers that we use to react on consumer / produced messages are attached to the client
// through which we can register middleware functions

/**
*
*
* Example of how to process a message before it is sent to the broker
*
*
*/
function testPublish() {
// mosquitto_sub -h test.mosquitto.org -p 1883 -t "smartylighting/streetlights/1/0/action/12/turn/on"

// Registering your custom logic in a channel-specific handler
// the passed handler function is called once the app sends a message to the channel
// For example `client.app.send` sends a message to some channel using and before it is sent, you want to perform some other actions
// in such a case, you can register middlewares like below
client.registerTurnOnMiddleware((message) => { // `turnOn` is the respective operationId
console.log("hitting the middleware before publishing the message");
console.log(
`sending turn on message to streetlight ${message.params.streetlightId}`,
message.payload
);
});

client.app.send(
{ command: "off" },
{},
"smartylighting/streetlights/1/0/action/12/turn/on"
);
}


/**
*
*
* Example of how to work with generated code as a consumer
*
*
*/
function testSubscribe() {
// mosquitto_pub -h test.mosquitto.org -p 1883 -t "smartylighting/streetlights/1/0/event/101/lighting/measured" -m '{"lumens": 10}'

// Writing your custom logic that should be triggered when your app receives as message from a given channel
// Registering your custom logic in a channel-specific handler
// the passed handler functions are called once the app gets message sent to the channel

client.registerReceiveLightMeasurementMiddleware((message) => { // `recieveLightMeasurement` is the respective operationId
console.log("recieved in middleware 1", message.payload);
});

client.registerReceiveLightMeasurementMiddleware((message) => {
console.log("recieved in middleware 2", message.payload);
});
}

testPublish();
testSubscribe();

/**
*
*
* Example of how to produce a message using API of generated app independently from the handlers
*
*
*/

(function myLoop (i) {
setTimeout(() => {
console.log('producing custom message');
client.app.send({percentage: 1}, {}, 'smartylighting/streetlights/1/0/action/1/turn/on');
if (--i) myLoop(i);
}, 1000);
}(3));
Look closely into [example script](test/example/script.js) that works with library generated using [this MQTT based AsyncAPI document](test/mocks/mqtt/asyncapi.yml). Look at available handlers API for reading incomming messages and processing outgoing messages. Learn how to start generated server with `init()` and also learn how to send messages, if needed.
```
You can run the above code and test the working of the handlers by sending a message using the mqtt cli / mosquitto broker software to the `smartylighting/streetlights/1/0/event/123/lighting/measured` channel using this command
`mosquitto_pub -h test.mosquitto.org -p 1883 -t "smartylighting/streetlights/1/0/event/101/lighting/measured" -m '{"lumens": 10, "sentAt": "2017-06-07T12:34:32.000Z"}'`
or
`mqtt pub -t 'smartylighting/streetlights/1/0/event/123/lighting/measured' -h 'test.mosquitto.org' -m '{"id": 1, "lumens": 3, }'` (if you are using the mqtt cli)

## Template configuration
You can configure this template by passing different parameters in the Generator CLI: `-p PARAM1_NAME=PARAM1_VALUE -p PARAM2_NAME=PARAM2_VALUE`
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"scripts": {
"test": "rimraf test/temp && jest --modulePathIgnorePatterns='./template'",
"test:updateSnapshot": "rimraf test/temp && jest --updateSnapshot --modulePathIgnorePatterns='./template'",
"test:example": "node test/example/script.js",
"lint": "eslint --max-warnings 0 --config .eslintrc .",
"lint:fix": "eslint --fix --config .eslintrc .",
"generate:assets": "npm run generate:readme:toc",
Expand Down
29 changes: 19 additions & 10 deletions template/README.md.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,31 @@ export default function readmeFile({asyncapi, params}) {
${ asyncapi.info().description() || '' }
## Running the server
## Set up your template
1. Install dependencies
\`\`\`sh
npm i
\`\`\`
${(params.securityScheme && (asyncapi.server(params.server).protocol() === 'kafka' || asyncapi.server(params.server).protocol() === 'kafka-secure') && asyncapi.components().securityScheme(params.securityScheme).type() === 'X509') ? '1. (Optional) For X509 security provide files with all data required to establish secure connection using certificates. Place files like `ca.pem`, `service.cert`, `service.key` in the root of the project or the location that you explicitly specified during generation.' : ''}
1. Start the server with default configuration
\`\`\`sh
npm start
\`\`\`
1. (Optional) Start server with secure production configuration
\`\`\`sh
NODE_ENV=production npm start
\`\`\`
> NODE_ENV=production relates to \`config/common.yml\` that contains different configurations for different environments. Starting server without \`NODE_ENV\` applies default configuration while starting the server as \`NODE_ENV=production npm start\` applies default configuration supplemented by configuration settings called \`production\`.`}
## Import and start
\`\`\`js
// output refers to the generated template folder
const { client } = require('../output'); // Modify require path if needed
client.init(); // starts the app
\`\`\`
## API
Use the \`client.register<OperationId>Middleware\` method as a bridge between the user-written handlers and the generated code. This can be used to register middlewares for specific methods on specific channels.
To send messages use built in sent function:
\`\`\`js
client.app.send({my: "message"}, {}, 'topic/name');
\`\`\`
`}
</File>;
}
3 changes: 0 additions & 3 deletions template/package.json.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,6 @@ export default function packageFile({ asyncapi, params }) {
packageJSON = {
...packageJSON,
main: './src/api',
scripts: {
start: 'node src/api/index.js',
},
dependencies,
};

Expand Down
6 changes: 0 additions & 6 deletions test/__snapshots__/integration.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -366,9 +366,6 @@ exports[`template integration tests for generated files using the generator and
\\"version\\": \\"1.0.0\\",
\\"description\\": \\"The Smartylighting Streetlights API allows you to remotely manage the city lights. ### Check out its awesome features: * Turn a specific streetlight on/off 🌃 * Dim a specific streetlight 😎 * Receive real-time information about environmental lighting conditions 📈\\",
\\"main\\": \\"./src/api\\",
\\"scripts\\": {
\\"start\\": \\"node src/api/index.js\\"
},
\\"dependencies\\": {
\\"chalk\\": \\"4.1.2\\",
\\"dotenv\\": \\"8.1.0\\",
Expand Down Expand Up @@ -748,9 +745,6 @@ exports[`template integration tests for generated files using the generator and
\\"version\\": \\"1.0.0\\",
\\"description\\": \\"The Smartylighting Streetlights API allows you to remotely manage the city lights. ### Check out its awesome features: * Turn a specific streetlight on/off 🌃 * Dim a specific streetlight 😎 * Receive real-time information about environmental lighting conditions 📈\\",
\\"main\\": \\"./src/api\\",
\\"scripts\\": {
\\"start\\": \\"node src/api/index.js\\"
},
\\"dependencies\\": {
\\"chalk\\": \\"4.1.2\\",
\\"dotenv\\": \\"8.1.0\\",
Expand Down
86 changes: 86 additions & 0 deletions test/example/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// output refers to the generated template folder
// You require the generated server. Running this code starts the server
// App exposes API to send messages
const { client } = require('../output'); // library is in the current directory. Modify require path if needed

client.init(); // starts the app

// Generated handlers that we use to react on consumer / produced messages are attached to the client
// through which we can register middleware functions

/**
*
*
* Example of how to process a message before it is sent to the broker
*
* Modify the following in the function \`testPublish\` according to your usecase
* 1. channel / topic name
* 2. tool used to publish message according to the procotol used (amqp/mqtt/etc ...)
* 3. name of the invocated middleware function (based on operation Id)
*/
function testPublish() {
// mosquitto_sub -h test.mosquitto.org -p 1883 -t "smartylighting/streetlights/1/0/action/12/turn/on"

// Registering your custom logic in a channel-specific handler
// the passed handler function is called once the app sends a message to the channel
// For example \`client.app.send\` sends a message to some channel using and before it is sent, you want to perform some other actions
// in such a case, you can register middlewares like below
client.registerTurnOnMiddleware((message) => { // \`turnOn\` is the respective operationId
console.log('hitting the middleware before publishing the message');
console.log('sending turn on message to streetlight test', message.payload);
});

client.app.send(
{ command: 'off' },
{},
'smartylighting/streetlights/1/0/action/12/turn/on'
);
}

/**
*
*
* Example of how to work with generated code as a consumer
*
* Modify the following in the function \`testSubscribe\` according to your usecase
* 1. channel / topic name
* 2. tool used to publish message according to the procotol used (amqp/mqtt/etc ...)
* 3. name of the invocated middleware function (based on operation Id)
*
*/
function testSubscribe() {
// mosquitto_pub -h test.mosquitto.org -p 1883 -t "smartylighting/streetlights/1/0/event/101/lighting/measured" -m '{"lumens": 10}'

// Writing your custom logic that should be triggered when your app receives as message from a given channel
// Registering your custom logic in a channel-specific handler
// the passed handler functions are called once the app gets message sent to the channel

client.registerReceiveLightMeasurementMiddleware((message) => { // \`recieveLightMeasurement\` is the respective operationId
console.log('recieved in middleware 1', message.payload);
});

client.registerReceiveLightMeasurementMiddleware((message) => {
console.log('recieved in middleware 2', message.payload);
});
}

testPublish();
testSubscribe();

/**
*
*
* Example of how to produce a message using API of generated app independently from the handlers
* Again, will have to modify the below according to your usecase:
* 1. payload
* 2. channel / topic name
*
*/

(function myLoop (i) {
setTimeout(() => {
console.log('producing custom message');
client.app.send({percentage: 1}, {}, 'smartylighting/streetlights/1/0/action/1/turn/on');
if (--i) myLoop(i);
}, 1000);
}(3));

0 comments on commit 6a0ff0b

Please sign in to comment.