motia 0.5.11-beta.120-742949 → 0.5.11-beta.120-110250
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/cli.js +0 -8
- package/dist/cjs/cloud/build/builders/python/index.js +2 -21
- package/dist/cjs/cloud/build/builders/python/python-builder.py +28 -120
- package/dist/cjs/create/templates/default/motia-workbench.json +15 -16
- package/dist/cjs/create/templates/default/services/pet-store.ts.txt +24 -0
- package/dist/cjs/create/templates/default/services/types.ts.txt +21 -0
- package/dist/cjs/create/templates/default/steps/01-api.step.ts-features.json.txt +67 -0
- package/dist/cjs/create/templates/{basic-tutorial → default/steps}/01-api.step.ts.txt +5 -13
- package/dist/cjs/create/templates/default/steps/02-process-food-order.step.ts-features.json.txt +67 -0
- package/dist/{esm/create/templates/basic-tutorial → cjs/create/templates/default/steps}/02-process-food-order.step.ts.txt +21 -7
- package/dist/cjs/create/templates/default/steps/03-state-audit-cron.step.ts-features.json.txt +26 -0
- package/dist/cjs/create/templates/default/steps/03-state-audit-cron.step.ts.txt +51 -0
- package/dist/cjs/create/templates/default/steps/04-notification.step.ts.txt +35 -0
- package/dist/cjs/create/templates/default/tutorial.tsx.txt +643 -0
- package/dist/cjs/create/templates/generate.js +0 -4
- package/dist/cjs/create/templates/generate.ts +0 -5
- package/dist/esm/cli.js +0 -8
- package/dist/esm/cloud/build/builders/python/index.js +2 -21
- package/dist/esm/cloud/build/builders/python/python-builder.py +28 -120
- package/dist/esm/create/templates/default/motia-workbench.json +15 -16
- package/dist/esm/create/templates/default/services/pet-store.ts.txt +24 -0
- package/dist/esm/create/templates/default/services/types.ts.txt +21 -0
- package/dist/esm/create/templates/default/steps/01-api.step.ts-features.json.txt +67 -0
- package/dist/esm/create/templates/{basic-tutorial → default/steps}/01-api.step.ts.txt +5 -13
- package/dist/esm/create/templates/default/steps/02-process-food-order.step.ts-features.json.txt +67 -0
- package/dist/{cjs/create/templates/basic-tutorial → esm/create/templates/default/steps}/02-process-food-order.step.ts.txt +21 -7
- package/dist/esm/create/templates/default/steps/03-state-audit-cron.step.ts-features.json.txt +26 -0
- package/dist/esm/create/templates/default/steps/03-state-audit-cron.step.ts.txt +51 -0
- package/dist/esm/create/templates/default/steps/04-notification.step.ts.txt +35 -0
- package/dist/esm/create/templates/default/tutorial.tsx.txt +643 -0
- package/dist/esm/create/templates/generate.js +0 -4
- package/dist/esm/create/templates/generate.ts +0 -5
- package/package.json +4 -4
- package/dist/cjs/create/setup-tutorial-flow.d.ts +0 -6
- package/dist/cjs/create/setup-tutorial-flow.js +0 -30
- package/dist/cjs/create/templates/basic-tutorial/03-state-audit-cron.step.ts.txt +0 -42
- package/dist/cjs/create/templates/basic-tutorial/04_new_order_notifications.step.py.txt +0 -27
- package/dist/cjs/create/templates/basic-tutorial/motia-workbench.json +0 -28
- package/dist/cjs/create/templates/basic-tutorial/services/pet-store.ts.txt +0 -32
- package/dist/cjs/create/templates/default/00-noop.step.ts.txt +0 -33
- package/dist/cjs/create/templates/default/00-noop.step.tsx.txt +0 -18
- package/dist/cjs/create/templates/default/01-api.step.ts.txt +0 -70
- package/dist/cjs/create/templates/default/02-test-state.step.ts.txt +0 -53
- package/dist/cjs/create/templates/default/03-check-state-change.step.ts.txt +0 -54
- package/dist/esm/create/setup-tutorial-flow.d.ts +0 -6
- package/dist/esm/create/setup-tutorial-flow.js +0 -23
- package/dist/esm/create/templates/basic-tutorial/03-state-audit-cron.step.ts.txt +0 -42
- package/dist/esm/create/templates/basic-tutorial/04_new_order_notifications.step.py.txt +0 -27
- package/dist/esm/create/templates/basic-tutorial/motia-workbench.json +0 -28
- package/dist/esm/create/templates/basic-tutorial/services/pet-store.ts.txt +0 -32
- package/dist/esm/create/templates/default/00-noop.step.ts.txt +0 -33
- package/dist/esm/create/templates/default/00-noop.step.tsx.txt +0 -18
- package/dist/esm/create/templates/default/01-api.step.ts.txt +0 -70
- package/dist/esm/create/templates/default/02-test-state.step.ts.txt +0 -53
- package/dist/esm/create/templates/default/03-check-state-change.step.ts.txt +0 -54
- package/dist/types/create/setup-tutorial-flow.d.ts +0 -6
|
@@ -14,11 +14,6 @@ export const generateTemplateSteps = (templateFolder: string): Generator => {
|
|
|
14
14
|
for (const fileName of files) {
|
|
15
15
|
const filePath = path.join(templatePath, fileName)
|
|
16
16
|
|
|
17
|
-
if (statSync(filePath).isDirectory() && !filePath.match(/services|utils|lib/)) {
|
|
18
|
-
// ignore folders
|
|
19
|
-
continue
|
|
20
|
-
}
|
|
21
|
-
|
|
22
17
|
if (statSync(filePath).isDirectory()) {
|
|
23
18
|
const folderPath = path.basename(filePath)
|
|
24
19
|
mkdirSync(path.join(rootDir, templateFolder, folderPath))
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "motia",
|
|
3
3
|
"description": "A Modern Unified Backend Framework for APIs, Events and Agents",
|
|
4
|
-
"version": "0.5.11-beta.120-
|
|
4
|
+
"version": "0.5.11-beta.120-110250",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"inquirer": "^8.2.5",
|
|
44
44
|
"table": "^6.9.0",
|
|
45
45
|
"ts-node": "^10.9.2",
|
|
46
|
-
"@motiadev/
|
|
47
|
-
"@motiadev/
|
|
48
|
-
"@motiadev/
|
|
46
|
+
"@motiadev/core": "0.5.11-beta.120-110250",
|
|
47
|
+
"@motiadev/stream-client-node": "0.5.11-beta.120-110250",
|
|
48
|
+
"@motiadev/workbench": "0.5.11-beta.120-110250"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@amplitude/analytics-types": "^2.9.2",
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.createTutorialFlow = void 0;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const fs_1 = __importDefault(require("fs"));
|
|
9
|
-
const utils_1 = require("./utils");
|
|
10
|
-
const setup_template_1 = require("./setup-template");
|
|
11
|
-
const createTutorialFlow = async ({ context }) => {
|
|
12
|
-
const rootDir = process.cwd();
|
|
13
|
-
if (!(0, utils_1.checkIfFileExists)(rootDir, 'motia-workbench.json')) {
|
|
14
|
-
context.log('invalid-project', (message) => message
|
|
15
|
-
.tag('failed')
|
|
16
|
-
.append('In order to setup the Motia tutorial you need to be in a valid Motia project, motia-workbench.json not found, if this is not a Motia project you can create one using the motia create cli command.'));
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
const stepsDir = path_1.default.join(rootDir, 'steps');
|
|
20
|
-
if (!(0, utils_1.checkIfDirectoryExists)(stepsDir)) {
|
|
21
|
-
fs_1.default.mkdirSync(stepsDir);
|
|
22
|
-
context.log('steps-directory-created', (message) => message.tag('success').append('Folder').append('steps', 'cyan').append('has been created.'));
|
|
23
|
-
}
|
|
24
|
-
if (!(0, utils_1.checkIfDirectoryExists)(path_1.default.join(stepsDir, 'basic-tutorial'))) {
|
|
25
|
-
fs_1.default.mkdirSync(path_1.default.join(stepsDir, 'basic-tutorial'));
|
|
26
|
-
}
|
|
27
|
-
await (0, setup_template_1.setupTemplate)('basic-tutorial', stepsDir, context);
|
|
28
|
-
context.log('tutorial-flow-setup-completed', (message) => message.tag('success').append('Tutorial flow setup completed, you can now start the tutorial from Workbench.'));
|
|
29
|
-
};
|
|
30
|
-
exports.createTutorialFlow = createTutorialFlow;
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { CronConfig, Handlers } from 'motia'
|
|
2
|
-
|
|
3
|
-
export const config: CronConfig = {
|
|
4
|
-
type: 'cron' as const,
|
|
5
|
-
name: 'StateAuditJob',
|
|
6
|
-
description: 'Runs every minute and emits a timestamp',
|
|
7
|
-
cron: '*/5 * * * *', // run every hour at minute 0
|
|
8
|
-
emits: ['order-audit-error', 'order-audit-warning'],
|
|
9
|
-
flows: ['basic-tutorial'],
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const handler: Handlers['StateAuditJob'] = async ({ state, emit }) => {
|
|
13
|
-
const stateValue = await state.getGroup<{
|
|
14
|
-
id: number
|
|
15
|
-
petId: number
|
|
16
|
-
quantity: number
|
|
17
|
-
shipDate: string
|
|
18
|
-
status: string
|
|
19
|
-
complete: boolean
|
|
20
|
-
}>('orders')
|
|
21
|
-
|
|
22
|
-
if (!Array.isArray(stateValue)) {
|
|
23
|
-
await emit({
|
|
24
|
-
topic: 'state-audit-error',
|
|
25
|
-
data: { message: 'State value is not an array' },
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
for (const item of stateValue) {
|
|
32
|
-
// check if current date is after item.shipDate
|
|
33
|
-
const currentDate = new Date()
|
|
34
|
-
const shipDate = new Date(item.shipDate)
|
|
35
|
-
if (!item.complete && currentDate > shipDate) {
|
|
36
|
-
await emit({
|
|
37
|
-
topic: 'order-audit-warning',
|
|
38
|
-
data: { message: 'Order is not complete and ship date is past' },
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field
|
|
2
|
-
|
|
3
|
-
class NewOrderNotificationInput(BaseModel):
|
|
4
|
-
order_id: str = Field(description="pet store order id")
|
|
5
|
-
|
|
6
|
-
config = {
|
|
7
|
-
"type": "event",
|
|
8
|
-
"name": "NewOrderNotifications",
|
|
9
|
-
"description": "Checks a state change using python",
|
|
10
|
-
"subscribes": ["new-order-notification"],
|
|
11
|
-
"emits": [],
|
|
12
|
-
"flows": ["basic-tutorial"],
|
|
13
|
-
"input": NewOrderNotificationInput.model_json_schema(),
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async def handler(input, ctx):
|
|
17
|
-
ctx.logger.info('Processing NewOrderNotifications', input)
|
|
18
|
-
ctx.logger.info('[NewOrderNotifications] order id', input.get('order_id'))
|
|
19
|
-
|
|
20
|
-
order = await ctx.state.get('orders', input.get('order_id'))
|
|
21
|
-
|
|
22
|
-
# This represents a call to some sort of notification service ot indicate that a new order has been placed
|
|
23
|
-
ctx.logger.info('New order notification sent using Python: ', {
|
|
24
|
-
'order_id': input.get('order_id'),
|
|
25
|
-
'order': order,
|
|
26
|
-
'trace_id': ctx.trace_id
|
|
27
|
-
})
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"id": "basic-tutorial",
|
|
4
|
-
"config": {
|
|
5
|
-
"steps/basic-tutorial/04_new_order_notifications.step.py": {
|
|
6
|
-
"x": 668,
|
|
7
|
-
"y": 266,
|
|
8
|
-
"targetHandlePosition": "left"
|
|
9
|
-
},
|
|
10
|
-
"steps/basic-tutorial/03-state-audit-cron.step.ts": {
|
|
11
|
-
"x": 224,
|
|
12
|
-
"y": 520
|
|
13
|
-
},
|
|
14
|
-
"steps/basic-tutorial/02-process-food-order.step.ts": {
|
|
15
|
-
"x": 220,
|
|
16
|
-
"y": 242,
|
|
17
|
-
"sourceHandlePosition": "right",
|
|
18
|
-
"targetHandlePosition": "left"
|
|
19
|
-
},
|
|
20
|
-
"steps/basic-tutorial/01-api.step.ts": {
|
|
21
|
-
"x": -243,
|
|
22
|
-
"y": 199,
|
|
23
|
-
"sourceHandlePosition": "right",
|
|
24
|
-
"targetHandlePosition": "left"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
]
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export const petStoreService = {
|
|
2
|
-
createPet: async (pet: { name: string; photoUrl: string }) => {
|
|
3
|
-
const response = await fetch('https://petstore.swagger.io/v2/pet', {
|
|
4
|
-
method: 'POST',
|
|
5
|
-
body: JSON.stringify({
|
|
6
|
-
name: pet.name,
|
|
7
|
-
photoUrls: [pet.photoUrl],
|
|
8
|
-
status: 'available',
|
|
9
|
-
}),
|
|
10
|
-
headers: {
|
|
11
|
-
'Content-Type': 'application/json',
|
|
12
|
-
},
|
|
13
|
-
})
|
|
14
|
-
return response.json()
|
|
15
|
-
},
|
|
16
|
-
createOrder: async (order: { id: string; quantity: number; petId: number }) => {
|
|
17
|
-
const response = await fetch('https://petstore.swagger.io/v2/store/order', {
|
|
18
|
-
method: 'POST',
|
|
19
|
-
body: JSON.stringify({
|
|
20
|
-
id: order.id,
|
|
21
|
-
quantity: order.quantity,
|
|
22
|
-
petId: order.petId,
|
|
23
|
-
shipDate: new Date().toISOString(),
|
|
24
|
-
status: 'placed',
|
|
25
|
-
}),
|
|
26
|
-
headers: {
|
|
27
|
-
'Content-Type': 'application/json',
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
return response.json()
|
|
31
|
-
},
|
|
32
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { NoopConfig } from 'motia'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* NOOP Steps don"t hold any logic in code. They are a way
|
|
5
|
-
* to create custom nodes for flows on the workbench and
|
|
6
|
-
* represent external actions such as human in the loop.
|
|
7
|
-
*
|
|
8
|
-
* In this case, we are using it to add a custom UI
|
|
9
|
-
* node with a play button to allow you to trigger the first
|
|
10
|
-
* API step in this flow.
|
|
11
|
-
*
|
|
12
|
-
* For more information, refer to the documentation: https://www.motia.dev/docs/workbench/noop-steps
|
|
13
|
-
*/
|
|
14
|
-
export const config: NoopConfig = {
|
|
15
|
-
type: 'noop',
|
|
16
|
-
name: 'Flow Starter',
|
|
17
|
-
description: 'Start the default flow',
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Used mostly to connect nodes that emit to this
|
|
21
|
-
*/
|
|
22
|
-
virtualSubscribes: [],
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Used mostly to connect nodes that subscribes to this
|
|
26
|
-
*/
|
|
27
|
-
virtualEmits: ['/default'],
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The flows this step belongs to, will be available in Workbench
|
|
31
|
-
*/
|
|
32
|
-
flows: ['default'],
|
|
33
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { BaseNode, NoopNodeProps } from 'motia/workbench'
|
|
3
|
-
import { Button } from '@motiadev/ui'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* For more information on how to override UI nodes, check documentation https://www.motia.dev/docs/workbench/ui-steps
|
|
7
|
-
*/
|
|
8
|
-
export const Node: React.FC<NoopNodeProps> = (data) => {
|
|
9
|
-
const start = () => {
|
|
10
|
-
fetch('/default', { method: 'POST', body: JSON.stringify({ message: 'test' }) })
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<BaseNode title="Start" variant="noop" {...data} disableTargetHandle>
|
|
15
|
-
<Button variant="accent" data-testid="start-flow-button" onClick={start}>Start Flow</Button>
|
|
16
|
-
</BaseNode>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { ApiRouteConfig, Handlers } from 'motia'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
|
|
4
|
-
export const config: ApiRouteConfig = {
|
|
5
|
-
type: 'api',
|
|
6
|
-
name: 'ApiTrigger',
|
|
7
|
-
description: 'default template api trigger',
|
|
8
|
-
|
|
9
|
-
method: 'POST',
|
|
10
|
-
path: '/default',
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* This API Step emits events to topic `test-state`
|
|
14
|
-
*/
|
|
15
|
-
emits: ['test-state'],
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Expected request body for type checking and documentation
|
|
19
|
-
*/
|
|
20
|
-
bodySchema: z.object({ message: z.string() }),
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Expected response body for type checking and documentation
|
|
24
|
-
*/
|
|
25
|
-
responseSchema: {
|
|
26
|
-
200: z.object({
|
|
27
|
-
message: z.string(),
|
|
28
|
-
traceId: z.string(),
|
|
29
|
-
})
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* We're using virtual subscribes to virtually connect noop step
|
|
34
|
-
* to this step.
|
|
35
|
-
*
|
|
36
|
-
* Noop step is defined in noop.step.ts
|
|
37
|
-
*/
|
|
38
|
-
virtualSubscribes: ['/default'],
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* The flows this step belongs to, will be available in Workbench
|
|
42
|
-
*/
|
|
43
|
-
flows: ['default'],
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export const handler: Handlers['ApiTrigger'] = async (req, { logger, emit, traceId }) => {
|
|
47
|
-
/**
|
|
48
|
-
* Avoid usage of console.log, use logger instead
|
|
49
|
-
*/
|
|
50
|
-
logger.info('Step 01 – Processing API Step', { body: req.body })
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Emit events to the topics to process asynchronously
|
|
54
|
-
*/
|
|
55
|
-
await emit({
|
|
56
|
-
topic: 'test-state',
|
|
57
|
-
data: { message: req.body.message },
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Return data back to the client
|
|
62
|
-
*/
|
|
63
|
-
return {
|
|
64
|
-
status: 200,
|
|
65
|
-
body: {
|
|
66
|
-
traceId,
|
|
67
|
-
message: 'test-state topic emitted',
|
|
68
|
-
},
|
|
69
|
-
}
|
|
70
|
-
}
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { EventConfig, Handlers } from 'motia'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
|
|
4
|
-
export const config: EventConfig = {
|
|
5
|
-
type: 'event',
|
|
6
|
-
name: 'SetStateChange',
|
|
7
|
-
description: 'set a state change for evaluation',
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* This step subscribes to the event `test-state` to
|
|
11
|
-
* be processed asynchronously.
|
|
12
|
-
*/
|
|
13
|
-
subscribes: ['test-state'],
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* It ultimately emits an event to `check-state-change` topic.
|
|
17
|
-
*/
|
|
18
|
-
emits: ['check-state-change'],
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Definition of the expected input
|
|
22
|
-
*/
|
|
23
|
-
input: z.object({ message: z.string() }),
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The flows this step belongs to, will be available in Workbench
|
|
27
|
-
*/
|
|
28
|
-
flows: ['default'],
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const handler: Handlers['SetStateChange'] = async (input, { traceId, logger, state, emit }) => {
|
|
32
|
-
/**
|
|
33
|
-
* Avoid usage of console.log, use logger instead
|
|
34
|
-
*/
|
|
35
|
-
logger.info('Step 02 – Pushing message content to state', { input })
|
|
36
|
-
|
|
37
|
-
const message = 'Welcome to motia!'
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Persist content on state to be used by other steps
|
|
41
|
-
* or in other workflows later
|
|
42
|
-
*/
|
|
43
|
-
await state.set<string>(traceId, 'test', message)
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Emit events to the topics to process separately
|
|
47
|
-
* on another step
|
|
48
|
-
*/
|
|
49
|
-
await emit({
|
|
50
|
-
topic: 'check-state-change',
|
|
51
|
-
data: { key: 'test', expected: message }
|
|
52
|
-
})
|
|
53
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { EventConfig, Handlers } from 'motia'
|
|
2
|
-
import { z } from 'zod'
|
|
3
|
-
|
|
4
|
-
export const config: EventConfig = {
|
|
5
|
-
type: 'event',
|
|
6
|
-
name: 'CheckStateChange',
|
|
7
|
-
description: 'check state change',
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* This step subscribes to the event `check-state-change` to
|
|
11
|
-
* be processed asynchronously.
|
|
12
|
-
*/
|
|
13
|
-
subscribes: ['check-state-change'],
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Doesn't emit anything, which means this is a final step in a workflow
|
|
17
|
-
*/
|
|
18
|
-
emits: [],
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Definition of the expected input
|
|
22
|
-
*/
|
|
23
|
-
input: z.object({ key: z.string(), expected: z.string() }),
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* The flows this step belongs to, will be available in Workbench
|
|
27
|
-
*/
|
|
28
|
-
flows: ['default'],
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const handler: Handlers['CheckStateChange'] = async (input, { traceId, logger, state }) => {
|
|
32
|
-
/**
|
|
33
|
-
* Avoid usage of console.log, use logger instead
|
|
34
|
-
*/
|
|
35
|
-
logger.info('Step 03 – Executing CheckStateChange step', { input })
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Fetches value from state with the given key
|
|
39
|
-
*/
|
|
40
|
-
const value = await state.get<string>(traceId, input.key)
|
|
41
|
-
|
|
42
|
-
if (value !== input.expected) {
|
|
43
|
-
logger.error('The provided value for the state key does not match', {
|
|
44
|
-
key: input.key,
|
|
45
|
-
value,
|
|
46
|
-
expected: input.expected
|
|
47
|
-
})
|
|
48
|
-
} else {
|
|
49
|
-
logger.info('The provided value matches the state value 🏁', {
|
|
50
|
-
key: input.key,
|
|
51
|
-
value,
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import path from 'path';
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import { checkIfDirectoryExists, checkIfFileExists } from './utils';
|
|
4
|
-
import { setupTemplate } from './setup-template';
|
|
5
|
-
export const createTutorialFlow = async ({ context }) => {
|
|
6
|
-
const rootDir = process.cwd();
|
|
7
|
-
if (!checkIfFileExists(rootDir, 'motia-workbench.json')) {
|
|
8
|
-
context.log('invalid-project', (message) => message
|
|
9
|
-
.tag('failed')
|
|
10
|
-
.append('In order to setup the Motia tutorial you need to be in a valid Motia project, motia-workbench.json not found, if this is not a Motia project you can create one using the motia create cli command.'));
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
const stepsDir = path.join(rootDir, 'steps');
|
|
14
|
-
if (!checkIfDirectoryExists(stepsDir)) {
|
|
15
|
-
fs.mkdirSync(stepsDir);
|
|
16
|
-
context.log('steps-directory-created', (message) => message.tag('success').append('Folder').append('steps', 'cyan').append('has been created.'));
|
|
17
|
-
}
|
|
18
|
-
if (!checkIfDirectoryExists(path.join(stepsDir, 'basic-tutorial'))) {
|
|
19
|
-
fs.mkdirSync(path.join(stepsDir, 'basic-tutorial'));
|
|
20
|
-
}
|
|
21
|
-
await setupTemplate('basic-tutorial', stepsDir, context);
|
|
22
|
-
context.log('tutorial-flow-setup-completed', (message) => message.tag('success').append('Tutorial flow setup completed, you can now start the tutorial from Workbench.'));
|
|
23
|
-
};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { CronConfig, Handlers } from 'motia'
|
|
2
|
-
|
|
3
|
-
export const config: CronConfig = {
|
|
4
|
-
type: 'cron' as const,
|
|
5
|
-
name: 'StateAuditJob',
|
|
6
|
-
description: 'Runs every minute and emits a timestamp',
|
|
7
|
-
cron: '*/5 * * * *', // run every hour at minute 0
|
|
8
|
-
emits: ['order-audit-error', 'order-audit-warning'],
|
|
9
|
-
flows: ['basic-tutorial'],
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const handler: Handlers['StateAuditJob'] = async ({ state, emit }) => {
|
|
13
|
-
const stateValue = await state.getGroup<{
|
|
14
|
-
id: number
|
|
15
|
-
petId: number
|
|
16
|
-
quantity: number
|
|
17
|
-
shipDate: string
|
|
18
|
-
status: string
|
|
19
|
-
complete: boolean
|
|
20
|
-
}>('orders')
|
|
21
|
-
|
|
22
|
-
if (!Array.isArray(stateValue)) {
|
|
23
|
-
await emit({
|
|
24
|
-
topic: 'state-audit-error',
|
|
25
|
-
data: { message: 'State value is not an array' },
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
for (const item of stateValue) {
|
|
32
|
-
// check if current date is after item.shipDate
|
|
33
|
-
const currentDate = new Date()
|
|
34
|
-
const shipDate = new Date(item.shipDate)
|
|
35
|
-
if (!item.complete && currentDate > shipDate) {
|
|
36
|
-
await emit({
|
|
37
|
-
topic: 'order-audit-warning',
|
|
38
|
-
data: { message: 'Order is not complete and ship date is past' },
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
from pydantic import BaseModel, Field
|
|
2
|
-
|
|
3
|
-
class NewOrderNotificationInput(BaseModel):
|
|
4
|
-
order_id: str = Field(description="pet store order id")
|
|
5
|
-
|
|
6
|
-
config = {
|
|
7
|
-
"type": "event",
|
|
8
|
-
"name": "NewOrderNotifications",
|
|
9
|
-
"description": "Checks a state change using python",
|
|
10
|
-
"subscribes": ["new-order-notification"],
|
|
11
|
-
"emits": [],
|
|
12
|
-
"flows": ["basic-tutorial"],
|
|
13
|
-
"input": NewOrderNotificationInput.model_json_schema(),
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
async def handler(input, ctx):
|
|
17
|
-
ctx.logger.info('Processing NewOrderNotifications', input)
|
|
18
|
-
ctx.logger.info('[NewOrderNotifications] order id', input.get('order_id'))
|
|
19
|
-
|
|
20
|
-
order = await ctx.state.get('orders', input.get('order_id'))
|
|
21
|
-
|
|
22
|
-
# This represents a call to some sort of notification service ot indicate that a new order has been placed
|
|
23
|
-
ctx.logger.info('New order notification sent using Python: ', {
|
|
24
|
-
'order_id': input.get('order_id'),
|
|
25
|
-
'order': order,
|
|
26
|
-
'trace_id': ctx.trace_id
|
|
27
|
-
})
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"id": "basic-tutorial",
|
|
4
|
-
"config": {
|
|
5
|
-
"steps/basic-tutorial/04_new_order_notifications.step.py": {
|
|
6
|
-
"x": 668,
|
|
7
|
-
"y": 266,
|
|
8
|
-
"targetHandlePosition": "left"
|
|
9
|
-
},
|
|
10
|
-
"steps/basic-tutorial/03-state-audit-cron.step.ts": {
|
|
11
|
-
"x": 224,
|
|
12
|
-
"y": 520
|
|
13
|
-
},
|
|
14
|
-
"steps/basic-tutorial/02-process-food-order.step.ts": {
|
|
15
|
-
"x": 220,
|
|
16
|
-
"y": 242,
|
|
17
|
-
"sourceHandlePosition": "right",
|
|
18
|
-
"targetHandlePosition": "left"
|
|
19
|
-
},
|
|
20
|
-
"steps/basic-tutorial/01-api.step.ts": {
|
|
21
|
-
"x": -243,
|
|
22
|
-
"y": 199,
|
|
23
|
-
"sourceHandlePosition": "right",
|
|
24
|
-
"targetHandlePosition": "left"
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
]
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
export const petStoreService = {
|
|
2
|
-
createPet: async (pet: { name: string; photoUrl: string }) => {
|
|
3
|
-
const response = await fetch('https://petstore.swagger.io/v2/pet', {
|
|
4
|
-
method: 'POST',
|
|
5
|
-
body: JSON.stringify({
|
|
6
|
-
name: pet.name,
|
|
7
|
-
photoUrls: [pet.photoUrl],
|
|
8
|
-
status: 'available',
|
|
9
|
-
}),
|
|
10
|
-
headers: {
|
|
11
|
-
'Content-Type': 'application/json',
|
|
12
|
-
},
|
|
13
|
-
})
|
|
14
|
-
return response.json()
|
|
15
|
-
},
|
|
16
|
-
createOrder: async (order: { id: string; quantity: number; petId: number }) => {
|
|
17
|
-
const response = await fetch('https://petstore.swagger.io/v2/store/order', {
|
|
18
|
-
method: 'POST',
|
|
19
|
-
body: JSON.stringify({
|
|
20
|
-
id: order.id,
|
|
21
|
-
quantity: order.quantity,
|
|
22
|
-
petId: order.petId,
|
|
23
|
-
shipDate: new Date().toISOString(),
|
|
24
|
-
status: 'placed',
|
|
25
|
-
}),
|
|
26
|
-
headers: {
|
|
27
|
-
'Content-Type': 'application/json',
|
|
28
|
-
},
|
|
29
|
-
})
|
|
30
|
-
return response.json()
|
|
31
|
-
},
|
|
32
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { NoopConfig } from 'motia'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* NOOP Steps don"t hold any logic in code. They are a way
|
|
5
|
-
* to create custom nodes for flows on the workbench and
|
|
6
|
-
* represent external actions such as human in the loop.
|
|
7
|
-
*
|
|
8
|
-
* In this case, we are using it to add a custom UI
|
|
9
|
-
* node with a play button to allow you to trigger the first
|
|
10
|
-
* API step in this flow.
|
|
11
|
-
*
|
|
12
|
-
* For more information, refer to the documentation: https://www.motia.dev/docs/workbench/noop-steps
|
|
13
|
-
*/
|
|
14
|
-
export const config: NoopConfig = {
|
|
15
|
-
type: 'noop',
|
|
16
|
-
name: 'Flow Starter',
|
|
17
|
-
description: 'Start the default flow',
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Used mostly to connect nodes that emit to this
|
|
21
|
-
*/
|
|
22
|
-
virtualSubscribes: [],
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Used mostly to connect nodes that subscribes to this
|
|
26
|
-
*/
|
|
27
|
-
virtualEmits: ['/default'],
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* The flows this step belongs to, will be available in Workbench
|
|
31
|
-
*/
|
|
32
|
-
flows: ['default'],
|
|
33
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { BaseNode, NoopNodeProps } from 'motia/workbench'
|
|
3
|
-
import { Button } from '@motiadev/ui'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* For more information on how to override UI nodes, check documentation https://www.motia.dev/docs/workbench/ui-steps
|
|
7
|
-
*/
|
|
8
|
-
export const Node: React.FC<NoopNodeProps> = (data) => {
|
|
9
|
-
const start = () => {
|
|
10
|
-
fetch('/default', { method: 'POST', body: JSON.stringify({ message: 'test' }) })
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<BaseNode title="Start" variant="noop" {...data} disableTargetHandle>
|
|
15
|
-
<Button variant="accent" data-testid="start-flow-button" onClick={start}>Start Flow</Button>
|
|
16
|
-
</BaseNode>
|
|
17
|
-
)
|
|
18
|
-
}
|