motia 0.7.2-beta.135-338519 → 0.7.2-beta.135-994416
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/dist/cjs/create/templates/nodejs/motia-workbench.json +5 -5
- package/dist/cjs/create/templates/nodejs/tutorial.tsx.txt +40 -8
- package/dist/cjs/create/templates/python/motia-workbench.json +4 -4
- package/dist/{esm/create/templates/python/steps → cjs/create/templates/python/steps/petstore}/api_step.py.txt +2 -2
- package/dist/cjs/create/templates/python/steps/{process_food_order_step.py.txt → petstore/process_food_order_step.py.txt} +1 -1
- package/dist/cjs/create/templates/python/tutorial.tsx.txt +41 -9
- package/dist/cjs/cursor-rules/dot-files/.cursor/index.mdc +16 -8
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/api-steps.mdc +112 -4
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/cron-steps.mdc +30 -3
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/event-steps.mdc +67 -6
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/middlewares.mdc +103 -8
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/realtime-streaming.mdc +151 -2
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/state-management.mdc +63 -0
- package/dist/cjs/cursor-rules/dot-files/.cursor/rules/motia/virtual-steps.mdc +83 -4
- package/dist/cjs/dev.js +0 -2
- package/dist/cjs/generate-locked-data.js +1 -1
- package/dist/cjs/generate-types.js +2 -2
- package/dist/cjs/start.js +0 -2
- package/dist/cjs/watcher.js +4 -4
- package/dist/esm/create/templates/nodejs/motia-workbench.json +5 -5
- package/dist/esm/create/templates/nodejs/tutorial.tsx.txt +40 -8
- package/dist/esm/create/templates/python/motia-workbench.json +4 -4
- package/dist/esm/create/templates/python/src/__init__.py.txt +0 -0
- package/dist/esm/create/templates/python/src/services/__init__.py.txt +0 -0
- package/dist/{cjs/create/templates/python/steps → esm/create/templates/python/steps/petstore}/api_step.py.txt +2 -2
- package/dist/esm/create/templates/python/steps/{process_food_order_step.py.txt → petstore/process_food_order_step.py.txt} +1 -1
- package/dist/esm/create/templates/python/tutorial.tsx.txt +41 -9
- package/dist/esm/cursor-rules/dot-files/.cursor/index.mdc +16 -8
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/api-steps.mdc +112 -4
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/cron-steps.mdc +30 -3
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/event-steps.mdc +67 -6
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/middlewares.mdc +103 -8
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/realtime-streaming.mdc +151 -2
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/state-management.mdc +63 -0
- package/dist/esm/cursor-rules/dot-files/.cursor/rules/motia/virtual-steps.mdc +83 -4
- package/dist/esm/dev.js +0 -2
- package/dist/esm/generate-locked-data.js +1 -1
- package/dist/esm/generate-types.js +2 -2
- package/dist/esm/start.js +0 -2
- package/dist/esm/watcher.js +4 -4
- package/package.json +4 -4
- package/dist/cjs/create/templates/nodejs/.cursor/architecture/database/database-migration.mdc +0 -49
- package/dist/cjs/create/templates/nodejs/.cursor/architecture/database/database.mdc +0 -83
- package/dist/cjs/dev/state-endpoints.d.ts +0 -2
- package/dist/cjs/dev/state-endpoints.js +0 -30
- package/dist/esm/create/templates/nodejs/.cursor/architecture/database/database-migration.mdc +0 -49
- package/dist/esm/create/templates/nodejs/.cursor/architecture/database/database.mdc +0 -83
- package/dist/esm/dev/state-endpoints.d.ts +0 -2
- package/dist/esm/dev/state-endpoints.js +0 -26
- package/dist/types/dev/state-endpoints.d.ts +0 -2
- /package/dist/cjs/create/templates/python/{steps/services → src}/__init__.py.txt +0 -0
- /package/dist/{esm/create/templates/python/steps → cjs/create/templates/python/src}/services/__init__.py.txt +0 -0
- /package/dist/cjs/create/templates/python/{steps → src}/services/pet_store.py.txt +0 -0
- /package/dist/cjs/create/templates/python/{steps → src}/services/types.py.txt +0 -0
- /package/dist/cjs/create/templates/python/steps/{api_step.py-features.json.txt → petstore/api_step.py-features.json.txt} +0 -0
- /package/dist/cjs/create/templates/python/steps/{notification_step.py.txt → petstore/notification_step.py.txt} +0 -0
- /package/dist/cjs/create/templates/python/steps/{process_food_order_step.py-features.json.txt → petstore/process_food_order_step.py-features.json.txt} +0 -0
- /package/dist/cjs/create/templates/python/steps/{state_audit_cron_step.py-features.json.txt → petstore/state_audit_cron_step.py-features.json.txt} +0 -0
- /package/dist/cjs/create/templates/python/steps/{state_audit_cron_step.py.txt → petstore/state_audit_cron_step.py.txt} +0 -0
- /package/dist/esm/create/templates/python/{steps → src}/services/pet_store.py.txt +0 -0
- /package/dist/esm/create/templates/python/{steps → src}/services/types.py.txt +0 -0
- /package/dist/esm/create/templates/python/steps/{api_step.py-features.json.txt → petstore/api_step.py-features.json.txt} +0 -0
- /package/dist/esm/create/templates/python/steps/{notification_step.py.txt → petstore/notification_step.py.txt} +0 -0
- /package/dist/esm/create/templates/python/steps/{process_food_order_step.py-features.json.txt → petstore/process_food_order_step.py-features.json.txt} +0 -0
- /package/dist/esm/create/templates/python/steps/{state_audit_cron_step.py-features.json.txt → petstore/state_audit_cron_step.py-features.json.txt} +0 -0
- /package/dist/esm/create/templates/python/steps/{state_audit_cron_step.py.txt → petstore/state_audit_cron_step.py.txt} +0 -0
|
@@ -2,27 +2,27 @@
|
|
|
2
2
|
{
|
|
3
3
|
"id": "basic-tutorial",
|
|
4
4
|
"config": {
|
|
5
|
-
"steps/state-audit-cron.step.ts": {
|
|
5
|
+
"steps/petstore/state-audit-cron.step.ts": {
|
|
6
6
|
"x": -165,
|
|
7
7
|
"y": 217,
|
|
8
8
|
"sourceHandlePosition": "right"
|
|
9
9
|
},
|
|
10
|
-
"steps/process-food-order.step.ts": {
|
|
10
|
+
"steps/petstore/process-food-order.step.ts": {
|
|
11
11
|
"x": 211,
|
|
12
12
|
"y": 17,
|
|
13
13
|
"sourceHandlePosition": "bottom",
|
|
14
14
|
"targetHandlePosition": "left"
|
|
15
15
|
},
|
|
16
|
-
"steps/api.step.ts": {
|
|
16
|
+
"steps/petstore/api.step.ts": {
|
|
17
17
|
"x": -100,
|
|
18
18
|
"y": 3,
|
|
19
19
|
"sourceHandlePosition": "right",
|
|
20
20
|
"targetHandlePosition": "left"
|
|
21
21
|
},
|
|
22
|
-
"steps/notification.step.ts": {
|
|
22
|
+
"steps/petstore/notification.step.ts": {
|
|
23
23
|
"x": 300,
|
|
24
24
|
"y": 264
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
]
|
|
28
|
+
]
|
|
@@ -371,25 +371,35 @@ export const steps: TutorialStep[] = [
|
|
|
371
371
|
before: [{ type: 'click', selector: workbenchXPath.closePanelButton }],
|
|
372
372
|
},
|
|
373
373
|
{
|
|
374
|
-
elementXpath: workbenchXPath.endpoints.
|
|
374
|
+
elementXpath: workbenchXPath.endpoints.endpointsList,
|
|
375
375
|
title: 'Endpoints Tool',
|
|
376
376
|
description: () => (
|
|
377
377
|
<p>
|
|
378
378
|
This section will display all of the endpoints declared in your API Steps. It will list the HTTP method, the URL
|
|
379
379
|
path, and the description declared in the Step configuration.
|
|
380
|
-
<br />
|
|
381
|
-
<br />
|
|
382
|
-
💡 Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to
|
|
383
|
-
use the endpoint and a tool to test the endpoint.
|
|
384
380
|
</p>
|
|
385
381
|
),
|
|
386
382
|
before: [{ type: 'click', selector: workbenchXPath.links.endpoints }],
|
|
387
383
|
},
|
|
388
384
|
{
|
|
389
|
-
elementXpath: workbenchXPath.
|
|
390
|
-
title: '
|
|
385
|
+
elementXpath: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial'),
|
|
386
|
+
title: 'Endpoints Tool',
|
|
391
387
|
description: () => (
|
|
392
388
|
<p>
|
|
389
|
+
Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to use
|
|
390
|
+
the endpoint and a tool to test the endpoint.
|
|
391
|
+
</p>
|
|
392
|
+
),
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
elementXpath: workbenchXPath.endpoints.callPanel,
|
|
396
|
+
title: 'API Endpoint Playground',
|
|
397
|
+
description: () => (
|
|
398
|
+
<p>
|
|
399
|
+
Once you click on an endpoint from the list, you will be able to test it by providing a request payload and
|
|
400
|
+
clicking on the <b>Play</b> button.
|
|
401
|
+
<br />
|
|
402
|
+
<br />
|
|
393
403
|
This section will provide an overview of your API endpoint.
|
|
394
404
|
<br />
|
|
395
405
|
<br />
|
|
@@ -399,6 +409,25 @@ export const steps: TutorialStep[] = [
|
|
|
399
409
|
),
|
|
400
410
|
before: [{ type: 'click', selector: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial') }],
|
|
401
411
|
},
|
|
412
|
+
{
|
|
413
|
+
elementXpath: workbenchXPath.endpoints.specButton,
|
|
414
|
+
title: 'API Endpoint Specification',
|
|
415
|
+
description: () => (
|
|
416
|
+
<p>
|
|
417
|
+
Clicking on this button will open the specification of your API endpoint. Like response and request schemas.
|
|
418
|
+
</p>
|
|
419
|
+
),
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
423
|
+
title: 'API Endpoint Specification',
|
|
424
|
+
description: () => (
|
|
425
|
+
<p>
|
|
426
|
+
This is what you see when clicking on the <b>Details</b> button.
|
|
427
|
+
</p>
|
|
428
|
+
),
|
|
429
|
+
before: [{ type: 'click', selector: workbenchXPath.endpoints.specButton }],
|
|
430
|
+
},
|
|
402
431
|
{
|
|
403
432
|
elementXpath: workbenchXPath.endpoints.callPanel,
|
|
404
433
|
title: 'API Endpoint Test',
|
|
@@ -428,7 +457,10 @@ export const steps: TutorialStep[] = [
|
|
|
428
457
|
</pre>
|
|
429
458
|
</p>
|
|
430
459
|
),
|
|
431
|
-
before: [
|
|
460
|
+
before: [
|
|
461
|
+
{ type: 'click', selector: workbenchXPath.closePanelButton },
|
|
462
|
+
{ type: 'click', selector: workbenchXPath.endpoints.bodyTab },
|
|
463
|
+
],
|
|
432
464
|
},
|
|
433
465
|
{
|
|
434
466
|
elementXpath: workbenchXPath.endpoints.playButton,
|
|
@@ -2,22 +2,22 @@
|
|
|
2
2
|
{
|
|
3
3
|
"id": "basic-tutorial",
|
|
4
4
|
"config": {
|
|
5
|
-
"steps/state_audit_cron_step.py": {
|
|
5
|
+
"steps/petstore/state_audit_cron_step.py": {
|
|
6
6
|
"x": -38,
|
|
7
7
|
"y": 683,
|
|
8
8
|
"sourceHandlePosition": "right"
|
|
9
9
|
},
|
|
10
|
-
"steps/process_food_order_step.py": {
|
|
10
|
+
"steps/petstore/process_food_order_step.py": {
|
|
11
11
|
"x": 384,
|
|
12
12
|
"y": 476,
|
|
13
13
|
"targetHandlePosition": "left"
|
|
14
14
|
},
|
|
15
|
-
"steps/notification_step.py": {
|
|
15
|
+
"steps/petstore/notification_step.py": {
|
|
16
16
|
"x": 601,
|
|
17
17
|
"y": 724,
|
|
18
18
|
"targetHandlePosition": "left"
|
|
19
19
|
},
|
|
20
|
-
"steps/api_step.py": {
|
|
20
|
+
"steps/petstore/api_step.py": {
|
|
21
21
|
"x": 15,
|
|
22
22
|
"y": 461,
|
|
23
23
|
"sourceHandlePosition": "right"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from pydantic import BaseModel
|
|
2
2
|
from typing import Optional
|
|
3
|
-
from .services.pet_store import pet_store_service
|
|
4
|
-
from .services.types import Pet
|
|
3
|
+
from src.services.pet_store import pet_store_service
|
|
4
|
+
from src.services.types import Pet
|
|
5
5
|
|
|
6
6
|
class PetRequest(BaseModel):
|
|
7
7
|
name: str
|
|
@@ -360,7 +360,7 @@ export const steps: TutorialStep[] = [
|
|
|
360
360
|
title: 'Endpoints',
|
|
361
361
|
description: () => (
|
|
362
362
|
<p>
|
|
363
|
-
Now that we've looked at Motia
|
|
363
|
+
Now that we've looked at Motia primitives, let's trigger the API Step from the <b>endpoints</b> section in
|
|
364
364
|
Workbench.
|
|
365
365
|
<br />
|
|
366
366
|
<br />
|
|
@@ -371,25 +371,35 @@ export const steps: TutorialStep[] = [
|
|
|
371
371
|
before: [{ type: 'click', selector: workbenchXPath.closePanelButton }],
|
|
372
372
|
},
|
|
373
373
|
{
|
|
374
|
-
elementXpath: workbenchXPath.endpoints.
|
|
374
|
+
elementXpath: workbenchXPath.endpoints.endpointsList,
|
|
375
375
|
title: 'Endpoints Tool',
|
|
376
376
|
description: () => (
|
|
377
377
|
<p>
|
|
378
378
|
This section will display all of the endpoints declared in your API Steps. It will list the HTTP method, the URL
|
|
379
379
|
path, and the description declared in the Step configuration.
|
|
380
|
-
<br />
|
|
381
|
-
<br />
|
|
382
|
-
💡 Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to
|
|
383
|
-
use the endpoint and a tool to test the endpoint.
|
|
384
380
|
</p>
|
|
385
381
|
),
|
|
386
382
|
before: [{ type: 'click', selector: workbenchXPath.links.endpoints }],
|
|
387
383
|
},
|
|
388
384
|
{
|
|
389
|
-
elementXpath: workbenchXPath.
|
|
390
|
-
title: '
|
|
385
|
+
elementXpath: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial'),
|
|
386
|
+
title: 'Endpoints Tool',
|
|
391
387
|
description: () => (
|
|
392
388
|
<p>
|
|
389
|
+
Clicking on an endpoint from the list will open the endpoint overview which provides documentation on how to use
|
|
390
|
+
the endpoint and a tool to test the endpoint.
|
|
391
|
+
</p>
|
|
392
|
+
),
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
elementXpath: workbenchXPath.endpoints.callPanel,
|
|
396
|
+
title: 'API Endpoint Playground',
|
|
397
|
+
description: () => (
|
|
398
|
+
<p>
|
|
399
|
+
Once you click on an endpoint from the list, you will be able to test it by providing a request payload and
|
|
400
|
+
clicking on the <b>Play</b> button.
|
|
401
|
+
<br />
|
|
402
|
+
<br />
|
|
393
403
|
This section will provide an overview of your API endpoint.
|
|
394
404
|
<br />
|
|
395
405
|
<br />
|
|
@@ -399,6 +409,25 @@ export const steps: TutorialStep[] = [
|
|
|
399
409
|
),
|
|
400
410
|
before: [{ type: 'click', selector: workbenchXPath.endpoints.endpoint('POST', '/basic-tutorial') }],
|
|
401
411
|
},
|
|
412
|
+
{
|
|
413
|
+
elementXpath: workbenchXPath.endpoints.specButton,
|
|
414
|
+
title: 'API Endpoint Specification',
|
|
415
|
+
description: () => (
|
|
416
|
+
<p>
|
|
417
|
+
Clicking on this button will open the specification of your API endpoint. Like response and request schemas.
|
|
418
|
+
</p>
|
|
419
|
+
),
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
elementXpath: workbenchXPath.sidebarContainer,
|
|
423
|
+
title: 'API Endpoint Specification',
|
|
424
|
+
description: () => (
|
|
425
|
+
<p>
|
|
426
|
+
This is what you see when clicking on the <b>Details</b> button.
|
|
427
|
+
</p>
|
|
428
|
+
),
|
|
429
|
+
before: [{ type: 'click', selector: workbenchXPath.endpoints.specButton }],
|
|
430
|
+
},
|
|
402
431
|
{
|
|
403
432
|
elementXpath: workbenchXPath.endpoints.callPanel,
|
|
404
433
|
title: 'API Endpoint Test',
|
|
@@ -428,7 +457,10 @@ export const steps: TutorialStep[] = [
|
|
|
428
457
|
</pre>
|
|
429
458
|
</p>
|
|
430
459
|
),
|
|
431
|
-
before: [
|
|
460
|
+
before: [
|
|
461
|
+
{ type: 'click', selector: workbenchXPath.closePanelButton },
|
|
462
|
+
{ type: 'click', selector: workbenchXPath.endpoints.bodyTab },
|
|
463
|
+
],
|
|
432
464
|
},
|
|
433
465
|
{
|
|
434
466
|
elementXpath: workbenchXPath.endpoints.playButton,
|
|
@@ -4,14 +4,6 @@ globs:
|
|
|
4
4
|
alwaysApply: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## Adding migration
|
|
8
|
-
|
|
9
|
-
Make sure to use [migration guide](./architecture/database/database-migration.mdc) to create a new migration.
|
|
10
|
-
|
|
11
|
-
## Database integration
|
|
12
|
-
|
|
13
|
-
Whenever database is used, please use [database guide](./architecture/database/database.mdc) to create a database connection and methods.
|
|
14
|
-
|
|
15
7
|
## Real time events
|
|
16
8
|
|
|
17
9
|
Make sure to use [real time events guide](./rules/motia/realtime-streaming.mdc) to create a new real time event.
|
|
@@ -20,6 +12,22 @@ Make sure to use [real time events guide](./rules/motia/realtime-streaming.mdc)
|
|
|
20
12
|
|
|
21
13
|
Make sure to use [state management guide](./rules/motia/state-management.mdc) to create a new state management.
|
|
22
14
|
|
|
15
|
+
## Creating HTTP Endpoints
|
|
16
|
+
|
|
17
|
+
Make sure to use [API steps guide](./rules/motia/api-steps.mdc) to create new HTTP endpoints.
|
|
18
|
+
|
|
19
|
+
## Background Tasks
|
|
20
|
+
|
|
21
|
+
Make sure to use [event steps guide](./rules/motia/event-steps.mdc) to create new background tasks.
|
|
22
|
+
|
|
23
|
+
## Scheduled Tasks
|
|
24
|
+
|
|
25
|
+
Make sure to use [cron steps guide](./rules/motia/cron-steps.mdc) to create new scheduled tasks.
|
|
26
|
+
|
|
27
|
+
## Virtual Steps & Flow Visualization
|
|
28
|
+
|
|
29
|
+
Make sure to use [virtual steps guide](./rules/motia/virtual-steps.mdc) when connecting nodes virtually and creating smooth flows in Workbench.
|
|
30
|
+
|
|
23
31
|
## Authentication
|
|
24
32
|
|
|
25
33
|
If ever need to add authentication, make sure to use middleware to authenticate the request.
|
|
@@ -18,13 +18,16 @@ Steps need to be created in the `steps` folder, it can be in subfolders.
|
|
|
18
18
|
|
|
19
19
|
Defining an API Step is done by two elements. Configuration and Handler.
|
|
20
20
|
|
|
21
|
-
###
|
|
21
|
+
### Schema Definition
|
|
22
22
|
|
|
23
|
-
Motia
|
|
23
|
+
- **TypeScript/JavaScript**: Motia uses Zod schemas for automatic validation of request/response data
|
|
24
|
+
- **Python**: Motia uses JSON Schema format. You can optionally use Pydantic models to generate JSON Schemas and handle manual validation in your handlers
|
|
24
25
|
|
|
25
26
|
### Configuration
|
|
26
27
|
|
|
27
|
-
You need to export a config constant via `export const config` that is a `ApiRouteConfig` type.
|
|
28
|
+
**TypeScript/JavaScript**: You need to export a config constant via `export const config` that is a `ApiRouteConfig` type.
|
|
29
|
+
|
|
30
|
+
**Python**: You need to define a `config` dictionary with the same properties as the TypeScript `ApiRouteConfig`.
|
|
28
31
|
|
|
29
32
|
```typescript
|
|
30
33
|
export type Emit = string | {
|
|
@@ -189,13 +192,24 @@ export type ApiRouteHandler<
|
|
|
189
192
|
|
|
190
193
|
### Handler definition
|
|
191
194
|
|
|
195
|
+
**TypeScript/JavaScript:**
|
|
192
196
|
```typescript
|
|
193
197
|
export const handler: Handlers['CreateResource'] = async (req, { emit, logger, state, streams }) => {
|
|
194
198
|
// Implementation
|
|
195
199
|
}
|
|
196
200
|
```
|
|
197
201
|
|
|
198
|
-
|
|
202
|
+
**Python:**
|
|
203
|
+
```python
|
|
204
|
+
async def handler(req, context):
|
|
205
|
+
# req: dictionary containing pathParams, queryParams, body, headers
|
|
206
|
+
# context: object containing emit, logger, state, streams, trace_id
|
|
207
|
+
pass
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Examples
|
|
211
|
+
|
|
212
|
+
#### TypeScript Example
|
|
199
213
|
|
|
200
214
|
```typescript
|
|
201
215
|
import { ApiRouteConfig, Handlers } from 'motia';
|
|
@@ -314,4 +328,98 @@ export const handler: Handlers['CreateResource'] = async (req, { emit, logger })
|
|
|
314
328
|
};
|
|
315
329
|
}
|
|
316
330
|
};
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
#### Python Example
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
from pydantic import BaseModel, Field
|
|
337
|
+
from typing import Optional, Dict, Any
|
|
338
|
+
|
|
339
|
+
class ResourceData(BaseModel):
|
|
340
|
+
title: str = Field(..., min_length=1, description="Title cannot be empty")
|
|
341
|
+
description: Optional[str] = None
|
|
342
|
+
category: str = Field(..., min_length=1, description="Category is required")
|
|
343
|
+
metadata: Optional[Dict[str, Any]] = None
|
|
344
|
+
|
|
345
|
+
class ResourceResponse(BaseModel):
|
|
346
|
+
id: str
|
|
347
|
+
title: str
|
|
348
|
+
category: str
|
|
349
|
+
status: str
|
|
350
|
+
|
|
351
|
+
class ErrorResponse(BaseModel):
|
|
352
|
+
error: str
|
|
353
|
+
|
|
354
|
+
config = {
|
|
355
|
+
"type": "api",
|
|
356
|
+
"name": "CreateResource",
|
|
357
|
+
"path": "/resources",
|
|
358
|
+
"method": "POST",
|
|
359
|
+
"emits": ["send-email"],
|
|
360
|
+
"flows": ["resource-management"],
|
|
361
|
+
"bodySchema": ResourceData.model_json_schema(),
|
|
362
|
+
"responseSchema": {
|
|
363
|
+
201: ResourceResponse.model_json_schema(),
|
|
364
|
+
400: ErrorResponse.model_json_schema()
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async def handler(req, context):
|
|
369
|
+
try:
|
|
370
|
+
body = req.get("body", {})
|
|
371
|
+
|
|
372
|
+
# Optional: Validate input manually using Pydantic (Motia doesn't do this automatically)
|
|
373
|
+
resource_data = ResourceData(**body)
|
|
374
|
+
|
|
375
|
+
context.logger.info("Attempting to create resource", {
|
|
376
|
+
"title": resource_data.title,
|
|
377
|
+
"category": resource_data.category
|
|
378
|
+
})
|
|
379
|
+
|
|
380
|
+
# Process the resource creation
|
|
381
|
+
result = await service.create_resource({
|
|
382
|
+
"title": resource_data.title,
|
|
383
|
+
"description": resource_data.description,
|
|
384
|
+
"category": resource_data.category,
|
|
385
|
+
"metadata": resource_data.metadata
|
|
386
|
+
})
|
|
387
|
+
|
|
388
|
+
# Emit event to trigger other steps
|
|
389
|
+
await context.emit({
|
|
390
|
+
"topic": "send-email",
|
|
391
|
+
"data": {
|
|
392
|
+
"resource": result,
|
|
393
|
+
"user_id": "example-user"
|
|
394
|
+
}
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
context.logger.info("Resource created successfully", {
|
|
398
|
+
"resource_id": result.get("id"),
|
|
399
|
+
"title": result.get("title"),
|
|
400
|
+
"category": result.get("category")
|
|
401
|
+
})
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
"status": 201,
|
|
405
|
+
"body": {
|
|
406
|
+
"id": result.get("id"),
|
|
407
|
+
"title": result.get("title"),
|
|
408
|
+
"category": result.get("category"),
|
|
409
|
+
"status": "active"
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
except ValidationError as e:
|
|
414
|
+
context.logger.error("Resource creation failed - Pydantic validation error", {"error": str(e)})
|
|
415
|
+
return {
|
|
416
|
+
"status": 400,
|
|
417
|
+
"body": {"error": "Validation failed"}
|
|
418
|
+
}
|
|
419
|
+
except Exception as e:
|
|
420
|
+
context.logger.error("Resource creation failed", {"error": str(e)})
|
|
421
|
+
return {
|
|
422
|
+
"status": 500,
|
|
423
|
+
"body": {"error": "Creation failed"}
|
|
424
|
+
}
|
|
317
425
|
```
|
|
@@ -26,7 +26,9 @@ Defining a CRON Step is done by two elements. Configuration and Handler.
|
|
|
26
26
|
|
|
27
27
|
### Configuration
|
|
28
28
|
|
|
29
|
-
You need to export a config constant via `export const config` that is a `CronConfig` type.
|
|
29
|
+
**TypeScript/JavaScript**: You need to export a config constant via `export const config` that is a `CronConfig` type.
|
|
30
|
+
|
|
31
|
+
**Python**: You need to define a `config` dictionary with the same properties as the TypeScript `CronConfig`.
|
|
30
32
|
|
|
31
33
|
```typescript
|
|
32
34
|
export type Emit = string | {
|
|
@@ -98,6 +100,7 @@ export type CronConfig = {
|
|
|
98
100
|
|
|
99
101
|
The handler is a function that is exported via `export const handler` that is a `CronHandler` type.
|
|
100
102
|
|
|
103
|
+
**TypeScript/JavaScript:**
|
|
101
104
|
```typescript
|
|
102
105
|
/**
|
|
103
106
|
* CRON handler accepts only one argument, the FlowContext.
|
|
@@ -109,6 +112,13 @@ export const handler: Handlers['CronJobEvery5Minutes'] = async ({ logger, emit,
|
|
|
109
112
|
}
|
|
110
113
|
```
|
|
111
114
|
|
|
115
|
+
**Python:**
|
|
116
|
+
```python
|
|
117
|
+
async def handler(context):
|
|
118
|
+
# context: object containing emit, logger, state, streams, trace_id
|
|
119
|
+
context.logger.info("CRON Job Every 5 Minutes started")
|
|
120
|
+
```
|
|
121
|
+
|
|
112
122
|
### Examples of Cron expressions
|
|
113
123
|
|
|
114
124
|
- `0 0 * * *`: Runs daily at midnight
|
|
@@ -127,7 +137,9 @@ export const handler: Handlers['CronJobEvery5Minutes'] = async ({ logger, emit,
|
|
|
127
137
|
- Collecting metrics from third-party services
|
|
128
138
|
- Reconciling data from different sources
|
|
129
139
|
|
|
130
|
-
##
|
|
140
|
+
## Examples
|
|
141
|
+
|
|
142
|
+
### TypeScript Example
|
|
131
143
|
|
|
132
144
|
```typescript
|
|
133
145
|
export const config: CronConfig = {
|
|
@@ -138,7 +150,22 @@ export const config: CronConfig = {
|
|
|
138
150
|
flows: ['example-flow']
|
|
139
151
|
};
|
|
140
152
|
|
|
141
|
-
export const handler: Handlers['CronJobEvery5Minutes'] = async (
|
|
153
|
+
export const handler: Handlers['CronJobEvery5Minutes'] = async ({ logger }) => {
|
|
142
154
|
logger.info('Cron job started')
|
|
143
155
|
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Python Example
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
config = {
|
|
162
|
+
"type": "cron",
|
|
163
|
+
"name": "CronJobEvery5Minutes",
|
|
164
|
+
"cron": "*/5 * * * *", # Run every 5 minutes
|
|
165
|
+
"emits": [], # No emits in this example
|
|
166
|
+
"flows": ["example-flow"]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async def handler(context):
|
|
170
|
+
context.logger.info("Cron job started")
|
|
144
171
|
```
|
|
@@ -28,13 +28,16 @@ Steps need to be created in the `steps` folder, it can be in subfolders.
|
|
|
28
28
|
|
|
29
29
|
Defining an API Step is done by two elements. Configuration and Handler.
|
|
30
30
|
|
|
31
|
-
###
|
|
31
|
+
### Schema Definition
|
|
32
32
|
|
|
33
|
-
Motia
|
|
33
|
+
- **TypeScript/JavaScript**: Motia uses Zod schemas for automatic validation of input data
|
|
34
|
+
- **Python**: Motia uses JSON Schema format. You can optionally use Pydantic models to generate JSON Schemas and handle manual validation in your handlers
|
|
34
35
|
|
|
35
36
|
### Configuration
|
|
36
37
|
|
|
37
|
-
You need to export a config constant via `export const config` that is a `EventConfig` type.
|
|
38
|
+
**TypeScript/JavaScript**: You need to export a config constant via `export const config` that is a `EventConfig` type.
|
|
39
|
+
|
|
40
|
+
**Python**: You need to define a `config` dictionary with the same properties as the TypeScript `EventConfig`.
|
|
38
41
|
|
|
39
42
|
```typescript
|
|
40
43
|
export type Emit = string | {
|
|
@@ -119,17 +122,28 @@ The handler is a function that is exported via `export const handler` that is a
|
|
|
119
122
|
|
|
120
123
|
### Handler definition
|
|
121
124
|
|
|
125
|
+
**TypeScript/JavaScript:**
|
|
122
126
|
```typescript
|
|
123
127
|
/**
|
|
124
128
|
* Input is inferred from the Event Step config['input']
|
|
125
129
|
* Context is the FlowContext
|
|
126
130
|
*/
|
|
127
|
-
export const handler: Handlers['
|
|
131
|
+
export const handler: Handlers['SendEmail'] = async (input, { emit, logger, state, streams }) => {
|
|
128
132
|
// Implementation
|
|
129
133
|
}
|
|
130
134
|
```
|
|
131
135
|
|
|
132
|
-
|
|
136
|
+
**Python:**
|
|
137
|
+
```python
|
|
138
|
+
async def handler(input_data, context):
|
|
139
|
+
# input_data: dictionary with the event data (matches the input schema)
|
|
140
|
+
# context: object containing emit, logger, state, streams, trace_id
|
|
141
|
+
pass
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Examples
|
|
145
|
+
|
|
146
|
+
#### TypeScript Example
|
|
133
147
|
|
|
134
148
|
```typescript
|
|
135
149
|
import { EventConfig, Handlers } from 'motia';
|
|
@@ -152,6 +166,53 @@ export const config: EventConfig = {
|
|
|
152
166
|
};
|
|
153
167
|
|
|
154
168
|
export const handler: Handlers['SendEmail'] = async (input, { emit, logger }) => {
|
|
155
|
-
|
|
169
|
+
const { email, templateId, templateData } = input;
|
|
170
|
+
|
|
171
|
+
// Process email sending logic here
|
|
172
|
+
await emailService.send({
|
|
173
|
+
to: email,
|
|
174
|
+
templateId,
|
|
175
|
+
data: templateData
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
logger.info('Email sent successfully', { email, templateId });
|
|
156
179
|
};
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
#### Python Example
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from pydantic import BaseModel
|
|
186
|
+
from typing import Dict, Any
|
|
187
|
+
|
|
188
|
+
class EmailData(BaseModel):
|
|
189
|
+
email: str
|
|
190
|
+
template_id: str
|
|
191
|
+
template_data: Dict[str, Any]
|
|
192
|
+
|
|
193
|
+
config = {
|
|
194
|
+
"type": "event",
|
|
195
|
+
"name": "SendEmail",
|
|
196
|
+
"description": "Sends email notification to the user",
|
|
197
|
+
"subscribes": ["send-email"],
|
|
198
|
+
"emits": [],
|
|
199
|
+
"input": EmailData.model_json_schema(),
|
|
200
|
+
"flows": ["resource-management"]
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async def handler(input_data, context):
|
|
204
|
+
# Optional: Validate input manually using Pydantic (Motia doesn't do this automatically)
|
|
205
|
+
email_data = EmailData(**input_data)
|
|
206
|
+
|
|
207
|
+
# Process email sending logic here
|
|
208
|
+
await email_service.send({
|
|
209
|
+
"to": email_data.email,
|
|
210
|
+
"template_id": email_data.template_id,
|
|
211
|
+
"data": email_data.template_data
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
context.logger.info("Email sent successfully", {
|
|
215
|
+
"email": email_data.email,
|
|
216
|
+
"template_id": email_data.template_id
|
|
217
|
+
})
|
|
157
218
|
```
|