Skip to content

Commit 309d6b3

Browse files
authored
Merge pull request #1039 from dapr/workflow
Reworked javascript workflow examples to use a webserver
2 parents 2ad346b + 74e2a59 commit 309d6b3

File tree

9 files changed

+416
-15
lines changed

9 files changed

+416
-15
lines changed

workflows/javascript/sdk/README.md

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
In this quickstart, you'll create a simple console application to demonstrate Dapr's workflow programming model and the workflow management API. The console app starts and manages the lifecycle of a workflow that stores and retrieves data in a state store.
44

5-
This quickstart includes one project:
5+
This quickstart includes 3 entry points demonstrating different ways to host a workflow:
66

7-
- JavaScript console app `order-processor`
7+
1. JavaScript console app `order-processor`
8+
2. Express app `order-process-with-express-server`
9+
3. Express app via Dapr Server `order-process-with-dapr-server`
810

911
The quickstart contains 1 workflow to simulate purchasing items from a store, and 5 unique activities within the workflow. These 5 activities are as follows:
1012

@@ -16,7 +18,7 @@ The quickstart contains 1 workflow to simulate purchasing items from a store, an
1618

1719
### Run the order processor workflow with multi-app-run
1820

19-
1. Open a new terminal window and navigate to `order-processor` directory:
21+
1. Open a new terminal window and install the dependencies:
2022

2123
<!-- STEP
2224
name: build order-process app
@@ -29,12 +31,13 @@ npm run build
2931
```
3032

3133
<!-- END_STEP -->
32-
2. Run the console app with Dapr:
34+
2. Run the app
3335

36+
- Entry point 1 : JavaScript console app
3437
<!-- STEP
3538
name: Run order-processor service
3639
expected_stdout_lines:
37-
- '== APP - workflowApp == == APP == Payment of 100 for 10 item1 processed successfully'
40+
- '== APP - workflowApp == Payment of 100 for 10 item1 processed successfully'
3841
- 'there are now 90 item1 in stock'
3942
- 'processed successfully!'
4043
expected_stderr_lines:
@@ -43,13 +46,33 @@ background: true
4346
sleep: 15
4447
timeout_seconds: 120
4548
-->
46-
49+
4750
```bash
4851
dapr run -f .
4952
```
50-
5153
<!-- END_STEP -->
5254

55+
- Entry point 2 : Express app
56+
57+
```bash
58+
dapr run -f dapr-AppWithExpressServer.yaml
59+
```
60+
61+
```bash
62+
curl --request POST \
63+
--url http://localhost:3500/v1.0/invoke/workflowApp/method/start-workflow
64+
```
65+
66+
- Entry point 3 : Express app via Dapr Server
67+
68+
```bash
69+
dapr run -f dapr-AppWithDaprServer.yaml
70+
```
71+
```bash
72+
curl --request POST \
73+
--url http://localhost:3500/v1.0/invoke/workflowApp/method/start-workflow
74+
```
75+
5376
3. Expected output
5477

5578

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: 1
2+
common:
3+
resourcesPath: ../../components
4+
apps:
5+
- appID: workflowApp
6+
appDirPath: ./order-processor/
7+
appPort: 3000
8+
daprHTTPPort: 3500
9+
daprGRPCPort: 50001
10+
command: ["npm", "run", "start:order-process-with-dapr-server"]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
version: 1
2+
common:
3+
resourcesPath: ../../components
4+
apps:
5+
- appID: workflowApp
6+
appDirPath: ./order-processor/
7+
appPort: 3000
8+
daprHTTPPort: 3500
9+
daprGRPCPort: 50001
10+
command: ["npm", "run", "start:order-process-with-express-server"]

workflows/javascript/sdk/dapr.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ common:
44
apps:
55
- appID: workflowApp
66
appDirPath: ./order-processor/
7-
command: ["npm", "run", "start:dapr:order-process"]
7+
command: ["npm", "run", "start:order-process"]

workflows/javascript/sdk/order-processor/workflowApp.ts renamed to workflows/javascript/sdk/order-processor/app.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1-
import { DaprWorkflowClient, WorkflowRuntime, DaprClient } from "@dapr/dapr";
1+
import { DaprWorkflowClient, WorkflowRuntime, DaprClient, CommunicationProtocolEnum } from "@dapr/dapr";
22
import { InventoryItem, OrderPayload } from "./model";
33
import { notifyActivity, orderProcessingWorkflow, processPaymentActivity, requestApprovalActivity, reserveInventoryActivity, updateInventoryActivity } from "./orderProcessingWorkflow";
44

5+
const workflowWorker = new WorkflowRuntime();
6+
57
async function start() {
68
// Update the gRPC client and worker to use a local address and port
79
const workflowClient = new DaprWorkflowClient();
8-
const workflowWorker = new WorkflowRuntime();
910

10-
const daprClient = new DaprClient();
11+
12+
const daprHost = process.env.DAPR_HOST ?? "127.0.0.1";
13+
const daprPort = process.env.DAPR_GRPC_PORT ?? "50001";
14+
15+
const daprClient = new DaprClient({
16+
daprHost,
17+
daprPort,
18+
communicationProtocol: CommunicationProtocolEnum.GRPC,
19+
});
20+
1121
const storeName = "statestore";
1222

1323
const inventory = new InventoryItem("item1", 100, 100);
@@ -52,10 +62,13 @@ async function start() {
5262
throw error;
5363
}
5464

55-
await workflowWorker.stop();
5665
await workflowClient.stop();
5766
}
5867

68+
process.on('SIGTERM', () => {
69+
workflowWorker.stop();
70+
})
71+
5972
start().catch((e) => {
6073
console.error(e);
6174
process.exit(1);
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { DaprWorkflowClient, WorkflowRuntime, DaprClient } from "@dapr/dapr";
2+
import { InventoryItem, OrderPayload } from "./model";
3+
import { notifyActivity, orderProcessingWorkflow, processPaymentActivity, requestApprovalActivity, reserveInventoryActivity, updateInventoryActivity } from "./orderProcessingWorkflow";
4+
import { DaprServer, CommunicationProtocolEnum } from "@dapr/dapr";
5+
import express from "express";
6+
7+
const daprHost = process.env.DAPR_HOST ?? "localhost"; // Dapr Sidecar Host
8+
const daprPort = process.env.DAPR_HTTP_PORT || "3500"; // Dapr Sidecar Port of this Example Server
9+
10+
const app = express();
11+
12+
const daprServer = new DaprServer({
13+
serverHost: "127.0.0.1", // App Host
14+
serverPort: process.env.APP_PORT || "3000", // App Port
15+
serverHttp: app,
16+
communicationProtocol: CommunicationProtocolEnum.HTTP, // Add this line
17+
clientOptions: {
18+
daprHost,
19+
daprPort
20+
}
21+
});
22+
23+
const daprClient = new DaprClient();
24+
const workflowClient = new DaprWorkflowClient();
25+
const workflowWorker = new WorkflowRuntime();
26+
27+
app.post("/start-workflow", async (req, res) => {
28+
29+
const storeName = "statestore";
30+
const inventory = new InventoryItem("item1", 100, 100);
31+
const key = inventory.itemName;
32+
33+
await daprClient.state.delete(storeName, key);
34+
await daprClient.state.save(storeName, [
35+
{
36+
key: key,
37+
value: inventory,
38+
}
39+
]);
40+
41+
const order = new OrderPayload("item1", 100, 10);
42+
43+
// Schedule a new orchestration
44+
try {
45+
const id = await workflowClient.scheduleNewWorkflow(orderProcessingWorkflow, order);
46+
console.log(`Orchestration scheduled with ID: ${id}`);
47+
48+
// Wait for orchestration completion
49+
const state = await workflowClient.waitForWorkflowCompletion(id, undefined, 30);
50+
51+
var orchestrationResult = `Orchestration completed! Result: ${state?.serializedOutput}`;
52+
console.log(orchestrationResult);
53+
} catch (error) {
54+
console.error("Error scheduling or waiting for orchestration:", error);
55+
throw error;
56+
}
57+
58+
res.send(orchestrationResult);
59+
});
60+
61+
async function start() {
62+
workflowWorker
63+
.registerWorkflow(orderProcessingWorkflow)
64+
.registerActivity(notifyActivity)
65+
.registerActivity(reserveInventoryActivity)
66+
.registerActivity(requestApprovalActivity)
67+
.registerActivity(processPaymentActivity)
68+
.registerActivity(updateInventoryActivity);
69+
70+
// Wrap the worker startup in a try-catch block to handle any errors during startup
71+
try {
72+
await workflowWorker.start();
73+
console.log("Workflow runtime started successfully");
74+
} catch (error) {
75+
console.error("Error starting workflow runtime:", error);
76+
}
77+
78+
// Initialize subscriptions before the server starts, the Dapr sidecar uses it.
79+
// This will also initialize the app server itself (removing the need for `app.listen` to be called).
80+
await daprServer.start();
81+
};
82+
83+
start().catch((e) => {
84+
workflowWorker.stop();
85+
console.error(e);
86+
process.exit(1);
87+
});
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import { DaprWorkflowClient, WorkflowRuntime, DaprClient } from "@dapr/dapr";
2+
import { InventoryItem, OrderPayload } from "./model";
3+
import { notifyActivity, orderProcessingWorkflow, processPaymentActivity, requestApprovalActivity, reserveInventoryActivity, updateInventoryActivity } from "./orderProcessingWorkflow";
4+
import express from "express";
5+
6+
const app = express();
7+
8+
const daprClient = new DaprClient();
9+
const workflowClient = new DaprWorkflowClient();
10+
const workflowWorker = new WorkflowRuntime();
11+
12+
app.post("/start-workflow", async (req, res) => {
13+
14+
const storeName = "statestore";
15+
const inventory = new InventoryItem("item1", 100, 100);
16+
const key = inventory.itemName;
17+
18+
await daprClient.state.save(storeName, [
19+
{
20+
key: key,
21+
value: inventory,
22+
}
23+
]);
24+
25+
const order = new OrderPayload("item1", 100, 10);
26+
27+
// Schedule a new orchestration
28+
try {
29+
const id = await workflowClient.scheduleNewWorkflow(orderProcessingWorkflow, order);
30+
console.log(`Orchestration scheduled with ID: ${id}`);
31+
32+
// Wait for orchestration completion
33+
const state = await workflowClient.waitForWorkflowCompletion(id, undefined, 30);
34+
35+
var orchestrationResult = `Orchestration completed! Result: ${state?.serializedOutput}`;
36+
console.log(orchestrationResult);
37+
} catch (error) {
38+
console.error("Error scheduling or waiting for orchestration:", error);
39+
throw error;
40+
}
41+
42+
res.send(orchestrationResult);
43+
});
44+
45+
async function start() {
46+
workflowWorker
47+
.registerWorkflow(orderProcessingWorkflow)
48+
.registerActivity(notifyActivity)
49+
.registerActivity(reserveInventoryActivity)
50+
.registerActivity(requestApprovalActivity)
51+
.registerActivity(processPaymentActivity)
52+
.registerActivity(updateInventoryActivity);
53+
54+
// Wrap the worker startup in a try-catch block to handle any errors during startup
55+
try {
56+
await workflowWorker.start();
57+
console.log("Workflow runtime started successfully");
58+
} catch (error) {
59+
console.error("Error starting workflow runtime:", error);
60+
}
61+
};
62+
63+
const server = app.listen(process.env.APP_PORT || 3000, () => {
64+
console.log(`Example app listening on port APP_PORT or 3000`);
65+
})
66+
67+
process.on('SIGTERM', () => {
68+
workflowWorker.stop();
69+
server.close();
70+
})
71+
72+
start().catch((e) => {
73+
workflowWorker.stop();
74+
console.error(e);
75+
process.exit(1);
76+
});

0 commit comments

Comments
 (0)