-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtutorial.tsx
More file actions
689 lines (672 loc) · 24 KB
/
tutorial.tsx
File metadata and controls
689 lines (672 loc) · 24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
import { workbenchXPath, TutorialStep } from '@motiadev/workbench'
export const steps: TutorialStep[] = [
{
title: 'Welcome to Motia',
image: {
height: 200,
src: 'https://github.com/MotiaDev/motia/raw/main/packages/docs/public/github-readme-banner.png',
},
description: () => (
<p>
Motia is an all-in-one framework for modern backend systems. Out of the box support for API endpoints,
background jobs, scheduled tasks and agentic workflow orchestration through a unified runtime. Thanks to its
event driven architecture you can run tasks in parallel, stream data to clients, or allow for seamless
orchestration of flows.
<br />
<br />
Let's start with <b>Workbench</b>, it is a development tool provided by Motia's ecosystem, from here you'll be
able to visualize your flows and observe their behavior.
<br />
<br />
💡 If you are already familiar with Motia, you can skip this tutorial.
</p>
),
},
// Flows
{
elementXpath: workbenchXPath.flows.node('apitrigger'),
title: 'API Step',
link: 'https://www.motia.dev/docs/concepts/steps/api',
description: () => (
<p>
Let's evaluate the Step that will allow you to receive traffic from external applications, API Steps will allow
you to expose an HTTP endpoint for external traffic.
</p>
),
before: [
{ type: 'click', selector: workbenchXPath.links.flows },
{ type: 'click', selector: workbenchXPath.flows.dropdownFlow('basic-tutorial') },
],
},
{
elementXpath: workbenchXPath.flows.previewButton('apitrigger'),
title: 'Code Preview',
description: () => <p>Clicking on this icon will allow you to visualize the source code for a given Step.</p>,
before: [
{
type: 'click',
selector: workbenchXPath.closePanelButton,
optional: true,
},
],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Step Config',
description: () => (
<div>
<p>
All Steps are defined by two main components, the <b>configuration</b> and the <b>handler</b>.
<br />
<br />
Let's start with the configuration, the common config attributes are
<i> type, name, description, and flows</i>.
<br />
<br />
</p>
<ul>
<li>
The <b>type</b> attribute is important since it declares the type of Step
</li>
<li>
The <b>flows</b> attribute will associate your Step with a given flow or set of flows.
</li>
<li>
The <b>name</b> and <b>description</b> attributes will provide context in the visualization and
observability tools.
</li>
</ul>
</div>
),
before: [
{ type: 'click', selector: workbenchXPath.flows.previewButton('apitrigger') },
{ type: 'click', selector: workbenchXPath.flows.feature('step-configuration') },
],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'API Step Configuration',
description: () => (
<p>
There are specific configuration attributes for an API Step. Let's start with the <b>method</b> attribute. This
will declare the type of HTTP method used to talk to your API Step.
<br />
<br />
Through the <b>path</b> attribute you'll declare the url path used to trigger your API Step
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('api-configuration') }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Request Body',
link: 'https://zod.dev',
description: () => (
<p>
The <b>bodySchema</b> attribute will define the shape of the request body.
<br />
<br />
<i>💡 Both the request body and response payload are defined by zod schemas</i>
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('request-body') }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Response Payload',
link: 'https://zod.dev',
description: () => (
<p>
Through the <b>responseSchema</b> attribute you can declare the different type of http responses based on the
http status code.
<br />
<br />
<i>💡 Both the request body and response payload are defined by zod schemas</i>
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('response-payload') }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Event Driven Architecture',
description: () => (
<p>
Motia allows you to interact between Steps or flows through an event driven architecture.
<br />
<br />
In order to connect your Steps during runtime you will use the <b>emits</b> and <b>subscribes</b> attributes.
<br />
<br />
Through the <b>emits</b>, you can specify a list of topics that your Step emits for others to <i>subscribe</i>.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('event-driven-architecture') }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Step Handler',
description: () => (
<p>
Now that we've covered how to declare a Step, let's dive into the <b>Step Handler</b>.<br />
<br />
Handlers are essential for the execution of your Step. For API Steps, the handler will receive the request
object as the first argument, followed by a second argument that provides access to the <b>logger</b>,{' '}
<b>event emitter</b>, <b>state manager</b>, and <b>trace id</b>.<br />
<br />
<br />
💡 We will cover these in depth further down the tutorial.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('handler') }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Logger',
description: () => (
<p>
We recommend using the provided <b>logger</b> util in order to guarantee observability through Motia's
ecosystem.
<br />
<br />
You can use logger similar to <i>console.log</i> for js or <i>print</i> for python, but with enhanced utilities,
such as being able to provide additional context.
<br />
<br />
Motia will take care of the rest to provide the best experience to visualize your logs and tie them through
tracing.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('logger') }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'HTTP Response',
description: () => (
<p>
Now let's wrap our API Step and return a response.
<br />
<br />
<br />
You simply need to return an object that complies with one of the <b>responseSchema</b> definitions
declared in your Step configuration.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('http-response') }],
},
// Event Steps
{
elementXpath: workbenchXPath.flows.node('processfoodorder'),
title: 'Event Step',
link: 'https://www.motia.dev/docs/concepts/steps/event',
description: () => (
<p>
Now that we have an entry point in our flow, let's focus on subscribing to a <b>topic</b> and performing a
specific task.
<br />
<br />
For this we will look at the <b>Event</b> Step.
<br />
<br />
<b> Event</b> Steps are essential for Motia's event driven architecture. Let's dive deeper into the
anatomy of an Event Step by taking a look at the code visualization tool.
<br />
<br />
💡 <b>Event</b> Steps can only be triggered internally, through topic subscriptions.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.closePanelButton }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Event Step',
link: 'https://www.motia.dev/docs/concepts/steps/event',
description: () => (
<p>
Now that we have an entry point in our flow, let's focus on subscribing to a <b>topic</b> and performing a
specific task.
<br /> <br />
For this we will look at the <b>Event</b> Step.
<br /> <br />
<b> Event</b> Steps are essential for Motia's event driven architecture. Let's dive deeper into the
anatomy of an Event Step by taking a look at the code visualization tool.
<br /> <br />
💡 <b>Event</b> Steps can only be triggered internally, through topic subscriptions.
</p>
),
before: [
{ type: 'click', selector: workbenchXPath.flows.previewButton('processfoodorder') },
{ type: 'click', selector: workbenchXPath.flows.feature('step-configuration') },
],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Event Step Input',
description: () => (
<p>
<b>Event</b> Steps, like other Steps types, are composed of a configuration and a handler.
<br />
<br />
<b>Event</b> Steps have a specific attribute from their config, the <b>input</b> attribute, which declares the
data structure provided by the topic it is subscribed to.
<br />
<br />
The <b>input</b> attributes is defined as a zod schema, think of the <b>input</b> attributes as a contract for
other Steps that emit the topics that your Step subscribes to.
<br />
<br /> 💡 <b>Multiple Steps can subscribe to the same topic, but their input schema must be the same.</b>
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('input-schema') }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Event Step Handler',
description: () => (
<p>
Let's take a look at the <b>Event</b> Step Handler.
<br />
<br />
The handler will seem familiar to other Step handlers, but notice that the first argument holds the data
provided for the topic or topics your Step subscribes to.
<br />
<br />
💡 The first argument will match the structure of your input schema, defined in the <b>Event</b> Step config.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('handler') }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Storing Data in State',
link: 'https://www.motia.dev/docs/concepts/state-management',
description: () => (
<p>
Let's take a closer look at storing data in state.
<br />
<br />
In this example we are persisting the result of a third party HTTP request in <b>State</b>, scoping it to a
group id named "orders".
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('state') }],
},
// Cron Steps
{
elementXpath: workbenchXPath.flows.node('stateauditjob'),
title: 'Cron Step',
link: 'https://www.motia.dev/docs/concepts/steps/cron',
description: () => (
<p>
Let's do a recap of what you've learned, thus far you've become familiar with two Step types <b>API</b>{' '}
and <b>Event</b> Steps.
<br />
<br />
You've also started to learn how to navigate around Workbench. Let's wrap up Motia's Step types with the last
one the <b>CRON</b> Step. Let's take a deeper look at its definition.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.closePanelButton }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Cron Schedule',
link: 'https://www.motia.dev/docs/concepts/steps/cron',
description: () => (
<p>
<b>CRON</b> Steps are similar to the other Step types, they are composed by a configuration and a handler.
<br />
<br />
The <b>CRON</b> Step config has a distinct attribute, the <b>cron</b> attribute, through this attribute you will
define the cron schedule for your Step.
<br />
<br />
For instance, in this example the cron schedule is configured to execute the Step handler every 5 minutes. Let's
take a look at the handler definition.
</p>
),
before: [
{ type: 'click', selector: workbenchXPath.flows.previewButton('stateauditjob') },
{ type: 'click', selector: workbenchXPath.flows.feature('cron-configuration') },
],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Cron Step Handler',
description: () => (
<p>
The <b>CRON</b> Step handler only receives one argument, which is the Motia context, if you recall the Motia
context gives you access to utilities to emit <i>topics</i>, <i>log</i>, <i>manage state</i>, and it provides
the <i>trace id</i> associated to your Step's execution.
<br />
<br />
In this CRON Step example we are evaluating orders persisted in state, and emitting warnings through a topic for
each order that hasn't been processed and has a shipping date in the past.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.flows.feature('handler') }],
},
// // Endpoints
{
elementXpath: workbenchXPath.links.endpoints,
title: 'Endpoints',
description: () => (
<p>
Now that we've looked at Motia primitives, let's trigger the API Step from the <b>endpoints</b> section in
Workbench.
<br />
<br />
💡 All of your API Steps declare HTTP endpoints that can be reviewed and tested from the <b>Endpoints</b>{' '}
section in Workbench.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.closePanelButton }],
},
{
elementXpath: workbenchXPath.endpoints.endpointsList,
title: 'Endpoints Tool',
description: () => (
<p>
This section will display all of the endpoints declared in your API Steps. It will list the HTTP method, the URL
path, and the description declared in the Step configuration.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.links.endpoints }],
},
{
elementXpath: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial'),
title: 'Endpoints Tool',
description: () => (
<p>
Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to use
the endpoint and a tool to test the endpoint.
</p>
),
},
{
elementXpath: workbenchXPath.endpoints.callPanel,
title: 'API Endpoint Playground',
description: () => (
<p>
Once you click on an endpoint from the list, you will be able to test it by providing a request payload and
clicking on the <b>Play</b> button.
<br />
<br />
This section will provide an overview of your API endpoint.
<br />
<br />
It will display documentation on how to use the endpoint in the <b>Details</b> Tab, and a form to test the
endpoint in the <b>Call</b> Tab.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial') }],
},
{
elementXpath: workbenchXPath.endpoints.specButton,
title: 'API Endpoint Specification',
description: () => (
<p>
Clicking on this button will open the specification of your API endpoint. Like response and request schemas.
</p>
),
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'API Endpoint Specification',
description: () => (
<p>
This is what you see when clicking on the <b>Details</b> button.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.endpoints.specButton }],
},
{
elementXpath: workbenchXPath.endpoints.callPanel,
title: 'API Endpoint Test',
description: () => (
<p>
This form will allow you to validate your API Step by executing an HTTP request against your API endpoint.
<br />
<br />
You can also test your API endpoints using your terminal through the curl command.
<br />
<br />
💡 Thanks to the <b>bodySchema</b> attribute from the API Step config, you are automatically provided with a
sample request payload.
<br />
<br />
<pre className="code-preview">
<code className="language-bash">
curl -X POST http://localhost:3000/basic-tutorial \<br />
{' '}-H "Content-Type: application/json" \<br />
{' '}-d '
{JSON.stringify({
pet: { name: 'Jack', photoUrl: 'https://images.dog.ceo/breeds/pug/n02110958_13560.jpg' },
foodOrder: { id: 'food-order-1', quantity: 0 },
})}
'
</code>
</pre>
</p>
),
before: [
{ type: 'click', selector: workbenchXPath.closePanelButton },
{ type: 'click', selector: workbenchXPath.endpoints.bodyTab },
],
},
{
elementXpath: workbenchXPath.endpoints.playButton,
title: 'API Endpoint Test',
description: () => (
<p>
Once you've filled the request payload, you can click on the <b>Play</b> button to trigger an HTTP request
against your API endpoint.
</p>
),
before: [
{
type: 'fill-editor',
content: {
pet: { name: 'Jack', photoUrl: 'https://images.dog.ceo/breeds/pug/n02110958_13560.jpg' },
foodOrder: { id: 'food-order-1', quantity: 0 },
},
},
],
},
{
elementXpath: workbenchXPath.endpoints.response,
title: 'Test Result',
description: () => <p>Once your request has been resolved, you will see the response from here.</p>,
before: [{ type: 'click', selector: workbenchXPath.endpoints.playButton }],
},
// Tracing
{
elementXpath: workbenchXPath.bottomPanel,
title: 'Tracing',
description: () => (
<p>
Great! You have triggered your first flow, now let's take a look at our example flow behavior using Workbench's
observability tools.
<br />
<br />
Let's start with <b>tracing</b>, in this section you will be able to see all of your flow executions grouped by{' '}
<b>trace id</b>.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.links.tracing }],
},
{
elementXpath: workbenchXPath.tracing.trace(1),
title: 'Tracing Tool',
description: () => (
<p>
Trace IDs are auto generated and injected throughout the execution of all Steps in your flow.
<br />
<br />
Clicking on a trace item from this list will allow you to dive deeper into your flow behavior.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.tracing.trace(1) }],
},
{
elementXpath: workbenchXPath.tracing.details,
title: 'Trace Timeline',
description: () => (
<p>
This section will show all Step executions associated to the selected trace, you will see a list of executed
Steps and their sequencing over a <b>timeline</b>.
</p>
),
},
{
elementXpath: workbenchXPath.tracing.timeline(1),
title: 'Trace Timeline Segment',
description: () => (
<p>
Each <b>timeline segment</b> will show you the time it took to execute each Step, you can click on any segment
and dive even deeper into that specific Step execution logs.
</p>
),
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Trace Details',
description: () => (
<p>
This is the <b>Trace Details View</b>, this will allow you to look deeper into the logs raised during the
execution of a Step.
<br />
<br />
💡 This is a simplified version of the logs, if you wish to look further into a log you will need to use the{' '}
<b>Logs Tool</b>.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.tracing.timeline(1) }],
},
// Logs
{
elementXpath: workbenchXPath.logs.container,
title: 'Logs',
description: () => (
<p>
Let's take a look at your execution logs, click on this tab will take you to the <b>Logs Tool</b>.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.links.logs }],
},
{
elementXpath: workbenchXPath.logs.traceColumn(1),
title: 'Filtering by Trace ID',
description: () => (
<p>
Your log results will show their associated <b>Trace ID</b> in the third column, the <b>Trace ID</b> values are
linked to update your search.
<br />
<br />
💡 Clicking a <b>Trace ID</b> will narrow down your search to only show logs from that trace.
</p>
),
},
{
elementXpath: workbenchXPath.logs.searchContainer,
title: 'Search Criteria',
description: () => (
<p>
By clicking the <b>Trace ID</b>, your search is updated to match results associated with that <b>Trace ID</b>.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.logs.traceColumn(1) }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'Logs',
description: () => (
<p>
When you click on a log row, it will open the <b>Log Details View</b>.
<br />
<br />
In here you will be able to look at your log details (<b>Log Level</b>, <b>Timestamp</b>, <b>Step Name</b>,{' '}
<b>Flow Name</b>, and <b>Trace ID</b>), along with any additional context you've provided in your log call.
</p>
),
},
// States
{
elementXpath: workbenchXPath.links.states,
title: 'State Management',
description: () => (
<p>
Ok now that we've seen the observability tools, let's take a look at the <b>State Management Tool</b>.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.links.states }],
},
{
elementXpath: workbenchXPath.states.container,
title: 'State Management Tool',
description: () => (
<p>
This is your <b>State Management Tool</b>, from here you will be able to see all of your persisted state
key/value pairs.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.states.row(1) }],
},
{
elementXpath: workbenchXPath.sidebarContainer,
title: 'State Details',
description: () => (
<p>
This section presents the details for a given state key, from here you will be able to manage the value assigned
to the selected state key.
</p>
),
before: [{ type: 'click', selector: workbenchXPath.links.states }],
},
// End of Tutorial
{
title: 'Congratulations 🎉',
link: 'https://www.motia.dev/docs',
description: () => (
<p>
You've completed our Motia basics tutorial!
<br />
<br />
You've learned about Motia's Step types, how to navigate around Workbench, and how to use core features from the
Motia Framework (State Management, Logging, and Tracing).
<br />
<br />
We recommend you give our{' '}
<a href="https://www.motia.dev/docs/concepts" target="_blank">
core concepts
</a>{' '}
a read if you wish to learn further about Motia's fundamentals.
<br />
<br />
Don't forget to join our{' '}
<a href="https://discord.com/invite/nJFfsH5d6v" target="_blank">
Discord community
</a>{' '}
or tag us in socials to show us what you've built with Motia.
<br />
<br />
We are an open source project, so feel free to raise your{' '}
<a href="https://github.com/MotiaDev/motia/issues" target="_blank">
issues
</a>{' '}
or{' '}
<a href="https://github.com/MotiaDev/motia/discussions" target="_blank">
suggestions
</a>{' '}
in our{' '}
<a href="https://github.com/MotiaDev/motia" target="_blank">
Github repo
</a>
.
<br />
<br />
Thank you for going this far in our tutorial!
</p>
),
before: [{ type: 'click', selector: workbenchXPath.closePanelButton }],
},
]