@asyncapi/generator 1.10.9 → 1.10.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/api.md CHANGED
@@ -47,7 +47,7 @@ Instantiates a new Generator object.
47
47
  - templateName `String` - Name of the template to generate.
48
48
  - targetDir `String` - Path to the directory where the files will be generated.
49
49
  - options `Object`
50
- - [.templateParams] `String` - Optional parameters to pass to the template. Each template define their own params.
50
+ - [.templateParams] `Object.<string, string>` - Optional parameters to pass to the template. Each template define their own params.
51
51
  - [.entrypoint] `String` - Name of the file to use as the entry point for the rendering process. Use in case you want to use only a specific template file. Note: this potentially avoids rendering every file in the template.
52
52
  - [.noOverwriteGlobs] `Array.<String>` - List of globs to skip when regenerating the template.
53
53
  - [.disabledHooks] `Object.<String, (Boolean|String|Array.<String>)>` - Object with hooks to disable. The key is a hook type. If key has "true" value, then the generator skips all hooks from the given type. If the value associated with a key is a string with the name of a single hook, then the generator skips only this single hook name. If the value associated with a key is an array of strings, then the generator skips only hooks from the array.
@@ -0,0 +1,575 @@
1
+ ---
2
+ title: "Creating a Generator template"
3
+ weight: 170
4
+ ---
5
+
6
+ This tutorial teaches you how to create a simple generator template using a Python MQTT client. You'll use the AsyncAPI document and the template you develop to generate Python code. Additionally, you'll create template code with a reusable component to reuse the custom functionality you create and test your code using an MQTT client.
7
+
8
+ Suppose you can only sleep when the AC in your bedroom is set to 22 °C, and you can't sleep when the temperature drops or rises above that. You can install a smart monitor in your bedroom that keeps track of the temperature and notifies you to adjust it to your optimum temperature when it fluctuates. You will create a template to alert you when the bedroom's temperature fluctuates from 22 °C.
9
+
10
+ In this tutorial:
11
+
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.
13
+ - You'll use [Python Paho-MQTT](https://pypi.org/project/paho-mqtt/) as the **MQTT client** in this project.
14
+ - 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.
15
+ - Lastly, create a reusable component for the output code's `sendTemperatureDrop` and `sendTemperatureRise` functions.
16
+
17
+ ## Background context
18
+
19
+ There is a list of [community maintained templates](https://www.asyncapi.com/docs/tools/generator/template#generator-templates-list), but what if you do not find what you need? In that case, you'll create a user-defined template that generates custom output from the generator.
20
+ Before you create the template, you'll need to have an [AsyncAPI document](https://www.asyncapi.com/docs/tools/generator/asyncapi-document) that defines the properties you want to use in your template to test against. In this tutorial, you'll use the following template saved in the **test/fixtures/asyncapi.yml** file in your template project directory.
21
+
22
+ ``` yml
23
+
24
+ asyncapi: 2.6.0
25
+
26
+ info:
27
+ title: Temperature Service
28
+ version: 1.0.0
29
+ description: This service is in charge of processing all the events related to temperature.
30
+
31
+ servers:
32
+ dev:
33
+ url: test.mosquitto.org
34
+ protocol: mqtt
35
+
36
+ channels:
37
+ temperature/changed:
38
+ description: Updates the bedroom temperature in the database when the temperatures drops or goes up.
39
+ publish:
40
+ operationId: temperatureChange
41
+ message:
42
+ description: Message that is being sent when the temperature in the bedroom changes.
43
+ payload:
44
+ type: object
45
+ additionalProperties: false
46
+ properties:
47
+ temperatureId:
48
+ type: string
49
+ components:
50
+ schemas:
51
+ temperatureId:
52
+ type: object
53
+ additionalProperties: false
54
+ properties:
55
+ temperatureId:
56
+ type: string
57
+ ```
58
+
59
+ <Remember>
60
+
61
+ - To generate code, use the [AsyncAPI CLI](https://www.asyncapi.com/tools/cli). If you don't have the CLI installed, follow [CLI installation guide](/docs/tools/generator/installation-guide#asyncapi-cli).
62
+ - If you are new to AsyncAPI Generator, check out the following docs: [template development](/docs/tools/generator/template-development), which explains the minimum requirements for a template and possible features.
63
+
64
+ </Remember>
65
+
66
+ ## Overview of steps
67
+
68
+ 1. Create a new directory for your template named **python-mqtt-client-template**.
69
+ 2. Install the AsyncAPI CLI using the command `npm install -g @asyncapi/cli`.
70
+ 3. Create a new folder **test/fixtures** with a file named **asyncapi.yml** in your fixtures directory. This file is used to define the **structure** of your template.
71
+ 4. Create a new file named **package.json** in your template directory. This file is used to define the **dependencies** for your template.
72
+ 5. Create a new file named **index.js** in your **template** directory. This file is used to define the **logic** for your template.
73
+ 6. Create a **test.py** file to validate the logic of your application.
74
+
75
+ Lets break it down:
76
+
77
+ ### package.json file
78
+
79
+ The **package.json** file is used to define the dependencies for your template. Add the following code snippet to your **package.json** file:
80
+
81
+ ``` json
82
+ {
83
+ "name": "python-mqtt-client-template",
84
+ "version": "0.0.1",
85
+ "description": "A template that generates a Python MQTT client using MQTT.",
86
+ "generator": {
87
+ "renderer": "react",
88
+ "apiVersion": "v1",
89
+ "generator": ">=1.10.0 <2.0.0",
90
+ "supportedProtocols": ["mqtt"]
91
+ },
92
+ "dependencies": {
93
+ "@asyncapi/generator-react-sdk": "^0.2.25"
94
+ },
95
+ "devDependencies": {
96
+ "rimraf": "^5.0.0"
97
+ }
98
+ }
99
+ ```
100
+
101
+ Here's what is contained in the code snippet above:
102
+
103
+ - **name** -the name of your template.
104
+ - **version** - the current version of your template.
105
+ - **description** - a description of what your template does.
106
+ - **generator** - specify generator [specific configuration](https://www.asyncapi.com/docs/tools/generator/configuration-file).
107
+ - **renderer** - can either be [`react`](https://www.asyncapi.com/docs/tools/generator/react-render-engine) or [`nunjucks`](https://www.asyncapi.com/docs/tools/generator/nunjucks-render-engine). In this case the generator will pass your template to the react render engine to generate the output.
108
+ - **apiVersion** - specifies which major version of the [Parser-API](https://github.com/asyncapi/parser-api) your template will use.
109
+ - **generator** - a string representing the generator version-range your template is compatible with.
110
+ - **supportedProtocols** - A list that specifies which protocols are supported by your template.
111
+ - **dependencies** - specifies which version of [`@asyncapi/generator-react-sdk`](https://github.com/asyncapi/generator-react-sdk) should be used.
112
+
113
+ Run the command `npm install` on your terminal to install the dependencies specified in **package.json**.
114
+
115
+ ### index.js file
116
+
117
+ The **index.js** file is used to define the logic for your template. Inside the template folder, create an **index.js** file and add the code snippet below:
118
+
119
+ ```js
120
+ //1
121
+ import { File } from '@asyncapi/generator-react-sdk'
122
+ //2
123
+ export default function ({ asyncapi }) {
124
+ //3
125
+ return <File name="client.py">{asyncapi.info().title()}</File>
126
+ }
127
+ ```
128
+
129
+ The code snippet above does the following:
130
+
131
+ 1. Import the `generator-react-sdk` dependency.
132
+ 2. The `asyncapi` argument is an instance of the [AsyncAPI Parser](https://www.asyncapi.com/docs/tools/generator/parser). It will allow you to access the content of the AsyncAPI document in your template using helper functions.
133
+ 3. The `asyncapi.info().title()` is using the info() helper function to return the info object from the AsyncAPI document illustrated in the code snippet below:
134
+
135
+ ``` json
136
+ info:
137
+ title: Temperature Service
138
+ version: 1.0.0
139
+ description: This service is in charge of processing all the events related to temperature.
140
+ ```
141
+
142
+ The `asyncapi.info().title()` returns `Temperature Service`.
143
+
144
+ ### Test using AsyncAPI CLI
145
+
146
+ To see this in action, run `asyncapi generate fromTemplate test/fixtures/asyncapi.yml ./ -o test/project` command on your terminal. If successful, you'll see the message below on your terminal:
147
+
148
+ ``` cmd
149
+ Generation in progress. Keep calm and wait a bit... done
150
+ Check out your shiny new generated files at output.
151
+ ```
152
+
153
+ Navigating to the **test/project** directory. You should see a **client.py** file; the only content is `Temperature Service`.
154
+
155
+ Let's break down the previous command:
156
+
157
+ - `asyncapi generate fromTemplate` is how you use AsyncAPI generator via the AsyncAPI CLI.
158
+ - `test/fixtures/asyncapi.yml` points to your AsyncAPI document.
159
+ - `./` specifies the location of your template.
160
+ - `-o` specifies where to output the result.
161
+
162
+ ## Creating a template
163
+
164
+ You will create an MQTT-supported template that will generate a Python client from the template and the AsyncAPI document above.
165
+
166
+ In this section, you'll:
167
+
168
+ 1. Write the MQTT client code.
169
+ 2. Write code to test the client works.
170
+ 3. Update the template to use the client code.
171
+ 4. Setup a script to help you run this code.
172
+ 5. Template your code.
173
+
174
+ ### 1. Create the client
175
+
176
+ The following is the sample code of the Python client you generated [above](#test-using-asyncapi-cli) using the Paho-MQTT library after running the `asyncapi generate fromTemplate test/fixtures/asyncapi.yml ./ -o test/project` command.
177
+
178
+ ``` python
179
+ # 1
180
+ import paho.mqtt.client as mqtt
181
+ # 2
182
+ mqttBroker = "test.mosquitto.org"
183
+
184
+ class TemperatureServiceClient:
185
+ def __init__(self):
186
+ # 3
187
+ self.client = mqtt.Client()
188
+ # 4
189
+ self.client.connect(mqttBroker)
190
+
191
+
192
+ def sendTemperatureChange(self, id):
193
+ # 5
194
+ topic = "temperature/changed"
195
+ # 6
196
+ self.client.publish(topic, id)
197
+ ```
198
+
199
+ Make sure you have the Paho-MQTT library installed. You can install it using pip with the `pip install paho-mqtt` command.
200
+ Let's break down the previous code snippet:
201
+
202
+ 1. Imports the MQTT module from the Paho package, which provides the MQTT client functionality.
203
+ 2. Assigns the MQTT broker address `test.mosquitto.org` to the variable MQTT broker. This specifies the location where the MQTT client will connect to.
204
+ 3. Defines an instance of the MQTT client object. This object will be used to establish a connection with the MQTT broker and perform MQTT operations.
205
+ 4. Defines that on client instance creation, it connects to the broker.
206
+ 5. The `sendTemperatureChange` is a function the client user invokes to publish a message to the broker, and its specific topic.
207
+
208
+ In summary, this code sets up an MQTT client using the Paho-MQTT library. It connects to the `test.mosquitto.org` MQTT broker, and the `sendTemperatureChange()` method publishes temperature change information to the `temperature/changed` topic whenever called.
209
+
210
+ ### 2. Test the client
211
+
212
+ You'll interact with the Temperature Service using the client module you created above. You'll create an instance of the client using `client = TemperatureServiceClient()` and then use `client.sendTemperatureChange` function to publish messages that Temperature Service is subscribed to.
213
+ Create a **test/project/test.py** file in your project and add the code snippet below:
214
+
215
+ ``` python
216
+ from client import TemperatureServiceClient
217
+ from random import randrange
218
+ import time
219
+
220
+ client = TemperatureServiceClient()
221
+
222
+ id_length = 8
223
+ min_value = 10**(id_length-1) # Minimum value with 8 digits (e.g., 10000000)
224
+ max_value = 10**id_length - 1 # Maximum value with 8 digits (e.g., 99999999)
225
+
226
+ while True:
227
+ randomId = randrange(min_value, max_value + 1)
228
+ client.sendTemperatureChange(randomId)
229
+ print("New temperature detected " + str(randomId) + " sent to temperature/changed")
230
+ time.sleep(1)
231
+
232
+ ```
233
+
234
+ Run the code above in your terminal using the command `python test.py`. You should see output similar to the snippet below logged on your terminal:
235
+
236
+ ``` cmd
237
+ New temperature detected 64250266 sent to temperature/changed
238
+ New temperature detected 36947728 sent to temperature/changed
239
+ New temperature detected 72955029 sent to temperature/changed
240
+ ```
241
+
242
+ To make sure your **test.py** and client code works check if the broker really receives temperature-related messages. You can do it using an [MQTT CLI](https://hivemq.github.io/mqtt-cli/) using docker. Run the command `docker run hivemq/mqtt-cli sub -t temperature/changed -h test.mosquitto.org` in your terminal. It will download the image if you don't have it locally, then the CLI will connect to the broker, subscribe to the `temperature/changed` topic and then output the temperature ids on the terminal.
243
+
244
+ ### 3. Update the template with client code
245
+
246
+ Open [**index.js**](#indexjs-file) and copy the content of [**client.py**](#1-create-the-client) and replace `{asyncapi.info().title()}` with it. It should look like the code snippet below now:
247
+
248
+ ``` js
249
+ import { File } from '@asyncapi/generator-react-sdk';
250
+
251
+ export default function ({ asyncapi }) {
252
+ return (
253
+ <File name="client.py">
254
+ {`import paho.mqtt.client as mqtt
255
+
256
+ mqttBroker = "test.mosquitto.org"
257
+
258
+ class TemperatureServiceClient:
259
+ def __init__(self):
260
+ self.client = mqtt.Client()
261
+ self.client.connect(mqttBroker)
262
+
263
+
264
+ def sendTemperatureChange(self, id):
265
+ topic = "temperature/changed"
266
+ self.client.publish(topic, id)`}
267
+ </File>
268
+ )
269
+ }
270
+ ```
271
+
272
+ ### 4. Write script to run the test code
273
+
274
+ In **package.json** you can have the scripts property that you invoke by calling `npm run <your_script>`. Add these scripts to **package.json**:
275
+
276
+ ``` json
277
+ "scripts": {
278
+ "test:clean": "rimraf test/project/client.py",
279
+ "test:generate": "asyncapi generate fromTemplate test/fixtures/asyncapi.yml ./ --output test/project --force-write",
280
+ "test:start": "python test/project/test.py",
281
+ "test": "npm run test:clean && npm run test:generate && npm run test:start"
282
+ }
283
+ ```
284
+
285
+ The 4 scripts above do the following:
286
+
287
+ 1. `test:clean`: This script uses the `rimraf` package to remove the old version of the file **test/project/client.py** every time you run your test.
288
+ 2. `test:generate`: This script uses the AsyncAPI CLI to generate a new version of **client.py**.
289
+ 3. `test:start`: This script runs the python code using **client.py**.
290
+ 4. `test`: This script runs all the other scripts in proper order.
291
+
292
+ Run `npm test` on your terminal to ensure everything works as expected.
293
+
294
+ ### 5. Template your code
295
+
296
+ #### 5a. Add parameters to the configuration file
297
+
298
+ You often have different runtime environments in programming, e.g., development and production. You will use different servers to spin both of these instances. You'll have two broker versions, one for production and the other for development. You have defined a dev server in the AsyncAPI document:
299
+
300
+ ```yml
301
+ servers:
302
+ dev:
303
+ url: test.mosquitto.org
304
+ protocol: mqtt
305
+ ```
306
+
307
+ This will allow you to also define the broker you will use in production in the servers section above.
308
+ Therefore, we can template the code `mqttBroker = 'test.mosquitto.org'` in **index.js** so the value is populated dynamically at runtime depending on the specified server environment.
309
+
310
+ The generator has a **parameters** object used to define parameters you use to dynamically modify your template code at runtime. It also supports the **server** parameter that defines the server configuration value. Navigate to **package.json** and add the snippet below:
311
+
312
+ ```json
313
+ "generator": {
314
+ # ...(redacted for brevity)
315
+ "parameters": {
316
+ "server": {
317
+ "description": "The server you want to use in the code.",
318
+ "required": true
319
+ }
320
+ }
321
+ }
322
+ ```
323
+
324
+ `"required": true`: makes the parameter mandatory and once user forgets to add it to the cli command, a proper error message is yielded.
325
+ You'll pass the server to be used to generate your code using `--param server=dev` in the AsyncAPI CLI command. Failure to which you'll get an error:
326
+
327
+ ```cmd
328
+ Generator Error: This template requires the following missing params: server.
329
+ ```
330
+
331
+ Update your `test:generate` script in **package.json** to include the server param
332
+
333
+ ```json
334
+ test:generate": "asyncapi generate fromTemplate test/fixtures/asyncapi.yml ./ --output test/project --force-write --param server=dev"
335
+ ```
336
+
337
+ You can now replace the static broker from `mqttBroker = 'test.mosquitto.org'` to `mqttBroker = "${asyncapi.servers().get(params.server).url()}"` in **index.js**.
338
+
339
+ Now the template code looks like this:
340
+
341
+ ``` js
342
+ import { File } from '@asyncapi/generator-react-sdk';
343
+
344
+ // notice that now the template not only gets the instance of parsed AsyncAPI document but also the parameters
345
+ export default function ({ asyncapi, params }) {
346
+
347
+ return (
348
+ <File name="client.py">
349
+ {`import paho.mqtt.client as mqtt
350
+
351
+ mqttBroker = "${asyncapi.servers().get(params.server).url()}"
352
+
353
+ class TemperatureServiceClient:
354
+ def __init__(self):
355
+ self.client = mqtt.Client()
356
+ self.client.connect(mqttBroker)
357
+
358
+
359
+ def sendTemperatureChange(self, id):
360
+ topic = "temperature/changed"
361
+ self.client.publish(topic, id)`}
362
+ </File>
363
+ )
364
+ }
365
+ ```
366
+
367
+ Run `npm test` to validate that your code still works as expected.
368
+
369
+ #### 5b. Templating index.js with React
370
+
371
+ Python takes indentation very seriously, and our generated output will be Python code. We, therefore, need to make sure the indentation in **index.js** looks right so the generated code is indented correctly. After templating the code in **index.js**, it will look like the following code snippet:
372
+
373
+ ```js
374
+ // 1
375
+ import { File, Text } from '@asyncapi/generator-react-sdk'
376
+ export default function ({ asyncapi, params }) {
377
+ return (
378
+ <File name="client.py">
379
+ // 2
380
+ <Text newLines={2}>import paho.mqtt.client as mqtt</Text>
381
+ // 3
382
+ <Text newLines={2}>mqttBroker = "{asyncapi.servers().get(params.server).url()}"</Text>
383
+ // 4
384
+ <Text newLines={2}>class {asyncapi.info().title().replaceAll(' ', '')}Client:</Text>
385
+ // 5
386
+ <Text indent={2} newLines={2}>
387
+ {`def __init__(self):
388
+ self.client = mqtt.Client()
389
+ self.client.connect(mqttBroker)`}
390
+ </Text>
391
+ </File>
392
+ )
393
+ }
394
+ ```
395
+
396
+ 1. Import the **Text** component that will wrap strings so they are indented properly in the output. Your import statement should now look like this: `import { File, Text } from '@asyncapi/generator-react-sdk'`.
397
+ 2. When the Paho module import is rendered in **client.py** file, it will add two extra new lines.
398
+ 3. The broker url is templated in a `Text` component removing the `$` from the string template.
399
+ 4. Dynamically get the class name **TemperatureServiceClient** from the AsyncAPI document from the **info** object using the Parser API using the code: `asyncapi.info().title()` . It will return `Temperature Service`, then remove the spaces and add `Client` as a suffix.
400
+ 5. There is no templating needed in the `__init__` function, there is only hardcoded information.
401
+
402
+ > If you're on the fence about which templating engine you should use in your template, check out the [React render engine](https://www.asyncapi.com/docs/tools/generator/react-render-engine) and [nunjucks render engine](https://www.asyncapi.com/docs/tools/generator/nunjucks-render-engine) documentation.
403
+ In the next section, you'll refactor your template to use React.
404
+
405
+ #### 5c. Creating a reusable component
406
+
407
+ Suppose you have two [channels](https://www.asyncapi.com/docs/concepts/channel), one to watch if the temperature drop below 22 °C and one to check if the temperature is above 22 °C, the generated output code would look like this:
408
+
409
+ ```python
410
+ import paho.mqtt.client as mqtt
411
+
412
+ mqttBroker = "test.mosquitto.org"
413
+
414
+ class TemperatureServiceClient:
415
+
416
+ def __init__(self):
417
+ self.client = mqtt.Client()
418
+ self.client.connect(mqttBroker)
419
+
420
+ def sendTemperatureDrop(self, id):
421
+ topic = "temperature/dropped"
422
+ self.client.publish(topic, id)
423
+ def sendTemperatureRise(self, id):
424
+ topic = "temperature/risen"
425
+ self.client.publish(topic, id)
426
+
427
+ ```
428
+
429
+ 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:
430
+
431
+ ```js
432
+ <Text newLines={2}>
433
+ <TopicFunction channels={asyncapi.channels().filterByReceive()} />
434
+ </Text>
435
+ ```
436
+
437
+ It's recommended to put reusable components outside 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 contain a `publish` operation. Add the following code in **components/TopicFunction.js** file:
438
+
439
+ ```js
440
+ /*
441
+ * This component returns a block of functions that user can use to send messages to specific topic.
442
+ * As input it requires a list of Channel models from the parsed AsyncAPI document
443
+ */
444
+ export function TopicFunction({ channels }) {
445
+ const topicsDetails = getTopics(channels)
446
+ let functions = ''
447
+
448
+ topicsDetails.forEach((t) => {
449
+ functions += `def send${t.name}(self, id):
450
+ topic = "${t.topic}"
451
+ self.client.publish(topic, id)\n`
452
+ })
453
+
454
+ return functions
455
+ }
456
+
457
+ /*
458
+ * This function returns a list of objects, one for each channel with two properties, name and topic
459
+ * name - holds information about the operationId provided in the AsyncAPI document
460
+ * topic - holds information about the address of the topic
461
+ *
462
+ * As input it requires a list of Channel models from the parsed AsyncAPI document
463
+ */
464
+ function getTopics(channels) {
465
+ const channelsCanSendTo = channels
466
+ let topicsDetails = []
467
+
468
+ channelsCanSendTo.forEach((ch) => {
469
+ const topic = {}
470
+ const operationId = ch.operations().filterByReceive()[0].id()
471
+ topic.name = operationId.charAt(0).toUpperCase() + operationId.slice(1)
472
+ topic.topic = ch.address()
473
+
474
+ topicsDetails.push(topic)
475
+ })
476
+
477
+ return topicsDetails
478
+ }
479
+ ```
480
+
481
+ `{ channels }`: the `TopicFunction` component accepts a custom prop called channels and in your template code
482
+ `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.
483
+
484
+ 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:
485
+
486
+ ```js
487
+ import { File, Text } from '@asyncapi/generator-react-sdk'
488
+ import { TopicFunction } from '../components/TopicFunction'
489
+
490
+ export default function ({ asyncapi, params }) {
491
+ return (
492
+ <File name="client.py">
493
+ <Text newLines={2}>import paho.mqtt.client as mqtt</Text>
494
+
495
+ <Text newLines={2}>mqttBroker = "{asyncapi.servers().get(params.server).url()}"</Text>
496
+
497
+ <Text newLines={2}>class {asyncapi.info().title().replaceAll(' ', '')}Client:</Text>
498
+
499
+ <Text indent={2} newLines={2}>
500
+ {`def __init__(self):
501
+ self.client = mqtt.Client()
502
+ self.client.connect(mqttBroker)`}
503
+ </Text>
504
+
505
+ <Text indent={2}>
506
+ <TopicFunction channels={asyncapi.channels().filterByReceive()} />
507
+ </Text>
508
+ </File>
509
+ )
510
+ }
511
+
512
+ ```
513
+
514
+ Run `npm test` on your terminal to ensure everything works as expected.
515
+
516
+ 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.
517
+
518
+ #### 5d. Update AsyncAPI document
519
+
520
+ Update the AsyncAPI document to use two channels:
521
+
522
+ ```yml
523
+ channels:
524
+ temperature/dropped:
525
+ description: Notifies the user when the temperature drops past a certain point.
526
+ publish:
527
+ operationId: temperatureDrop
528
+ message:
529
+ description: Message that is being sent when the temperature drops past a certain point.
530
+ payload:
531
+ type: object
532
+ additionalProperties: false
533
+ properties:
534
+ temperatureId:
535
+ type: string
536
+
537
+ temperature/risen:
538
+ description: Notifies the user when the temperature rises past a certain point.
539
+ publish:
540
+ operationId: temperatureRise
541
+ message:
542
+ description: Message that is being sent when the temperature rises past a certain point.
543
+ payload:
544
+ type: object
545
+ additionalProperties: false
546
+ properties:
547
+ temperatureId:
548
+ type: string
549
+ ```
550
+
551
+ And update your test script in test.py to test the two functions as below:
552
+
553
+ ```py
554
+ client.sendTemperatureDrop(randomId)
555
+ print("Temperature drop detected " + str(randomId) + " sent to temperature/dropped")
556
+ client.sendTemperatureRise(randomId)
557
+ print("Temperature rise detected " + str(randomId) + " sent to temperature/risen")
558
+ ```
559
+
560
+ Run `npm test` to validate that everything works as expected. You should see logs similar to the snippet below in your terminal:
561
+
562
+ ```cmd
563
+ Temperature drop detected 49040460 sent to temperature/dropped
564
+ Temperature rise detected 49040460 sent to temperature/risen
565
+ Temperature drop detected 66943992 sent to temperature/dropped
566
+ Temperature rise detected 66943992 sent to temperature/risen
567
+ ```
568
+
569
+ ## Where to go from here?
570
+
571
+ 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.😃
572
+
573
+ 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).
574
+
575
+ 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.
@@ -11,20 +11,24 @@ Let's break down the minimum template requirements: the `template` directory and
11
11
 
12
12
  ### `template` directory
13
13
 
14
- The `template` directory stores generated outputs in files. In other words, the generator processes all the files stored in this directory.
14
+ The `template` directory holds all the files that will be used for generating the output. The generator will process all the files stored in this directory.
15
15
 
16
+ The following code is an example of an `index.js` file inside the `template` folder.
16
17
  ```js
17
18
  import { File, Text } from "@asyncapi/generator-react-sdk";
18
19
 
19
- export default function({ asyncapi, params, originalAsyncAPI }) {
20
- return (
20
+ export default function ({ asyncapi, params, originalAsyncAPI }) {
21
+ return (
21
22
  <File name="asyncapi.md">
22
- <Text>My application's markdown file.</Text>
23
- <Text>App name: **{ asyncapi.info().title() }**</Text>
23
+ <Text>My application's markdown file.</Text>
24
+ <Text>App name: **{asyncapi.info().title()}**</Text>
24
25
  </File>
25
- );
26
+ );
26
27
  }
27
28
  ```
29
+
30
+ The above example will produce an `asyncapi.md` file where usage of the AsyncAPI document information (i.e. the `title`) is demonstrated.
31
+
28
32
  ### `package.json` file
29
33
 
30
34
  Before the generation process begins, the generator installs the template into its dependencies. A `package.json` file is necessary to identify the template name.
@@ -43,8 +47,6 @@ The following block shows an example `package.json` file that points to the [Rea
43
47
  }
44
48
  ```
45
49
 
46
- The above example of a `template/index.js` file shows the generation process result. The user also receives an `asyncapi.md` file with hardcoded and dynamic (application title from the AsyncAPI document) information.
47
-
48
50
  Every template must depend on the [`@asyncapi/generator-react-sdk` package](https://github.com/asyncapi/generator-react-sdk), which contains a template file's basic components.
49
51
 
50
52
  ## Additional configuration options
@@ -73,14 +75,16 @@ The following examples show some advanced configurations that we can use in our
73
75
  "name": "myTemplate",
74
76
  "generator": {
75
77
  "renderer": "react",
76
- "supportedProtocols": "mqtt"
78
+ "supportedProtocols": [
79
+ "mqtt"
80
+ ]
77
81
  },
78
82
  "dependencies": {
79
83
  "@asyncapi/generator-react-sdk": "^0.2.25"
80
84
  }
81
85
  }
82
86
  ```
83
- The above `package.json` file has a newly added configuration called `supportedProtocols` which is set to `mqtt`. This configuration displays all the protocols that this template supports. You can have multiple supported protocols in our template.
87
+ The above `package.json` file has a newly added configuration called `supportedProtocols` which is set to a list containing only `mqtt`. This configuration displays all the protocols that this template supports. You can have multiple supported protocols in our template.
84
88
 
85
89
  For example, if you want to generate an output using the above template, you need to have an AsyncAPI document with servers that use `mqtt` to generate your desired output. If your AsyncAPI document has server connections with `kafka`, the generation process will be terminated since the only supported protocol mentioned is `mqtt`.
86
90
 
@@ -93,7 +97,9 @@ Additionally, we can also have a configuration called `parameters`, which is an
93
97
  "name": "myTemplate",
94
98
  "generator": {
95
99
  "renderer": "react",
96
- "supportedProtocols": "mqtt",
100
+ "supportedProtocols": [
101
+ "mqtt"
102
+ ],
97
103
  "parameters": {
98
104
  "version": {
99
105
  "description": "Overrides application version under `info.version` in the AsyncAPI document.",
package/docs/usage.md CHANGED
@@ -111,14 +111,15 @@ Install [Docker](https://docs.docker.com/get-docker/) first, then use docker to
111
111
  docker run --rm -it \
112
112
  -v [ASYNCAPI SPEC FILE LOCATION]:/app/asyncapi.yml \
113
113
  -v [GENERATED FILES LOCATION]:/app/output \
114
- asyncapi/generator [COMMAND HERE]
114
+ asyncapi/cli [COMMAND HERE]
115
115
 
116
- # Example that you can run inside the generator directory after cloning this repository. First, you specify the mount in the location of your AsyncAPI specification file and then you mount it in the directory where the generation result should be saved.
116
+ # Example that you can run inside the cli directory after cloning this repository. First, you specify the mount in the location of your AsyncAPI specification file and then you mount it in the directory where the generation result should be saved.
117
117
  docker run --rm -it \
118
- -v ${PWD}/test/docs/dummy.yml:/app/asyncapi.yml \
119
- -v ${PWD}/output:/app/output \
120
- asyncapi/generator -o /app/output /app/asyncapi.yml @asyncapi/html-template --force-write
118
+ -v ${PWD}/test/fixtures/asyncapi_v1.yml:/app/asyncapi.yml \
119
+ -v ${PWD}/output:/app/output \
120
+ asyncapi/cli generate fromTemplate -o /app/output /app/asyncapi.yml @asyncapi/html-template --force-write
121
121
  ```
122
+ Note: Use ``` ` ``` instead of `\` for Windows.
122
123
 
123
124
  ### CLI usage with `npx` instead of `npm`
124
125
 
@@ -11,7 +11,7 @@ Something went wrong:
11
11
  Error: This template is not compatible with the current version of the generator (${generatorVersion}). This template is compatible with the following version range: ${generator}.`)
12
12
  ```
13
13
 
14
- > Use the following command to check the version of the AsyncAPI CLI you have installed; `asyncapi --version`
14
+ > Use the following command to check the version of the AsyncAPI CLI you have installed with all its dependencies, like AsyncAPI Generator; `asyncapi config versions`
15
15
 
16
16
  It is better to lock a specific version of the template and the generator if you plan to use the AsyncAPI CLI and a particular template in production. The differences between using the version of the AsyncAPI CLI you have installed and locking a certain version on production are demonstrated in the following code snippets.
17
17
 
package/lib/generator.js CHANGED
@@ -77,7 +77,7 @@ class Generator {
77
77
  * @param {String} templateName Name of the template to generate.
78
78
  * @param {String} targetDir Path to the directory where the files will be generated.
79
79
  * @param {Object} options
80
- * @param {String} [options.templateParams] Optional parameters to pass to the template. Each template define their own params.
80
+ * @param {Object<string, string>} [options.templateParams] Optional parameters to pass to the template. Each template define their own params.
81
81
  * @param {String} [options.entrypoint] Name of the file to use as the entry point for the rendering process. Use in case you want to use only a specific template file. Note: this potentially avoids rendering every file in the template.
82
82
  * @param {String[]} [options.noOverwriteGlobs] List of globs to skip when regenerating the template.
83
83
  * @param {Object<String, Boolean | String | String[]>} [options.disabledHooks] Object with hooks to disable. The key is a hook type. If key has "true" value, then the generator skips all hooks from the given type. If the value associated with a key is a string with the name of a single hook, then the generator skips only this single hook name. If the value associated with a key is an array of strings, then the generator skips only hooks from the array.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asyncapi/generator",
3
- "version": "1.10.9",
3
+ "version": "1.10.11",
4
4
  "description": "The AsyncAPI generator. It can generate documentation, code, anything!",
5
5
  "main": "./lib/generator.js",
6
6
  "bin": {
@@ -51,7 +51,7 @@
51
51
  "@asyncapi/avro-schema-parser": "^3.0.2",
52
52
  "@asyncapi/generator-react-sdk": "^0.2.23",
53
53
  "@asyncapi/openapi-schema-parser": "^3.0.3",
54
- "@asyncapi/parser": "^2.0.3",
54
+ "@asyncapi/parser": "^2.1.0",
55
55
  "@asyncapi/raml-dt-schema-parser": "^4.0.3",
56
56
  "@npmcli/arborist": "^2.2.4",
57
57
  "ajv": "^8.12.0",