From 578df3a5cbed70857e66823aa5bb76962efa9302 Mon Sep 17 00:00:00 2001 From: pasindufernando1 Date: Tue, 23 Jun 2026 13:36:09 +0530 Subject: [PATCH] Add initial content --- .../.choreo/context.yaml | 3 + .../Ballerina.toml | 3 + .../orderprocessingautomation/.gitignore | 15 + .../orderprocessingautomation/Ballerina.toml | 27 ++ .../Dependencies.toml | 265 ++++++++++++++++++ .../orderprocessingautomation/README.md | 106 +++++++ .../orderprocessingautomation/agents.bal | 1 + .../orderprocessingautomation/automation.bal | 26 ++ .../orderprocessingautomation/config.bal | 10 + .../orderprocessingautomation/connections.bal | 7 + .../data_mappings.bal | 1 + .../orderprocessingautomation/functions.bal | 1 + .../orderprocessingautomation/main.bal | 1 + .../persist/ordersDB/model.bal | 57 ++++ .../orderprocessingautomation/types.bal | 26 ++ 15 files changed, 549 insertions(+) create mode 100644 integrator-default-profile/samples/order-management-automation/.choreo/context.yaml create mode 100644 integrator-default-profile/samples/order-management-automation/Ballerina.toml create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/.gitignore create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/Ballerina.toml create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/Dependencies.toml create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/README.md create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/agents.bal create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/automation.bal create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/config.bal create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/connections.bal create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/data_mappings.bal create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/functions.bal create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/main.bal create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/persist/ordersDB/model.bal create mode 100644 integrator-default-profile/samples/order-management-automation/orderprocessingautomation/types.bal diff --git a/integrator-default-profile/samples/order-management-automation/.choreo/context.yaml b/integrator-default-profile/samples/order-management-automation/.choreo/context.yaml new file mode 100644 index 00000000..c51c398f --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/.choreo/context.yaml @@ -0,0 +1,3 @@ +- org: pasindufernando + project: order-management-automation + local: true diff --git a/integrator-default-profile/samples/order-management-automation/Ballerina.toml b/integrator-default-profile/samples/order-management-automation/Ballerina.toml new file mode 100644 index 00000000..823b172a --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/Ballerina.toml @@ -0,0 +1,3 @@ +[workspace] +title = "order-management-automation" +packages = ["orderprocessingautomation"] diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/.gitignore b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/.gitignore new file mode 100644 index 00000000..2bbd1709 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/.gitignore @@ -0,0 +1,15 @@ +# Ballerina generates this directory during the compilation of a package. +# It contains compiler-generated artifacts and the final executable if this is an application package. +target/ + +# Ballerina maintains the compiler-generated source code here. +# Remove this if you want to commit generated sources. +generated/ + +# Contains configuration values used during development time. +# See https://ballerina.io/learn/provide-values-to-configurable-variables/ for more details. +Config.toml + +# File used to enable development-time tracing. +# This should not be committed to version control. +trace_enabled.bal \ No newline at end of file diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/Ballerina.toml b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/Ballerina.toml new file mode 100644 index 00000000..c005e52a --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/Ballerina.toml @@ -0,0 +1,27 @@ +[package] +org = "wso2" +name = "orderprocessingautomation" +version = "0.1.0" +distribution = "2201.13.3" +title = "OrderProcessingAutomation" + +[build-options] +sticky = true + +[[tool.persist]] +id = "orderprocessingautomation.ordersdb" +targetModule = "orderprocessingautomation.ordersdb" +filePath = "persist/ordersDB/model.bal" +options.datastore = "mysql" +options.eagerLoading = true +options.withInitParams = true + +[[dependency]] +org = "ballerina" +name = "tool.persist" +version = "1.9.1" + +[[platform.java21.dependency]] +groupId = "io.ballerina.stdlib" +artifactId = "persist.sql-native" +version = "1.7.2" diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/Dependencies.toml b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/Dependencies.toml new file mode 100644 index 00000000..9a2416e7 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/Dependencies.toml @@ -0,0 +1,265 @@ +# AUTO-GENERATED FILE. DO NOT MODIFY. + +# This file is auto-generated by Ballerina for managing dependency versions. +# It should not be modified by hand. + +[ballerina] +dependencies-toml-version = "2" +distribution-version = "2201.13.3-20260411-175200-6a2a1e46" + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.10.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "data.jsondata" +version = "1.1.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "email" +version = "2.13.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "task"} +] +modules = [ + {org = "ballerina", packageName = "email", moduleName = "email"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" +modules = [ + {org = "ballerina", packageName = "jballerina.java", moduleName = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.__internal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.int" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"}, + {org = "ballerina", name = "lang.object"} +] + +[[package]] +org = "ballerina" +name = "lang.object" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.17.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerina", packageName = "log", moduleName = "log"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.12.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.7.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "persist" +version = "1.7.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] +modules = [ + {org = "ballerina", packageName = "persist", moduleName = "persist"} +] + +[[package]] +org = "ballerina" +name = "random" +version = "1.7.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "sql" +version = "1.19.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.object"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "sql", moduleName = "sql"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.7.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] +modules = [ + {org = "ballerina", packageName = "time", moduleName = "time"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerinax" +name = "cdc" +version = "1.3.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "data.jsondata"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerinai", name = "observe"} +] + +[[package]] +org = "ballerinax" +name = "mysql" +version = "1.18.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "random"}, + {org = "ballerina", name = "sql"}, + {org = "ballerina", name = "time"}, + {org = "ballerinax", name = "cdc"} +] +modules = [ + {org = "ballerinax", packageName = "mysql", moduleName = "mysql"} +] + +[[package]] +org = "ballerinax" +name = "mysql.driver" +version = "1.8.1" +modules = [ + {org = "ballerinax", packageName = "mysql.driver", moduleName = "mysql.driver"} +] + +[[package]] +org = "ballerinax" +name = "persist.sql" +version = "1.7.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "persist"}, + {org = "ballerina", name = "sql"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerinax", packageName = "persist.sql", moduleName = "persist.sql"} +] + +[[package]] +org = "wso2" +name = "orderprocessingautomation" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "email"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "persist"}, + {org = "ballerina", name = "sql"}, + {org = "ballerina", name = "time"}, + {org = "ballerinax", name = "mysql"}, + {org = "ballerinax", name = "mysql.driver"}, + {org = "ballerinax", name = "persist.sql"} +] +modules = [ + {org = "wso2", packageName = "orderprocessingautomation", moduleName = "orderprocessingautomation"}, + {org = "wso2", packageName = "orderprocessingautomation", moduleName = "orderprocessingautomation.ordersdb"} +] + +[[tool]] +id = "persist" +org = "ballerina" +name = "tool.persist" +version = "1.9.2" + diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/README.md b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/README.md new file mode 100644 index 00000000..205d4569 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/README.md @@ -0,0 +1,106 @@ +# Order Management Automation + +A scheduled automation that processes newly placed orders. On each run it reads every order still in the `PLACED` state from a MySQL `orders_db`, emails the customer that their order is being handled, advances the order to `PROCESSING`, and logs a summary. When nothing is waiting, the run exits early. + +## How it works + +```mermaid +flowchart LR + Scheduler((Scheduler)) -->|schedule| Automation[Automation] + Automation <-->|read / update orders| DB[(MySQL orders_db)] + Automation -->|send status email| SMTP[SMTP server] + SMTP -->|notify| Customer((Customer)) +``` + +1. Read the `orders` table for rows whose status is still `PLACED`. +2. If nothing is waiting, log a note and stop. +3. Otherwise, for each waiting order, email the customer and update the order to `PROCESSING`. +4. Log a one-line summary of how much work the run did. + +## Prerequisites + +- MySQL Server running on `localhost:3306`, with the sample `orders_db` database seeded using [the setup script](#database-setup). +- An SMTP-enabled email account to send the customer notifications. + +## Database setup + +```sql +CREATE DATABASE IF NOT EXISTS orders_db; +CREATE USER IF NOT EXISTS 'orders_user'@'localhost' IDENTIFIED BY 'orders_pass'; +GRANT SELECT, UPDATE ON orders_db.* TO 'orders_user'@'localhost'; +FLUSH PRIVILEGES; + +USE orders_db; + +CREATE TABLE customers ( + customer_id VARCHAR(20) PRIMARY KEY, + name VARCHAR(100), + email VARCHAR(100), + address VARCHAR(255) +); + +CREATE TABLE products ( + product_id VARCHAR(20) PRIMARY KEY, + product_name VARCHAR(100), + category VARCHAR(50), + price DECIMAL(10,2) +); + +CREATE TABLE orders ( + order_id VARCHAR(20) PRIMARY KEY, + customer_id VARCHAR(20), + product_id VARCHAR(20), + amount DECIMAL(10,2), + status VARCHAR(20) DEFAULT 'PLACED', + placed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (customer_id) REFERENCES customers(customer_id), + FOREIGN KEY (product_id) REFERENCES products(product_id) +); + +INSERT INTO customers VALUES + ('CUST-001', 'Ada Lovelace', 'ada@example.com', '10 Analytical Way'), + ('CUST-002', 'Alan Turing', 'alan@example.com', '24 Enigma Street'), + ('CUST-003', 'Grace Hopper', 'grace@example.com', '7 Compiler Lane'), + ('CUST-004', 'Edsger Dijkstra','ed@example.com', '3 Shortest Path'); + +INSERT INTO products VALUES + ('PROD-001', 'Mechanical Keyboard', 'Peripherals', 79.99), + ('PROD-002', 'USB-C Hub', 'Accessories', 34.50), + ('PROD-003', 'Standing Desk', 'Furniture', 129.00), + ('PROD-004', 'Desk Lamp', 'Lighting', 49.99); + +INSERT INTO orders (order_id, customer_id, product_id, amount, status) VALUES + ('ORD-001', 'CUST-001', 'PROD-001', 79.99, 'PLACED'), + ('ORD-002', 'CUST-002', 'PROD-002', 34.50, 'PLACED'), + ('ORD-003', 'CUST-003', 'PROD-003', 129.00, 'PROCESSING'), + ('ORD-004', 'CUST-004', 'PROD-004', 49.99, 'PROCESSING'); +``` + +## Configuration + +Provide the database password and SMTP details in `Config.toml`: + +```toml +ordersDBPassword = "orders_pass" +emailHost = "smtp.example.com" +emailUserName = "orders@example.com" +emailPassword = "" +emailPort = 465 +``` + +## Run + +Generate the database client and run the automation: + +```bash +bal persist generate +bal run +``` + +On the first run, the two `PLACED` orders advance to `PROCESSING` and their customers are emailed. A second run finds nothing waiting and exits with `No new orders to process.` + +To reset and run again, move the orders back: + +```sql +UPDATE orders_db.orders SET status = 'PLACED' WHERE order_id IN ('ORD-001', 'ORD-002'); +``` diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/agents.bal b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/agents.bal new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/agents.bal @@ -0,0 +1 @@ + diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/automation.bal b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/automation.bal new file mode 100644 index 00000000..57c3e5c5 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/automation.bal @@ -0,0 +1,26 @@ +import orderprocessingautomation.ordersdb; + +import ballerina/log; + +public function main() returns error? { + do { + PlacedOrdersType[] placedOrders = check ordersDB->/orders.get(whereClause = `status = ${"PLACED"}`); + if placedOrders.length() == 0 { + log:printInfo("No new orders to process."); + return; + } + foreach PlacedOrdersType placedOrder in placedOrders { + ordersdb:Order updatedOrder = check ordersDB->/orders/[placedOrder.orderId].put({status: "PROCESSING"}); + check emailSmtpclient->sendMessage({ + to: placedOrder.customer.email, + subject: placedOrder.orderId + ": status update", + body: "Your order bearing id :" + placedOrder.orderId + " is now under process" + }); + log:printInfo(string `Order advanced to PROCESSING: ${updatedOrder.orderId}`); + } + log:printInfo(string `Done - processed ${placedOrders.length()} orders`); + } on fail error e { + log:printError("Error occurred", 'error = e); + return e; + } +} diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/config.bal b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/config.bal new file mode 100644 index 00000000..d177d868 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/config.bal @@ -0,0 +1,10 @@ + +configurable string ordersDBHost = "localhost"; +configurable int ordersDBPort = 3306; +configurable string ordersDBUser = "orders_user"; +configurable string ordersDBPassword = ?; +configurable string ordersDBDatabase = "orders_db"; +configurable string emailHost = "127.0.0.1"; +configurable string emailUserName = "orders@example.com"; +configurable string emailPassword = ?; +configurable int emailPort = 2525; \ No newline at end of file diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/connections.bal b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/connections.bal new file mode 100644 index 00000000..0d9549c5 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/connections.bal @@ -0,0 +1,7 @@ +import orderprocessingautomation.ordersdb; + +import ballerina/email; + +final ordersdb:Client ordersDB = check new (ordersDBHost, ordersDBPort, ordersDBUser, ordersDBPassword, ordersDBDatabase); + +final email:SmtpClient emailSmtpclient = check new (string `${emailHost}`, string `${emailUserName}`, string `${emailPassword}`, port = emailPort, security = "START_TLS_NEVER"); diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/data_mappings.bal b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/data_mappings.bal new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/data_mappings.bal @@ -0,0 +1 @@ + diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/functions.bal b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/functions.bal new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/functions.bal @@ -0,0 +1 @@ + diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/main.bal b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/main.bal new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/main.bal @@ -0,0 +1 @@ + diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/persist/ordersDB/model.bal b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/persist/ordersDB/model.bal new file mode 100644 index 00000000..d6ecad6b --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/persist/ordersDB/model.bal @@ -0,0 +1,57 @@ +import ballerina/persist as _; +import ballerina/time; +import ballerinax/persist.sql; + +@sql:Name {value: "orders"} +public type Order record {| + @sql:Name {value: "order_id"} + @sql:Varchar {length: 36} + readonly string orderId; + @sql:Name {value: "customer_id"} + @sql:Varchar {length: 36} + @sql:Index {name: "customer_id"} + string customerId; + @sql:Name {value: "product_id"} + @sql:Varchar {length: 36} + @sql:Index {name: "product_id"} + string productId; + @sql:Decimal {precision: [10, 2]} + decimal amount; + @sql:Varchar {length: 20} + string status; + @sql:Name {value: "placed_at"} + time:Civil placedAt; + @sql:Relation {keys: ["customerId"]} + Customer customer; + @sql:Relation {keys: ["productId"]} + Product product; +|}; + +@sql:Name {value: "customers"} +public type Customer record {| + @sql:Name {value: "customer_id"} + @sql:Varchar {length: 36} + readonly string customerId; + @sql:Varchar {length: 100} + string name; + @sql:Varchar {length: 100} + string email; + @sql:Varchar {length: 255} + string address; + Order[] orders; +|}; + +@sql:Name {value: "products"} +public type Product record {| + @sql:Name {value: "product_id"} + @sql:Varchar {length: 36} + readonly string productId; + @sql:Name {value: "product_name"} + @sql:Varchar {length: 100} + string productName; + @sql:Varchar {length: 50} + string category; + @sql:Decimal {precision: [10, 2]} + decimal price; + Order[] orders; +|}; diff --git a/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/types.bal b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/types.bal new file mode 100644 index 00000000..f637b6b1 --- /dev/null +++ b/integrator-default-profile/samples/order-management-automation/orderprocessingautomation/types.bal @@ -0,0 +1,26 @@ +import ballerina/time; + +public type PlacedOrdersType record {| + string orderId; + decimal amount; + string status; + time:Civil placedAt; + string customerId; + string productId; + PlacedOrdersCustomerType customer; + PlacedOrdersProductType product; +|}; + +public type PlacedOrdersCustomerType record {| + string customerId; + string name; + string email; + string address; +|}; + +public type PlacedOrdersProductType record {| + string productId; + string productName; + string category; + decimal price; +|};