Skip to content

Commit 5cf3422

Browse files
Harsh16guptaasyncapi-botderberg
authored
docs: rewrite generator template tutorial to AsyncAPI v3 (#1826)
Co-authored-by: Harsh Gupta <harsh16official@gmail.com> Co-authored-by: Chan <bot+chan@asyncapi.io> Co-authored-by: Lukasz Gornicki <lpgornicki@gmail.com>
1 parent 1689b66 commit 5cf3422

File tree

1 file changed

+75
-65
lines changed

1 file changed

+75
-65
lines changed

apps/generator/docs/generator-template.md

Lines changed: 75 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Suppose you can only sleep when the AC in your bedroom is set to 22 °C, and you
99

1010
In this tutorial:
1111

12-
- You'll use the [Eclipse Mosquito](https://test.mosquitto.org) **MQTT broker**, which you'll connect to subscribe and publish messages using an MQTT client.
12+
- You'll use the [Eclipse Mosquitto](https://test.mosquitto.org) **MQTT broker**, which you'll connect to subscribe and publish messages using an MQTT client.
1313
- You'll use [Python Paho-MQTT](https://pypi.org/project/paho-mqtt/) as the **MQTT client** in this project.
1414
- You'll create a React template that will use the MQTT broker to allow you to monitor your bedroom's temperature and notify you when the temperature drops or rises above 22 °C.
1515
- Lastly, create a reusable component for the output code's `sendTemperatureDrop` and `sendTemperatureRise` functions.
@@ -44,7 +44,7 @@ Before you create the template, you'll need to have an [AsyncAPI document](https
4444
4545
``` yml
4646
47-
asyncapi: 2.6.0
47+
asyncapi: 3.0.1
4848
4949
info:
5050
title: Temperature Service
@@ -53,22 +53,26 @@ info:
5353
5454
servers:
5555
dev:
56-
url: test.mosquitto.org #in case you're using local mosquitto instance, change this value to localhost.
56+
host: test.mosquitto.org #in case you're using local mosquitto instance, change this value to localhost.
5757
protocol: mqtt
5858
5959
channels:
60-
temperature/changed:
61-
description: Updates the bedroom temperature in the database when the temperatures drops or goes up.
62-
publish:
63-
operationId: temperatureChange
64-
message:
60+
temperatureChanged:
61+
address: temperature/changed
62+
messages:
63+
temperatureChange:
6564
description: Message that is being sent when the temperature in the bedroom changes.
6665
payload:
67-
type: object
68-
additionalProperties: false
69-
properties:
70-
temperatureId:
71-
type: string
66+
$ref: '#/components/schemas/temperatureId'
67+
description: Updates the bedroom temperature in the database when the temperatures drops or goes up.
68+
69+
operations:
70+
temperatureChange:
71+
action: receive
72+
summary: Message sent to the broker when the temperature is changed.
73+
channel:
74+
$ref: '#/channels/temperatureChanged'
75+
7276
components:
7377
schemas:
7478
temperatureId:
@@ -112,8 +116,8 @@ The **package.json** file is used to define the dependencies for your template.
112116
"version": "0.0.1",
113117
"description": "A template that generates a Python MQTT client using MQTT.",
114118
"generator": {
115-
"apiVersion": "v1",
116-
"generator": ">=1.10.0 <2.0.0",
119+
"apiVersion": "v3",
120+
"generator": ">=2.0.0 <4.0.0",
117121
"supportedProtocols": ["mqtt"]
118122
},
119123
"dependencies": {
@@ -326,8 +330,8 @@ In **package.json** you can have the scripts property that you invoke by calling
326330
"test": "npm run test:clean && npm run test:generate && npm run test:start"
327331
},
328332
"generator": {
329-
"apiVersion": "v1",
330-
"generator": ">=1.10.0 <2.0.0",
333+
"apiVersion": "v3",
334+
"generator": ">=2.0.0 <4.0.0",
331335
"supportedProtocols": ["mqtt"]
332336
},
333337
"dependencies": {
@@ -357,7 +361,7 @@ You often have different runtime environments in programming, e.g., development
357361
```yml
358362
servers:
359363
dev:
360-
url: test.mosquitto.org
364+
host: test.mosquitto.org
361365
protocol: mqtt
362366
```
363367
@@ -391,7 +395,7 @@ Update your `test:generate` script in **package.json** to include the server par
391395
"test:generate": "asyncapi generate fromTemplate test/fixtures/asyncapi.yml ./ --output test/project --force-write --param server=dev"
392396
```
393397
394-
You can now replace the static broker from `mqttBroker = 'test.mosquitto.org'` to `mqttBroker = "${asyncapi.servers().get(params.server).url()}"` in **index.js**.
398+
You can now replace the static broker from `mqttBroker = 'test.mosquitto.org'` to `mqttBroker = "${asyncapi.servers().get(params.server).host()}"` in **index.js**.
395399
396400
Now the template code looks like this:
397401
@@ -405,7 +409,7 @@ export default function ({ asyncapi, params }) {
405409
<File name="client.py">
406410
{`import paho.mqtt.client as mqtt
407411
408-
mqttBroker = "${asyncapi.servers().get(params.server).url()}"
412+
mqttBroker = "${asyncapi.servers().get(params.server).host()}"
409413
410414
class TemperatureServiceClient:
411415
def __init__(self):
@@ -436,7 +440,7 @@ export default function ({ asyncapi, params }) {
436440
// 2
437441
<Text newLines={2}>import paho.mqtt.client as mqtt</Text>
438442
// 3
439-
<Text newLines={2}>mqttBroker = "{asyncapi.servers().get(params.server).url()}"</Text>
443+
<Text newLines={2}>mqttBroker = "{asyncapi.servers().get(params.server).host()}"</Text>
440444
// 4
441445
<Text newLines={2}>class {asyncapi.info().title().replaceAll(' ', '')}Client:</Text>
442446
// 5
@@ -483,23 +487,23 @@ class TemperatureServiceClient:
483487
484488
```
485489
486-
You'll then need to template to dynamically generate `sendTemperatureDrop` and `sendTemperatureRise` functions in the generated code based off the AsyncAPI document content. The goal is to write template code that returns functions for channels that the Temperature Service application is subscribed to. The template code to generate these functions will look like this:
490+
You'll then need a template to dynamically generate `sendTemperatureDrop` and `sendTemperatureRise` functions in the generated code based off the AsyncAPI document content. The goal is to write template code that returns functions for operations marked with `action: receive`, using their associated channels. The template code to generate these functions will look like this:
487491
488492
```js
489493
<Text indent={2} newLines={2}>
490-
<TopicFunction channels={asyncapi.channels().filterByReceive()} />
494+
<TopicFunction operations={asyncapi.operations().filterByReceive()} />
491495
</Text>
492496
```
493497
494-
It's recommended to put reusable components outside the template directory in a new directory called components. You'll create a component that will dynamically generate functions in the output for as many channels as there are in your AsyncAPI document that contains a `publish` operation. Add the following code in the **python-mqtt-client-template/components/TopicFunction.js** file, after creating the **python-mqtt-client-template/components/** directory:
498+
It's recommended to put reusable components outside the template directory in a new directory called components. You'll create a component that will dynamically generate functions in the output for as many operations as there are in your AsyncAPI document that are marked with `action: receive`. Add the following code in the **python-mqtt-client-template/components/TopicFunction.js** file, after creating the **python-mqtt-client-template/components/** directory:
495499
496500
```js
497501
/*
498502
* This component returns a block of functions that user can use to send messages to specific topic.
499-
* As input it requires a list of Channel models from the parsed AsyncAPI document
503+
* As input it requires a list of Operation models from the parsed AsyncAPI document marked with `action: receive`.
500504
*/
501-
export function TopicFunction({ channels }) {
502-
const topicsDetails = getTopics(channels);
505+
export function TopicFunction({ operations }) {
506+
const topicsDetails = getTopics(operations);
503507
let functions = '';
504508
505509
topicsDetails.forEach((t) => {
@@ -518,25 +522,28 @@ export function TopicFunction({ channels }) {
518522
*
519523
* As input it requires a list of Channel models from the parsed AsyncAPI document
520524
*/
521-
function getTopics(channels) {
522-
const channelsCanSendTo = channels;
525+
function getTopics(operations) {
523526
let topicsDetails = [];
524527
525-
channelsCanSendTo.forEach((ch) => {
526-
const topic = {};
527-
const operationId = ch.operations().filterByReceive()[0].id();
528-
topic.name = operationId.charAt(0).toUpperCase() + operationId.slice(1);
529-
topic.topic = ch.address();
530-
531-
topicsDetails.push(topic);
532-
})
533-
528+
operations.forEach((op) => {
529+
const channels = op.channels().all();
530+
if (!channels.length) return;
531+
532+
const channel = channels[0];
533+
const operationId = op.operationId() || op.id();
534+
535+
topicsDetails.push({
536+
name: operationId.charAt(0).toUpperCase() + operationId.slice(1),
537+
topic: channel.address()
538+
});
539+
});
540+
534541
return topicsDetails;
535542
}
536543
```
537544
538-
`{ channels }`: the `TopicFunction` component accepts a custom prop called channels and in your template code
539-
`getTopics(channels)`: Returns a list of objects, one for each channel with two properties; name and topic. The **name** holds information about the `operationId` provided in the AsyncAPI document while the **topic** holds information about the address of the topic.
545+
`{ operations }`: the `TopicFunction` component accepts a custom prop called operations and in your template code
546+
`getTopics(operations)`: Returns a list of objects, one for each operation with two properties; name and topic. The **name** holds information about the `operationId` provided in the AsyncAPI document (or a generated ID if operationId is not set) while the **topic** holds information about the address of the topic from the operation's associated channel.
540547
541548
Import the `TopicFunction` component in your template code in **index.js** and add the template code to generate the functions to topics that the `Temperature Service` application is subscribed to. In your case, the final version of your template code should look like this:
542549
@@ -549,7 +556,7 @@ export default function ({ asyncapi, params }) {
549556
<File name="client.py">
550557
<Text newLines={2}>import paho.mqtt.client as mqtt</Text>
551558
552-
<Text newLines={2}>mqttBroker = "{asyncapi.servers().get(params.server).url()}"</Text>
559+
<Text newLines={2}>mqttBroker = "{asyncapi.servers().get(params.server).host()}"</Text>
553560
554561
<Text newLines={2}>class {asyncapi.info().title().replaceAll(' ', '')}Client:</Text>
555562
@@ -560,7 +567,7 @@ export default function ({ asyncapi, params }) {
560567
</Text>
561568
562569
<Text indent={2} newLines={2}>
563-
<TopicFunction channels={asyncapi.channels().filterByReceive()} />
570+
<TopicFunction operations={asyncapi.operations().filterByReceive()} />
564571
</Text>
565572
</File>
566573
)
@@ -587,39 +594,42 @@ python-mqtt-client-template
587594
588595
Run `npm test` on your terminal to ensure everything works as expected.
589596
590-
In the next section, you'll add another channel to **asyncapi.yml** file called `temperature/dropped` and `temperature/risen` then run the template again to make sure it still works as expected.
597+
In the next section, you'll add another channel to **asyncapi.yml** file called `temperatureDropped` and `temperatureRisen` then run the template again to make sure it still works as expected.
591598
592599
#### 5d. Update AsyncAPI document
593600
594601
Update the AsyncAPI document to use two channels:
595602
596603
```yml
597604
channels:
598-
temperature/dropped:
599-
description: Notifies the user when the temperature drops past a certain point.
600-
publish:
601-
operationId: temperatureDrop
602-
message:
605+
temperatureDropped:
606+
address: temperature/dropped
607+
messages:
608+
temperatureDrop:
603609
description: Message that is being sent when the temperature drops past a certain point.
604610
payload:
605-
type: object
606-
additionalProperties: false
607-
properties:
608-
temperatureId:
609-
type: string
610-
611-
temperature/risen:
612-
description: Notifies the user when the temperature rises past a certain point.
613-
publish:
614-
operationId: temperatureRise
615-
message:
611+
$ref: '#/components/schemas/temperatureId'
612+
description: Notifies the user when the temperature drops past a certain point.
613+
temperatureRisen:
614+
address: temperature/risen
615+
messages:
616+
temperatureRise:
616617
description: Message that is being sent when the temperature rises past a certain point.
617618
payload:
618-
type: object
619-
additionalProperties: false
620-
properties:
621-
temperatureId:
622-
type: string
619+
$ref: '#/components/schemas/temperatureId'
620+
description: Notifies the user when the temperature rises past a certain point.
621+
622+
operations:
623+
temperatureDrop:
624+
action: receive
625+
summary: Message sent to the broker when the temperature is dropped.
626+
channel:
627+
$ref: '#/channels/temperatureDropped'
628+
temperatureRise:
629+
action: receive
630+
summary: Message sent to the broker when the temperature is risen.
631+
channel:
632+
$ref: '#/channels/temperatureRisen'
623633
```
624634
625635
And update your test script in test.py to test the two functions as below:
@@ -644,6 +654,6 @@ Temperature rise detected 66943992 sent to temperature/risen
644654
645655
Great job completing this tutorial! You have learnt how to use an AsyncAPI file to create a Python MQTT template and used it with the Paho-MQTT library in Python to connect to an MQTT broker and publish messages.😃
646656
647-
If you want to tinker with a completed template and see what it would look like in production, check out the [Paho-MQTT template](https://github.com/derberg/python-mqtt-client-template/tree/v1.0.0). You can also check out the accompanying [article about creating MQTT client code](https://www.brainfart.dev/blog/asyncapi-codegen-python).
657+
If you want to tinker with a completed template and see what it would look like in production, check out the [Paho-MQTT template](https://github.com/Harsh16gupta/asyncapi-v3-template-final).
648658
649659
You can also check out the [MQTT beginners guide](https://medium.com/python-point/mqtt-basics-with-python-examples-7c758e605d4) tutorial to learn more about asynchronous messaging using MQTT.

0 commit comments

Comments
 (0)