diff --git a/blog_notification_app/.devcontainer.json b/blog_notification_app/.devcontainer.json new file mode 100644 index 0000000..834cba9 --- /dev/null +++ b/blog_notification_app/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.6.0-20230525-070000-e36fc7ac", + "extensions": ["WSO2.ballerina"], +} diff --git a/blog_notification_app/.gitignore b/blog_notification_app/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/blog_notification_app/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/blog_notification_app/Ballerina.toml b/blog_notification_app/Ballerina.toml new file mode 100644 index 0000000..b8f9ccc --- /dev/null +++ b/blog_notification_app/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "anjana" +name = "blog_app" +version = "0.1.0" +distribution = "2201.6.0-20230525-070000-e36fc7ac" + +[build-options] +observabilityIncluded = true diff --git a/blog_notification_app/Dependencies.toml b/blog_notification_app/Dependencies.toml new file mode 100644 index 0000000..e67bf6c --- /dev/null +++ b/blog_notification_app/Dependencies.toml @@ -0,0 +1,334 @@ +# 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.6.0-20230525-070000-e36fc7ac" + +[[package]] +org = "anjana" +name = "blog_app" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "io"}, + {org = "ballerinai", name = "observe"}, + {org = "ballerinax", name = "slack"} +] +modules = [ + {org = "anjana", packageName = "blog_app", moduleName = "blog_app"} +] + +[[package]] +org = "ballerina" +name = "auth" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.5.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.2.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "http", moduleName = "http"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[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.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[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.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.6.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "regex" +version = "1.4.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.string"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.3.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.2.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "client.config" +version = "1.0.1" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "oauth2"} +] + +[[package]] +org = "ballerinax" +name = "slack" +version = "3.3.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "regex"}, + {org = "ballerina", name = "url"}, + {org = "ballerinax", name = "client.config"} +] +modules = [ + {org = "ballerinax", packageName = "slack", moduleName = "slack"} +] + diff --git a/blog_notification_app/README.md b/blog_notification_app/README.md new file mode 100644 index 0000000..399e5bd --- /dev/null +++ b/blog_notification_app/README.md @@ -0,0 +1,40 @@ +# Blog Notification Application + +This application demonstrates how to write a simple blog backend application using Ballerina. It also sends slack notification to a slack channel when a new blog is added. + +## Prerequisites + +### Setting up Slack tokens +1. Visit https://api.slack.com/apps +2. Click `Create New App` and select the `From scratch` option. +3. Create the app by providing an `App name` and selecting the workspace on where to send the notifications. +4. In the `Add features and functionality` section, Click `Permissions`. +5. Go to the `Scopes` section and add necessary OAuth scopes in `User Token Scopes` section. (`channels:history`, `channels:read`, `channels:write`, `chat:write`, `emoji:read`, `files:read`, `files:write`, `groups:read`, `reactions:read`, `users:read`, `users:read.email`) +6. Go back to the `Basic Information` section of your Slack App. Then go to the `Install your app section` and install the app to the workspace by clicking the `Install to Workspace` button. +7. Get your User OAuth token from the `OAuth & Permissions` section of your Slack App + + +### Configuring the Slack token +1. Create a file named `Config.toml` in the blog_app directory and add the following content to it. Replace the `slackToken` with the token you got from the previous step. + +``` +slackToken = +``` + +## Running the Sample + +- Run the ballerina application `bal run` + +## Testing the Sample + + +- Create a blog +``` +curl --location 'http://localhost:9090/blog' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "title": "Sample Blog", + "content": "This is a sample blog" +}' +``` +- You should see a slack notification in the configured slack channel. diff --git a/blog_notification_app/service.bal b/blog_notification_app/service.bal new file mode 100644 index 0000000..5ecb0d9 --- /dev/null +++ b/blog_notification_app/service.bal @@ -0,0 +1,42 @@ +import ballerina/http; +import ballerinax/slack; + +configurable string slackToken = ?; + +type Blog record { + string title; + string content; +}; + +Blog[] blogs = []; + +service /blog on new http:Listener(9090) { + slack:Client slackClient; + + public function init() returns error? { + slack:ConnectionConfig slackConfig = { + auth: { + token: slackToken + } + }; + self.slackClient = check new(slackConfig); + } + + resource function get .() returns Blog[] { + return blogs; + } + + resource function post .(@http:Payload Blog blog) returns error? { + future slackFuture = start self.notifySlack(blog); + blogs.push(blog); + check wait slackFuture; + } + + function notifySlack(Blog blog) returns error? { + slack:Message messageParams = { + channelName: "Test Channel", + text: "New Blog is created : " + blog.title + }; + _ = check self.slackClient->postMessage(messageParams); + } +} \ No newline at end of file diff --git a/inventory_store_mysql/.devcontainer.json b/inventory_store_mysql/.devcontainer.json new file mode 100644 index 0000000..c4667c3 --- /dev/null +++ b/inventory_store_mysql/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.5.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/inventory_store_mysql/.gitignore b/inventory_store_mysql/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/inventory_store_mysql/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/inventory_store_mysql/Ballerina.toml b/inventory_store_mysql/Ballerina.toml new file mode 100644 index 0000000..607d68f --- /dev/null +++ b/inventory_store_mysql/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "anjana" +name = "inventory_store" +version = "0.1.0" +distribution = "2201.5.0" + +[build-options] +observabilityIncluded = true diff --git a/inventory_store_mysql/Dependencies.toml b/inventory_store_mysql/Dependencies.toml new file mode 100644 index 0000000..65e8851 --- /dev/null +++ b/inventory_store_mysql/Dependencies.toml @@ -0,0 +1,346 @@ +# 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.5.0" + +[[package]] +org = "anjana" +name = "inventory_store" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "sql"}, + {org = "ballerinai", name = "observe"}, + {org = "ballerinax", name = "mysql"}, + {org = "ballerinax", name = "mysql.driver"} +] +modules = [ + {org = "anjana", packageName = "inventory_store", moduleName = "inventory_store"} +] + +[[package]] +org = "ballerina" +name = "auth" +version = "2.7.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "regex"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.4.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.1.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.7.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "regex"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.7.0" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "regex"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "http", moduleName = "http"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.7.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "regex"}, + {org = "ballerina", name = "time"} +] + +[[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.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[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.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.7.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.7.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.7.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.6.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "regex" +version = "1.4.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.string"} +] + +[[package]] +org = "ballerina" +name = "sql" +version = "1.8.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.3.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.2.3" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "mysql" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "sql"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerinax", packageName = "mysql", moduleName = "mysql"} +] + +[[package]] +org = "ballerinax" +name = "mysql.driver" +version = "1.4.1" +modules = [ + {org = "ballerinax", packageName = "mysql.driver", moduleName = "mysql.driver"} +] + diff --git a/inventory_store_mysql/README.md b/inventory_store_mysql/README.md new file mode 100644 index 0000000..dcdef73 --- /dev/null +++ b/inventory_store_mysql/README.md @@ -0,0 +1,57 @@ +# Inventory Management with Ballerina and MySQL +This guide walks you through the process of building an inventory management system with Ballerina and MySQL. + +## Setting up MySQL Database +You can either setup a MySQL database locally, use a database hosted in cloud remotely to try this sample out. We will be using a MySQL docker image for simplicitiy. + +Run the following command to start the database +``` +docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -v "./db/init.sql:/docker-entrypoint-initdb.d/init.sql" -d mysql:latest +``` + +## Setting Configurables +In Ballerina, you can make your code configurable in different environments. These fields are marked as configurables in the code. + +```ballerina +configurable string host = ?; +configurable int port = ?; +configurable string user = ?; +configurable string password = ?; +configurable string database = ?; +``` + +We need to provide the values for these fields in `Config.toml` file. If you are using the docker setup as mentioned above, you don't have to change the values, otherwise you need to change the values accordingly. + +```toml +host="localhost" +port=3306 +user="root" +password="root" +database="storedb" +``` + +## Running the Sample +Now we can run the ballerina package using following command. + +```bash +bal run +``` + +## Testing the Sample +You can invoke the following curl command to add an item to the store. + +```bash +curl --location 'http://localhost:9090/store' \ +--header 'Content-Type: application/json' \ +--data '{ + "name" : "Item1", + "quantity" : 11 +}' + +``` + +You can invoke the following curl command to get the list of items in the store. + +```bash +curl --location 'http://localhost:9090/store' +``` \ No newline at end of file diff --git a/inventory_store_mysql/db/init.sql b/inventory_store_mysql/db/init.sql new file mode 100644 index 0000000..a2ef269 --- /dev/null +++ b/inventory_store_mysql/db/init.sql @@ -0,0 +1,10 @@ +CREATE DATABASE IF NOT EXISTS storedb; + +USE storedb; + +CREATE TABLE IF NOT EXISTS inventory ( + id INTEGER NOT NULL AUTO_INCREMENT, + name VARCHAR(300), + quantity INTEGER, + PRIMARY KEY (id) +); diff --git a/inventory_store_mysql/service.bal b/inventory_store_mysql/service.bal new file mode 100644 index 0000000..4049cfd --- /dev/null +++ b/inventory_store_mysql/service.bal @@ -0,0 +1,37 @@ +import ballerina/http; +import ballerinax/mysql; +import ballerina/sql; +import ballerinax/mysql.driver as _; + +listener http:Listener helloEP = new(9090); + +type Item record { + int id?; + string name; + int quantity; +}; + +configurable string host = ?; +configurable int port = ?; +configurable string user = ?; +configurable string password = ?; +configurable string database = ?; + +service /store on helloEP { + final mysql:Client databaseClient; + + public function init() returns error? { + self.databaseClient = check new (host = host, port = port, user = user, password = password, database = database); + } + + resource function get .() returns Item[]|error { + stream itemStream = self.databaseClient->query(`SELECT * FROM inventory`); + return from Item item in itemStream + select item; + } + resource function post .(@http:Payload Item item) returns error? { + _ = check self.databaseClient->execute(` + INSERT INTO inventory(name, quantity) + VALUES (${item.name}, ${item.quantity});`); + } +} diff --git a/order_events/README.md b/order_events/README.md new file mode 100644 index 0000000..c4e7fae --- /dev/null +++ b/order_events/README.md @@ -0,0 +1,76 @@ +# Event Driven Order Management System + +## Service and Event Composition +Upon user clicking submit button, the HTTP POST endpoint will be called in the Order Service. It will start the event chaining by pushing event to topics. This implementation follows Database per service pattern. Implementation is focused on service compositon and event chaining rather than the business logic for simplicity. + +### Event - HTTP POST /order +#### Service - Order Service +- Validate the data +- Persist Order +- Invoke orderCreatedEvent + +### Topic - orderCreatedEvent +#### Service - Payment Service +- Talk to payment gateway and process payment +- Invoke paymentCompletedEvent + +### Topic - paymentCompletedEvent +#### Service - Inventory Service +- Update inventory +- Invoke inventoryUpdatedEvent + +### Topic - inventoryUpdatedEvent +#### Service - SMS Service +- Send SMS to customer + +### Topic - inventoryUpdatedEvent +#### Service - Email Service +- Send Email to customer + +### Topic - inventoryUpdatedEvent +#### Service - Order Service +- Update the Order status in db + + +## Running the Sample + +- Start the Kafka Broker - `docker compose up -d` +- Publish types package to local central - `(cd order_types && bal pack && bal push --repository=local)` +- Start Order Service - `(cd order_service && bal run)` +- Start Inventory Service - `(cd inventory_service && bal run)` +- Start Payment Service - `(cd payment_service && bal run)` +- Start Email Service - `(cd email_service && bal run)` +- Start SMS Service - `(cd sms_service && bal run)` + + +## Testing the Sample + +### Create an order +``` +curl --location 'http://localhost:9090/order' \ +--header 'Content-Type: application/json' \ +--data-raw '{ + "customer": { + "name": "Anjana", + "email": "test@gmail.com", + "phoneNumber": "762222222" + }, + "items": [ + { + "id": "Item1", + "quantity": 2 + } + ], + "paymentDetails": { + "name": "NGAS Subashana", + "cardNumber": "1234", + "expiryDate": "02/22", + "cvv": "123" + } +}' +``` + +### Check the order status +``` +curl --location 'http://localhost:9090/order' +``` \ No newline at end of file diff --git a/order_events/docker-compose.yml b/order_events/docker-compose.yml new file mode 100644 index 0000000..cc6303e --- /dev/null +++ b/order_events/docker-compose.yml @@ -0,0 +1,27 @@ +--- +version: '3' +services: + zookeeper: + image: confluentinc/cp-zookeeper:7.3.2 + container_name: zookeeper + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + ZOOKEEPER_TICK_TIME: 2000 + + broker: + image: confluentinc/cp-kafka:7.3.2 + container_name: broker + ports: + # To learn about configuring Kafka for access across networks see + # https://www.confluent.io/blog/kafka-client-cannot-connect-to-broker-on-aws-on-docker-etc/ + - "9092:9092" + depends_on: + - zookeeper + environment: + KAFKA_BROKER_ID: 1 + KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181' + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_INTERNAL:PLAINTEXT + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://localhost:9092,PLAINTEXT_INTERNAL://broker:29092 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1 + KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1 \ No newline at end of file diff --git a/order_events/email_service/.devcontainer.json b/order_events/email_service/.devcontainer.json new file mode 100644 index 0000000..86079a3 --- /dev/null +++ b/order_events/email_service/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.6.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/order_events/email_service/.gitignore b/order_events/email_service/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/order_events/email_service/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/order_events/email_service/Ballerina.toml b/order_events/email_service/Ballerina.toml new file mode 100644 index 0000000..1f9aaa1 --- /dev/null +++ b/order_events/email_service/Ballerina.toml @@ -0,0 +1,14 @@ +[package] +org = "wso2" +name = "email_service" +version = "0.1.0" +distribution = "2201.6.0" + +[build-options] +observabilityIncluded = true + +[[dependency]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +repository = "local" \ No newline at end of file diff --git a/order_events/email_service/Dependencies.toml b/order_events/email_service/Dependencies.toml new file mode 100644 index 0000000..d9b3a75 --- /dev/null +++ b/order_events/email_service/Dependencies.toml @@ -0,0 +1,145 @@ +# 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.6.0" + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[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 = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "uuid" +version = "1.5.1" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "kafka" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "uuid"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "kafka", moduleName = "kafka"} +] + +[[package]] +org = "wso2" +name = "email_service" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerinai", name = "observe"}, + {org = "ballerinax", name = "kafka"}, + {org = "wso2", name = "orders.types"} +] +modules = [ + {org = "wso2", packageName = "email_service", moduleName = "email_service"} +] + +[[package]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +dependencies = [ + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "wso2", packageName = "orders.types", moduleName = "orders.types"} +] + diff --git a/order_events/email_service/service.bal b/order_events/email_service/service.bal new file mode 100644 index 0000000..5cb4db0 --- /dev/null +++ b/order_events/email_service/service.bal @@ -0,0 +1,24 @@ +import ballerinax/kafka; +import ballerina/io; +import wso2/orders.types; + +listener kafka:Listener orderListener = new (kafka:DEFAULT_URL, { + groupId: "email_group", + topics: "inventory_updated_topic" +}); + +service on orderListener { + private final kafka:Producer orderProducer; + + function init() returns error? { + self.orderProducer = check new (kafka:DEFAULT_URL); + } + + remote function onConsumerRecord(types:Order[] orders) returns error? { + + foreach types:Order 'order in orders { + types:Customer customer = 'order.customer; + io:println("Sending email to " + customer.email); + } + } +} diff --git a/order_events/inventory_service/.devcontainer.json b/order_events/inventory_service/.devcontainer.json new file mode 100644 index 0000000..86079a3 --- /dev/null +++ b/order_events/inventory_service/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.6.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/order_events/inventory_service/.gitignore b/order_events/inventory_service/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/order_events/inventory_service/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/order_events/inventory_service/Ballerina.toml b/order_events/inventory_service/Ballerina.toml new file mode 100644 index 0000000..4ae68e1 --- /dev/null +++ b/order_events/inventory_service/Ballerina.toml @@ -0,0 +1,14 @@ +[package] +org = "wso2" +name = "inventory_service" +version = "0.1.0" +distribution = "2201.6.0" + +[build-options] +observabilityIncluded = true + +[[dependency]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +repository = "local" \ No newline at end of file diff --git a/order_events/inventory_service/Dependencies.toml b/order_events/inventory_service/Dependencies.toml new file mode 100644 index 0000000..454bb84 --- /dev/null +++ b/order_events/inventory_service/Dependencies.toml @@ -0,0 +1,124 @@ +# 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.6.0" + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[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 = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "uuid" +version = "1.5.1" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "kafka" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "uuid"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "kafka", moduleName = "kafka"} +] + +[[package]] +org = "wso2" +name = "inventory_service" +version = "0.1.0" +dependencies = [ + {org = "ballerinai", name = "observe"}, + {org = "ballerinax", name = "kafka"}, + {org = "wso2", name = "orders.types"} +] +modules = [ + {org = "wso2", packageName = "inventory_service", moduleName = "inventory_service"} +] + +[[package]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +dependencies = [ + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "wso2", packageName = "orders.types", moduleName = "orders.types"} +] + diff --git a/order_events/inventory_service/service.bal b/order_events/inventory_service/service.bal new file mode 100644 index 0000000..5607bc2 --- /dev/null +++ b/order_events/inventory_service/service.bal @@ -0,0 +1,38 @@ +import ballerinax/kafka; +import wso2/orders.types; + +listener kafka:Listener orderListener = new (kafka:DEFAULT_URL, { + groupId: "inventory_group", + topics: "payment_completed_topic" +}); + +map inventory = { + "Item1": 10, + "Item2": 10, + "Item3": 10 +}; + +service on orderListener { + private final kafka:Producer orderProducer; + + function init() returns error? { + self.orderProducer = check new (kafka:DEFAULT_URL); + } + + remote function onConsumerRecord(types:Order[] orders) returns error? { + + foreach types:Order 'order in orders { + types:Item[] items = 'order.items; + foreach types:Item item in items { + int newQuantity = inventory.get(item.id) - item.quantity; + inventory[item.id] = newQuantity; + } + + check self.orderProducer->send({ + topic: "inventory_updated_topic", + value: 'order + }); + } + } +} + diff --git a/order_events/order_service/.devcontainer.json b/order_events/order_service/.devcontainer.json new file mode 100644 index 0000000..86079a3 --- /dev/null +++ b/order_events/order_service/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.6.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/order_events/order_service/.gitignore b/order_events/order_service/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/order_events/order_service/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/order_events/order_service/Ballerina.toml b/order_events/order_service/Ballerina.toml new file mode 100644 index 0000000..e7221f1 --- /dev/null +++ b/order_events/order_service/Ballerina.toml @@ -0,0 +1,14 @@ +[package] +org = "wso2" +name = "order_service" +version = "0.1.0" +distribution = "2201.6.0" + +[build-options] +observabilityIncluded = true + +[[dependency]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +repository = "local" \ No newline at end of file diff --git a/order_events/order_service/Dependencies.toml b/order_events/order_service/Dependencies.toml new file mode 100644 index 0000000..7d36c4c --- /dev/null +++ b/order_events/order_service/Dependencies.toml @@ -0,0 +1,335 @@ +# 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.6.0" + +[[package]] +org = "ballerina" +name = "auth" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.5.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.2.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "http", moduleName = "http"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[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.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[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.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.6.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.3.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.2.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "uuid" +version = "1.5.1" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "uuid", moduleName = "uuid"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "kafka" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "uuid"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "kafka", moduleName = "kafka"} +] + +[[package]] +org = "wso2" +name = "order_service" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "uuid"}, + {org = "ballerinai", name = "observe"}, + {org = "ballerinax", name = "kafka"}, + {org = "wso2", name = "orders.types"} +] +modules = [ + {org = "wso2", packageName = "order_service", moduleName = "order_service"} +] + +[[package]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +dependencies = [ + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "wso2", packageName = "orders.types", moduleName = "orders.types"} +] + diff --git a/order_events/order_service/service.bal b/order_events/order_service/service.bal new file mode 100644 index 0000000..e86ad4d --- /dev/null +++ b/order_events/order_service/service.bal @@ -0,0 +1,53 @@ +import ballerina/http; +import ballerina/uuid; +import ballerinax/kafka; +import wso2/orders.types; + +map ordersStore = {}; + +service / on new http:Listener(9090) { + private final kafka:Producer orderProducer; + + function init() returns error? { + self.orderProducer = check new (kafka:DEFAULT_URL); + } + + resource function post 'order(@http:Payload types:OrderRequest recievedOrder) returns error? { + string orderId = uuid:createType1AsString(); + types:Order 'order = { + ...recievedOrder, + id: orderId, + status: types:CREATED + }; + ordersStore[orderId] = 'order; + check self.orderProducer->send({ + topic: "order_created_topic", + value: 'order + }); + } + + resource function get 'order() returns types:Order[] { + return ordersStore.toArray(); + } +} + +listener kafka:Listener orderListener = new (kafka:DEFAULT_URL, { + groupId: "order_dispatch_group", + topics: "inventory_updated_topic" +}); + +service on orderListener { + private final kafka:Producer orderProducer; + + function init() returns error? { + self.orderProducer = check new (kafka:DEFAULT_URL); + } + + remote function onConsumerRecord(types:Order[] orders) returns error? { + + foreach types:Order 'order in orders { + string id = 'order.id; + ordersStore[id].status = types:DISPATCHED; + } + } +} diff --git a/order_events/order_types/.devcontainer.json b/order_events/order_types/.devcontainer.json new file mode 100644 index 0000000..86079a3 --- /dev/null +++ b/order_events/order_types/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.6.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/order_events/order_types/.gitignore b/order_events/order_types/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/order_events/order_types/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/order_events/order_types/Ballerina.toml b/order_events/order_types/Ballerina.toml new file mode 100644 index 0000000..ab9274b --- /dev/null +++ b/order_events/order_types/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "wso2" +name = "orders.types" +version = "0.1.0" +distribution = "2201.6.0" + +[build-options] +observabilityIncluded = true diff --git a/order_events/order_types/Dependencies.toml b/order_events/order_types/Dependencies.toml new file mode 100644 index 0000000..b9a0813 --- /dev/null +++ b/order_events/order_types/Dependencies.toml @@ -0,0 +1,45 @@ +# 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.6.0" + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +dependencies = [ + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "wso2", packageName = "orders.types", moduleName = "orders.types"} +] + diff --git a/order_events/order_types/Package.md b/order_events/order_types/Package.md new file mode 100644 index 0000000..1c75f01 --- /dev/null +++ b/order_events/order_types/Package.md @@ -0,0 +1 @@ +Contains the types used across the microservices. \ No newline at end of file diff --git a/order_events/order_types/types.bal b/order_events/order_types/types.bal new file mode 100644 index 0000000..d9c7c6f --- /dev/null +++ b/order_events/order_types/types.bal @@ -0,0 +1,36 @@ +public type OrderRequest record {| + Customer customer; + Item[] items; + PaymentDetails paymentDetails; +|}; + +public type Order record {| + *OrderRequest; + string id; + OrderStatus status; +|}; + +public type Customer record {| + string name; + string email; + string phoneNumber; +|}; + +public type Item record {| + string id; + int quantity; +|}; + +public enum OrderStatus { + CREATED, + PAYMENT_COMPLETED, + PAYMENT_FAILED, + DISPATCHED +}; + +public type PaymentDetails record {| + string name; + string cardNumber; + string expiryDate; + string cvv; +|}; diff --git a/order_events/payment_service/.devcontainer.json b/order_events/payment_service/.devcontainer.json new file mode 100644 index 0000000..86079a3 --- /dev/null +++ b/order_events/payment_service/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.6.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/order_events/payment_service/.gitignore b/order_events/payment_service/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/order_events/payment_service/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/order_events/payment_service/Ballerina.toml b/order_events/payment_service/Ballerina.toml new file mode 100644 index 0000000..b6c4d0b --- /dev/null +++ b/order_events/payment_service/Ballerina.toml @@ -0,0 +1,14 @@ +[package] +org = "wso2" +name = "payment_service" +version = "0.1.0" +distribution = "2201.6.0" + +[build-options] +observabilityIncluded = true + +[[dependency]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +repository = "local" \ No newline at end of file diff --git a/order_events/payment_service/Dependencies.toml b/order_events/payment_service/Dependencies.toml new file mode 100644 index 0000000..024a4e3 --- /dev/null +++ b/order_events/payment_service/Dependencies.toml @@ -0,0 +1,124 @@ +# 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.6.0" + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[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 = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "uuid" +version = "1.5.1" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "kafka" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "uuid"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "kafka", moduleName = "kafka"} +] + +[[package]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +dependencies = [ + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "wso2", packageName = "orders.types", moduleName = "orders.types"} +] + +[[package]] +org = "wso2" +name = "payment_service" +version = "0.1.0" +dependencies = [ + {org = "ballerinai", name = "observe"}, + {org = "ballerinax", name = "kafka"}, + {org = "wso2", name = "orders.types"} +] +modules = [ + {org = "wso2", packageName = "payment_service", moduleName = "payment_service"} +] + diff --git a/order_events/payment_service/service.bal b/order_events/payment_service/service.bal new file mode 100644 index 0000000..f613399 --- /dev/null +++ b/order_events/payment_service/service.bal @@ -0,0 +1,44 @@ +import ballerinax/kafka; +import wso2/orders.types; + +listener kafka:Listener orderListener = new (kafka:DEFAULT_URL, { + groupId: "payment_group", + topics: "order_created_topic" +}); + +service on orderListener { + private final kafka:Producer orderProducer; + + function init() returns error? { + self.orderProducer = check new (kafka:DEFAULT_URL); + } + + remote function onConsumerRecord(types:Order[] orders) returns error? { + + foreach types:Order 'order in orders { + types:PaymentDetails paymentDetails = 'order.paymentDetails; + if (isPaymentValid(paymentDetails)) { + processPayment(paymentDetails); + check self.orderProducer->send({ + topic: "payment_completed_topic", + value: 'order + }); + } else { + check self.orderProducer->send({ + topic: "payment_failed_topic", + value: 'order + }); + } + } + } +} + + +function isPaymentValid(types:PaymentDetails paymentDetails) returns boolean { + //TODO : Implement payment validation logic + return true; +} + +function processPayment(types:PaymentDetails paymentDetails) { + //TODO : Implement payment gateway logic +} diff --git a/order_events/sms_service/.devcontainer.json b/order_events/sms_service/.devcontainer.json new file mode 100644 index 0000000..86079a3 --- /dev/null +++ b/order_events/sms_service/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.6.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/order_events/sms_service/.gitignore b/order_events/sms_service/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/order_events/sms_service/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/order_events/sms_service/Ballerina.toml b/order_events/sms_service/Ballerina.toml new file mode 100644 index 0000000..12d7e6d --- /dev/null +++ b/order_events/sms_service/Ballerina.toml @@ -0,0 +1,14 @@ +[package] +org = "wso2" +name = "sms_service" +version = "0.1.0" +distribution = "2201.6.0" + +[build-options] +observabilityIncluded = true + +[[dependency]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +repository = "local" diff --git a/order_events/sms_service/Dependencies.toml b/order_events/sms_service/Dependencies.toml new file mode 100644 index 0000000..baa607d --- /dev/null +++ b/order_events/sms_service/Dependencies.toml @@ -0,0 +1,145 @@ +# 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.6.0" + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[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 = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "uuid" +version = "1.5.1" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + +[[package]] +org = "ballerinax" +name = "kafka" +version = "3.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "uuid"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "ballerinax", packageName = "kafka", moduleName = "kafka"} +] + +[[package]] +org = "wso2" +name = "orders.types" +version = "0.1.0" +dependencies = [ + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "wso2", packageName = "orders.types", moduleName = "orders.types"} +] + +[[package]] +org = "wso2" +name = "sms_service" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerinai", name = "observe"}, + {org = "ballerinax", name = "kafka"}, + {org = "wso2", name = "orders.types"} +] +modules = [ + {org = "wso2", packageName = "sms_service", moduleName = "sms_service"} +] + diff --git a/order_events/sms_service/service.bal b/order_events/sms_service/service.bal new file mode 100644 index 0000000..ff0ae3d --- /dev/null +++ b/order_events/sms_service/service.bal @@ -0,0 +1,25 @@ +import ballerinax/kafka; +import ballerina/io; +import wso2/orders.types; + +listener kafka:Listener orderListener = new (kafka:DEFAULT_URL, { + groupId: "sms_group", + topics: "inventory_updated_topic" +}); + +service on orderListener { + private final kafka:Producer orderProducer; + + function init() returns error? { + self.orderProducer = check new (kafka:DEFAULT_URL); + } + + remote function onConsumerRecord(types:Order[] orders) returns error? { + + foreach types:Order 'order in orders { + types:Customer customer = 'order.customer; + io:println("Sending sms to " + customer.phoneNumber); + } + } +} + diff --git a/travel_integration/README.md b/travel_integration/README.md new file mode 100644 index 0000000..caf8508 --- /dev/null +++ b/travel_integration/README.md @@ -0,0 +1,60 @@ +# Travel Agency Sample +This sample takes the use case of travel agency as an example to demonstrate how ballerina can be used to coordinate microservices to perform real world buisness operations. + +## Microservices involved +- Travel App - This is the main microservice which is exposed to the end user. This microservice is responsible for coordinating the other microservices to perform the business operation. +- Hotel Booking Service - This microservice is responsible for booking the hotel for the given location. +- Flight Reservation Service - This microservice is responsible for booking the flight for the given location. +- Car Rental Service - This microservice is responsible for booking the car for the given location. + + +## Generating Protocol Buffers +The following commands were used to generate the protocol buffers for the sample. Since the generated code is already included in the sample, you don't need to run these commands again. If you make any changes to the proto files, you can use these commands to generate the code again. + +```bash +bal grpc --mode service --input car_rent.proto --output car_rental/ +bal grpc --mode service --input flight_book.proto --output flight_res/ +bal grpc --mode service --input hotel_book.proto --output hotel_bookings/ + +bal grpc --mode client --input car_rent.proto --output travel/ +bal grpc --mode client --input flight_book.proto --output travel/ +bal grpc --mode client --input hotel_book.proto --output travel/ +``` + +## Running the sample + +```bash +bal run car_rental +bal run flight_res/ +bal run hotel_bookings/ + +bal run travel/ +``` + +## Testing the sample +You should be able to call the booking service using the following curl command. It will talk to other internal gRPC services internally and perform the booking. + +```bash +curl --location 'http://localhost:9090/booking' \ +--header 'Content-Type: application/json' \ +--data '{ + "username" : "xlight", + "hotelBooking": { + "hotelName": "Citadel", + "guestName": "Anjana Supun", + "checkinDate": "11", + "checkoutDate": "12", + "noOfRooms": 1 + }, + "passengers": [ + { + "name": "Anjana Supun", + "passportNumber": "2" + } + ], + "vehicle": { + "type": "BIKE", + "count": 1 + } +}' +``` \ No newline at end of file diff --git a/travel_integration/car_rent.proto b/travel_integration/car_rent.proto new file mode 100644 index 0000000..ca0effa --- /dev/null +++ b/travel_integration/car_rent.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package vehiclerent; + +service VehicleRentService { + rpc rentCar(VehicleRentRequest) returns (VehicleReservationResponse); +} + +message VehicleRentRequest { + VEHICLE_TYPE type = 1; + int32 count = 2; +} + +enum VEHICLE_TYPE{ + BIKE = 0; + CAR = 1; + VAN = 2; + BUS = 3; +} + +message VehicleReservationResponse { + string reservationId = 1; +} \ No newline at end of file diff --git a/travel_integration/car_rental/.devcontainer.json b/travel_integration/car_rental/.devcontainer.json new file mode 100644 index 0000000..c4667c3 --- /dev/null +++ b/travel_integration/car_rental/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.5.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/travel_integration/car_rental/.gitignore b/travel_integration/car_rental/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/travel_integration/car_rental/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/travel_integration/car_rental/Ballerina.toml b/travel_integration/car_rental/Ballerina.toml new file mode 100644 index 0000000..a9d3699 --- /dev/null +++ b/travel_integration/car_rental/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "anjana" +name = "car_rental" +version = "0.1.0" +distribution = "2201.5.0" + +[build-options] +observabilityIncluded = true diff --git a/travel_integration/car_rental/Dependencies.toml b/travel_integration/car_rental/Dependencies.toml new file mode 100644 index 0000000..54a0c49 --- /dev/null +++ b/travel_integration/car_rental/Dependencies.toml @@ -0,0 +1,282 @@ +# 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.6.0" + +[[package]] +org = "anjana" +name = "car_rental" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "grpc"}, + {org = "ballerina", name = "protobuf"}, + {org = "ballerina", name = "uuid"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "anjana", packageName = "car_rental", moduleName = "car_rental"} +] + +[[package]] +org = "ballerina" +name = "auth" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.5.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.2.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "grpc" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "protobuf"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "grpc", moduleName = "grpc"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.any"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.duration"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.struct"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.timestamp"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.wrappers"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[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.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[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.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "protobuf" +version = "1.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.any"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.duration"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.empty"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.struct"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.timestamp"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.wrappers"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.3.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.2.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "uuid" +version = "1.5.1" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "uuid", moduleName = "uuid"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + diff --git a/travel_integration/car_rental/car_rent_pb.bal b/travel_integration/car_rental/car_rent_pb.bal new file mode 100644 index 0000000..d278e7a --- /dev/null +++ b/travel_integration/car_rental/car_rent_pb.bal @@ -0,0 +1,101 @@ +import ballerina/grpc; +import ballerina/protobuf; + +public const string CAR_RENT_DESC = "0A0E6361725F72656E742E70726F746F120B76656869636C6572656E7422590A1256656869636C6552656E7452657175657374122D0A047479706518012001280E32192E76656869636C6572656E742E56454849434C455F5459504552047479706512140A05636F756E741802200128055205636F756E7422420A1A56656869636C655265736572766174696F6E526573706F6E736512240A0D7265736572766174696F6E4964180120012809520D7265736572766174696F6E49642A330A0C56454849434C455F5459504512080A0442494B45100012070A03434152100112070A0356414E100212070A03425553100332690A1256656869636C6552656E745365727669636512530A0772656E74436172121F2E76656869636C6572656E742E56656869636C6552656E74526571756573741A272E76656869636C6572656E742E56656869636C655265736572766174696F6E526573706F6E7365620670726F746F33"; + +public isolated client class VehicleRentServiceClient { + *grpc:AbstractClientEndpoint; + + private final grpc:Client grpcClient; + + public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? { + self.grpcClient = check new (url, config); + check self.grpcClient.initStub(self, CAR_RENT_DESC); + } + + isolated remote function rentCar(VehicleRentRequest|ContextVehicleRentRequest req) returns VehicleReservationResponse|grpc:Error { + map headers = {}; + VehicleRentRequest message; + if req is ContextVehicleRentRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("vehiclerent.VehicleRentService/rentCar", message, headers); + [anydata, map] [result, _] = payload; + return result; + } + + isolated remote function rentCarContext(VehicleRentRequest|ContextVehicleRentRequest req) returns ContextVehicleReservationResponse|grpc:Error { + map headers = {}; + VehicleRentRequest message; + if req is ContextVehicleRentRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("vehiclerent.VehicleRentService/rentCar", message, headers); + [anydata, map] [result, respHeaders] = payload; + return {content: result, headers: respHeaders}; + } +} + +public client class VehicleRentServiceVehicleReservationResponseCaller { + private grpc:Caller caller; + + public isolated function init(grpc:Caller caller) { + self.caller = caller; + } + + public isolated function getId() returns int { + return self.caller.getId(); + } + + isolated remote function sendVehicleReservationResponse(VehicleReservationResponse response) returns grpc:Error? { + return self.caller->send(response); + } + + isolated remote function sendContextVehicleReservationResponse(ContextVehicleReservationResponse response) returns grpc:Error? { + return self.caller->send(response); + } + + isolated remote function sendError(grpc:Error response) returns grpc:Error? { + return self.caller->sendError(response); + } + + isolated remote function complete() returns grpc:Error? { + return self.caller->complete(); + } + + public isolated function isCancelled() returns boolean { + return self.caller.isCancelled(); + } +} + +public type ContextVehicleRentRequest record {| + VehicleRentRequest content; + map headers; +|}; + +public type ContextVehicleReservationResponse record {| + VehicleReservationResponse content; + map headers; +|}; + +@protobuf:Descriptor {value: CAR_RENT_DESC} +public type VehicleRentRequest record {| + VEHICLE_TYPE 'type = BIKE; + int count = 0; +|}; + +@protobuf:Descriptor {value: CAR_RENT_DESC} +public type VehicleReservationResponse record {| + string reservationId = ""; +|}; + +public enum VEHICLE_TYPE { + BIKE, CAR, VAN, BUS +} + diff --git a/travel_integration/car_rental/vehiclerentservice_service.bal b/travel_integration/car_rental/vehiclerentservice_service.bal new file mode 100644 index 0000000..19f074b --- /dev/null +++ b/travel_integration/car_rental/vehiclerentservice_service.bal @@ -0,0 +1,17 @@ +import ballerina/grpc; +import ballerina/uuid; + +listener grpc:Listener ep = new (9091); + +@grpc:Descriptor {value: CAR_RENT_DESC} +service "VehicleRentService" on ep { + + remote function rentCar(VehicleRentRequest value) returns VehicleReservationResponse|error { + //TODO: See if Vehicles are available + //TODO: Persist to Vehicles DB + return { + reservationId: uuid:createType1AsString() + }; + } +} + diff --git a/travel_integration/flight_book.proto b/travel_integration/flight_book.proto new file mode 100644 index 0000000..98cfd0a --- /dev/null +++ b/travel_integration/flight_book.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package flightbooking; + +service FlightBookingService { + rpc bookFlight(FlightReservationRequest) returns (FlightReservationResponse); +} + +message FlightReservationRequest { + repeated Passenger passengers = 2; +} + +message FlightReservationResponse { + string reservationId = 1; +} + +message Passenger { + string name = 1; + string passportNumber = 2; +} \ No newline at end of file diff --git a/travel_integration/flight_res/.devcontainer.json b/travel_integration/flight_res/.devcontainer.json new file mode 100644 index 0000000..c4667c3 --- /dev/null +++ b/travel_integration/flight_res/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.5.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/travel_integration/flight_res/.gitignore b/travel_integration/flight_res/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/travel_integration/flight_res/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/travel_integration/flight_res/Ballerina.toml b/travel_integration/flight_res/Ballerina.toml new file mode 100644 index 0000000..bcb8273 --- /dev/null +++ b/travel_integration/flight_res/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "anjana" +name = "flight_res" +version = "0.1.0" +distribution = "2201.5.0" + +[build-options] +observabilityIncluded = true diff --git a/travel_integration/flight_res/Dependencies.toml b/travel_integration/flight_res/Dependencies.toml new file mode 100644 index 0000000..e0b977a --- /dev/null +++ b/travel_integration/flight_res/Dependencies.toml @@ -0,0 +1,282 @@ +# 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.6.0" + +[[package]] +org = "anjana" +name = "flight_res" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "grpc"}, + {org = "ballerina", name = "protobuf"}, + {org = "ballerina", name = "uuid"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "anjana", packageName = "flight_res", moduleName = "flight_res"} +] + +[[package]] +org = "ballerina" +name = "auth" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.5.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.2.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "grpc" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "protobuf"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "grpc", moduleName = "grpc"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.any"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.duration"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.struct"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.timestamp"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.wrappers"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[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.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[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.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "protobuf" +version = "1.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.any"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.duration"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.empty"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.struct"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.timestamp"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.wrappers"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.3.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.2.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "uuid" +version = "1.5.1" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "uuid", moduleName = "uuid"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + diff --git a/travel_integration/flight_res/flight_book_pb.bal b/travel_integration/flight_res/flight_book_pb.bal new file mode 100644 index 0000000..9008a24 --- /dev/null +++ b/travel_integration/flight_res/flight_book_pb.bal @@ -0,0 +1,102 @@ +import ballerina/grpc; +import ballerina/protobuf; + +public const string FLIGHT_BOOK_DESC = "0A11666C696768745F626F6F6B2E70726F746F120D666C69676874626F6F6B696E6722540A18466C696768745265736572766174696F6E5265717565737412380A0A70617373656E6765727318022003280B32182E666C69676874626F6F6B696E672E50617373656E676572520A70617373656E6765727322410A19466C696768745265736572766174696F6E526573706F6E736512240A0D7265736572766174696F6E4964180120012809520D7265736572766174696F6E496422470A0950617373656E67657212120A046E616D6518012001280952046E616D6512260A0E70617373706F72744E756D626572180220012809520E70617373706F72744E756D62657232770A14466C69676874426F6F6B696E6753657276696365125F0A0A626F6F6B466C6967687412272E666C69676874626F6F6B696E672E466C696768745265736572766174696F6E526571756573741A282E666C69676874626F6F6B696E672E466C696768745265736572766174696F6E526573706F6E7365620670726F746F33"; + +public isolated client class FlightBookingServiceClient { + *grpc:AbstractClientEndpoint; + + private final grpc:Client grpcClient; + + public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? { + self.grpcClient = check new (url, config); + check self.grpcClient.initStub(self, FLIGHT_BOOK_DESC); + } + + isolated remote function bookFlight(FlightReservationRequest|ContextFlightReservationRequest req) returns FlightReservationResponse|grpc:Error { + map headers = {}; + FlightReservationRequest message; + if req is ContextFlightReservationRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("flightbooking.FlightBookingService/bookFlight", message, headers); + [anydata, map] [result, _] = payload; + return result; + } + + isolated remote function bookFlightContext(FlightReservationRequest|ContextFlightReservationRequest req) returns ContextFlightReservationResponse|grpc:Error { + map headers = {}; + FlightReservationRequest message; + if req is ContextFlightReservationRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("flightbooking.FlightBookingService/bookFlight", message, headers); + [anydata, map] [result, respHeaders] = payload; + return {content: result, headers: respHeaders}; + } +} + +public client class FlightBookingServiceFlightReservationResponseCaller { + private grpc:Caller caller; + + public isolated function init(grpc:Caller caller) { + self.caller = caller; + } + + public isolated function getId() returns int { + return self.caller.getId(); + } + + isolated remote function sendFlightReservationResponse(FlightReservationResponse response) returns grpc:Error? { + return self.caller->send(response); + } + + isolated remote function sendContextFlightReservationResponse(ContextFlightReservationResponse response) returns grpc:Error? { + return self.caller->send(response); + } + + isolated remote function sendError(grpc:Error response) returns grpc:Error? { + return self.caller->sendError(response); + } + + isolated remote function complete() returns grpc:Error? { + return self.caller->complete(); + } + + public isolated function isCancelled() returns boolean { + return self.caller.isCancelled(); + } +} + +public type ContextFlightReservationResponse record {| + FlightReservationResponse content; + map headers; +|}; + +public type ContextFlightReservationRequest record {| + FlightReservationRequest content; + map headers; +|}; + +@protobuf:Descriptor {value: FLIGHT_BOOK_DESC} +public type FlightReservationResponse record {| + string reservationId = ""; +|}; + +@protobuf:Descriptor {value: FLIGHT_BOOK_DESC} +public type FlightReservationRequest record {| + Passenger[] passengers = []; +|}; + +@protobuf:Descriptor {value: FLIGHT_BOOK_DESC} +public type Passenger record {| + string name = ""; + string passportNumber = ""; +|}; + diff --git a/travel_integration/flight_res/flightbookingservice_service.bal b/travel_integration/flight_res/flightbookingservice_service.bal new file mode 100644 index 0000000..90fb1d2 --- /dev/null +++ b/travel_integration/flight_res/flightbookingservice_service.bal @@ -0,0 +1,17 @@ +import ballerina/grpc; +import ballerina/uuid; + +listener grpc:Listener ep = new (9092); + +@grpc:Descriptor {value: FLIGHT_BOOK_DESC} +service "FlightBookingService" on ep { + + remote function bookFlight(FlightReservationRequest value) returns FlightReservationResponse|error { + //TODO: See if Flights are available + //TODO: Persist to Flights DB + return { + reservationId: uuid:createType1AsString() + }; + } +} + diff --git a/travel_integration/hotel_book.proto b/travel_integration/hotel_book.proto new file mode 100644 index 0000000..55cc601 --- /dev/null +++ b/travel_integration/hotel_book.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package hotelbook; + +service HotelBookingService { + rpc bookHotel(HotelBookingRequest) returns (HotelReservationResponse); +} + +message HotelBookingRequest { + string hotelName = 1; + string guestName = 2; + string checkinDate = 3; + string checkoutDate = 4; + int32 noOfRooms = 5; +} + +message HotelReservationResponse { + string reservationId = 1; +} \ No newline at end of file diff --git a/travel_integration/hotel_bookings/.devcontainer.json b/travel_integration/hotel_bookings/.devcontainer.json new file mode 100644 index 0000000..c4667c3 --- /dev/null +++ b/travel_integration/hotel_bookings/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.5.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/travel_integration/hotel_bookings/.gitignore b/travel_integration/hotel_bookings/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/travel_integration/hotel_bookings/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/travel_integration/hotel_bookings/Ballerina.toml b/travel_integration/hotel_bookings/Ballerina.toml new file mode 100644 index 0000000..6e9f5e3 --- /dev/null +++ b/travel_integration/hotel_bookings/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "anjana" +name = "hotel_bookings" +version = "0.1.0" +distribution = "2201.5.0" + +[build-options] +observabilityIncluded = true diff --git a/travel_integration/hotel_bookings/Dependencies.toml b/travel_integration/hotel_bookings/Dependencies.toml new file mode 100644 index 0000000..68e03c8 --- /dev/null +++ b/travel_integration/hotel_bookings/Dependencies.toml @@ -0,0 +1,282 @@ +# 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.6.0" + +[[package]] +org = "anjana" +name = "hotel_bookings" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "grpc"}, + {org = "ballerina", name = "protobuf"}, + {org = "ballerina", name = "uuid"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "anjana", packageName = "hotel_bookings", moduleName = "hotel_bookings"} +] + +[[package]] +org = "ballerina" +name = "auth" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.5.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.2.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "grpc" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "protobuf"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "grpc", moduleName = "grpc"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.any"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.duration"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.struct"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.timestamp"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.wrappers"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[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.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[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.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "protobuf" +version = "1.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.any"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.duration"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.empty"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.struct"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.timestamp"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.wrappers"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.3.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.2.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "uuid" +version = "1.5.1" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "uuid", moduleName = "uuid"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + diff --git a/travel_integration/hotel_bookings/hotel_book_pb.bal b/travel_integration/hotel_bookings/hotel_book_pb.bal new file mode 100644 index 0000000..4c95a44 --- /dev/null +++ b/travel_integration/hotel_bookings/hotel_book_pb.bal @@ -0,0 +1,100 @@ +import ballerina/grpc; +import ballerina/protobuf; + +public const string HOTEL_BOOK_DESC = "0A10686F74656C5F626F6F6B2E70726F746F1209686F74656C626F6F6B22B5010A13486F74656C426F6F6B696E6752657175657374121C0A09686F74656C4E616D651801200128095209686F74656C4E616D65121C0A0967756573744E616D65180220012809520967756573744E616D6512200A0B636865636B696E44617465180320012809520B636865636B696E4461746512220A0C636865636B6F757444617465180420012809520C636865636B6F757444617465121C0A096E6F4F66526F6F6D7318052001280552096E6F4F66526F6F6D7322400A18486F74656C5265736572766174696F6E526573706F6E736512240A0D7265736572766174696F6E4964180120012809520D7265736572766174696F6E496432670A13486F74656C426F6F6B696E675365727669636512500A09626F6F6B486F74656C121E2E686F74656C626F6F6B2E486F74656C426F6F6B696E67526571756573741A232E686F74656C626F6F6B2E486F74656C5265736572766174696F6E526573706F6E7365620670726F746F33"; + +public isolated client class HotelBookingServiceClient { + *grpc:AbstractClientEndpoint; + + private final grpc:Client grpcClient; + + public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? { + self.grpcClient = check new (url, config); + check self.grpcClient.initStub(self, HOTEL_BOOK_DESC); + } + + isolated remote function bookHotel(HotelBookingRequest|ContextHotelBookingRequest req) returns HotelReservationResponse|grpc:Error { + map headers = {}; + HotelBookingRequest message; + if req is ContextHotelBookingRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("hotelbook.HotelBookingService/bookHotel", message, headers); + [anydata, map] [result, _] = payload; + return result; + } + + isolated remote function bookHotelContext(HotelBookingRequest|ContextHotelBookingRequest req) returns ContextHotelReservationResponse|grpc:Error { + map headers = {}; + HotelBookingRequest message; + if req is ContextHotelBookingRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("hotelbook.HotelBookingService/bookHotel", message, headers); + [anydata, map] [result, respHeaders] = payload; + return {content: result, headers: respHeaders}; + } +} + +public client class HotelBookingServiceHotelReservationResponseCaller { + private grpc:Caller caller; + + public isolated function init(grpc:Caller caller) { + self.caller = caller; + } + + public isolated function getId() returns int { + return self.caller.getId(); + } + + isolated remote function sendHotelReservationResponse(HotelReservationResponse response) returns grpc:Error? { + return self.caller->send(response); + } + + isolated remote function sendContextHotelReservationResponse(ContextHotelReservationResponse response) returns grpc:Error? { + return self.caller->send(response); + } + + isolated remote function sendError(grpc:Error response) returns grpc:Error? { + return self.caller->sendError(response); + } + + isolated remote function complete() returns grpc:Error? { + return self.caller->complete(); + } + + public isolated function isCancelled() returns boolean { + return self.caller.isCancelled(); + } +} + +public type ContextHotelReservationResponse record {| + HotelReservationResponse content; + map headers; +|}; + +public type ContextHotelBookingRequest record {| + HotelBookingRequest content; + map headers; +|}; + +@protobuf:Descriptor {value: HOTEL_BOOK_DESC} +public type HotelReservationResponse record {| + string reservationId = ""; +|}; + +@protobuf:Descriptor {value: HOTEL_BOOK_DESC} +public type HotelBookingRequest record {| + string hotelName = ""; + string guestName = ""; + string checkinDate = ""; + string checkoutDate = ""; + int noOfRooms = 0; +|}; + diff --git a/travel_integration/hotel_bookings/hotelbookingservice_service.bal b/travel_integration/hotel_bookings/hotelbookingservice_service.bal new file mode 100644 index 0000000..6cf3046 --- /dev/null +++ b/travel_integration/hotel_bookings/hotelbookingservice_service.bal @@ -0,0 +1,17 @@ +import ballerina/grpc; +import ballerina/uuid; + +listener grpc:Listener ep = new (9093); + +@grpc:Descriptor {value: HOTEL_BOOK_DESC} +service "HotelBookingService" on ep { + + remote function bookHotel(HotelBookingRequest value) returns HotelReservationResponse|error { + //TODO: See if hotel is available + //TODO: Persist to Hotel DB + return { + reservationId: uuid:createType1AsString() + }; + } +} + diff --git a/travel_integration/travel/.devcontainer.json b/travel_integration/travel/.devcontainer.json new file mode 100644 index 0000000..c4667c3 --- /dev/null +++ b/travel_integration/travel/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.5.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/travel_integration/travel/.gitignore b/travel_integration/travel/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/travel_integration/travel/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/travel_integration/travel/Ballerina.toml b/travel_integration/travel/Ballerina.toml new file mode 100644 index 0000000..2e81acf --- /dev/null +++ b/travel_integration/travel/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "anjana" +name = "travel_integration" +version = "0.1.0" +distribution = "2201.5.0" + +[build-options] +observabilityIncluded = true diff --git a/travel_integration/travel/Dependencies.toml b/travel_integration/travel/Dependencies.toml new file mode 100644 index 0000000..015cc05 --- /dev/null +++ b/travel_integration/travel/Dependencies.toml @@ -0,0 +1,337 @@ +# 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.6.0" + +[[package]] +org = "anjana" +name = "travel_integration" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "grpc"}, + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "protobuf"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "anjana", packageName = "travel_integration", moduleName = "travel_integration"} +] + +[[package]] +org = "ballerina" +name = "auth" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.5.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.2.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "grpc" +version = "1.8.0" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "protobuf"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "grpc", moduleName = "grpc"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.any"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.duration"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.struct"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.timestamp"}, + {org = "ballerina", packageName = "grpc", moduleName = "grpc.types.wrappers"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "http", moduleName = "http"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[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.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[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.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.6.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "protobuf" +version = "1.4.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] +modules = [ + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.any"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.duration"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.empty"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.struct"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.timestamp"}, + {org = "ballerina", packageName = "protobuf", moduleName = "protobuf.types.wrappers"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.3.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.2.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + diff --git a/travel_integration/travel/car_rent_pb.bal b/travel_integration/travel/car_rent_pb.bal new file mode 100644 index 0000000..718a1a1 --- /dev/null +++ b/travel_integration/travel/car_rent_pb.bal @@ -0,0 +1,69 @@ +import ballerina/grpc; +import ballerina/protobuf; + +public const string CAR_RENT_DESC = "0A0E6361725F72656E742E70726F746F120B76656869636C6572656E7422590A1256656869636C6552656E7452657175657374122D0A047479706518012001280E32192E76656869636C6572656E742E56454849434C455F5459504552047479706512140A05636F756E741802200128055205636F756E7422420A1A56656869636C655265736572766174696F6E526573706F6E736512240A0D7265736572766174696F6E4964180120012809520D7265736572766174696F6E49642A330A0C56454849434C455F5459504512080A0442494B45100012070A03434152100112070A0356414E100212070A03425553100332690A1256656869636C6552656E745365727669636512530A0772656E74436172121F2E76656869636C6572656E742E56656869636C6552656E74526571756573741A272E76656869636C6572656E742E56656869636C655265736572766174696F6E526573706F6E7365620670726F746F33"; + +public isolated client class VehicleRentServiceClient { + *grpc:AbstractClientEndpoint; + + private final grpc:Client grpcClient; + + public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? { + self.grpcClient = check new (url, config); + check self.grpcClient.initStub(self, CAR_RENT_DESC); + } + + isolated remote function rentCar(VehicleRentRequest|ContextVehicleRentRequest req) returns VehicleReservationResponse|grpc:Error { + map headers = {}; + VehicleRentRequest message; + if req is ContextVehicleRentRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("vehiclerent.VehicleRentService/rentCar", message, headers); + [anydata, map] [result, _] = payload; + return result; + } + + isolated remote function rentCarContext(VehicleRentRequest|ContextVehicleRentRequest req) returns ContextVehicleReservationResponse|grpc:Error { + map headers = {}; + VehicleRentRequest message; + if req is ContextVehicleRentRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("vehiclerent.VehicleRentService/rentCar", message, headers); + [anydata, map] [result, respHeaders] = payload; + return {content: result, headers: respHeaders}; + } +} + +public type ContextVehicleRentRequest record {| + VehicleRentRequest content; + map headers; +|}; + +public type ContextVehicleReservationResponse record {| + VehicleReservationResponse content; + map headers; +|}; + +@protobuf:Descriptor {value: CAR_RENT_DESC} +public type VehicleRentRequest record {| + VEHICLE_TYPE 'type = BIKE; + int count = 0; +|}; + +@protobuf:Descriptor {value: CAR_RENT_DESC} +public type VehicleReservationResponse record {| + string reservationId = ""; +|}; + +public enum VEHICLE_TYPE { + BIKE, CAR, VAN, BUS +} + diff --git a/travel_integration/travel/flight_book_pb.bal b/travel_integration/travel/flight_book_pb.bal new file mode 100644 index 0000000..f3f6fec --- /dev/null +++ b/travel_integration/travel/flight_book_pb.bal @@ -0,0 +1,70 @@ +import ballerina/grpc; +import ballerina/protobuf; + +public const string FLIGHT_BOOK_DESC = "0A11666C696768745F626F6F6B2E70726F746F120D666C69676874626F6F6B696E6722540A18466C696768745265736572766174696F6E5265717565737412380A0A70617373656E6765727318022003280B32182E666C69676874626F6F6B696E672E50617373656E676572520A70617373656E6765727322410A19466C696768745265736572766174696F6E526573706F6E736512240A0D7265736572766174696F6E4964180120012809520D7265736572766174696F6E496422470A0950617373656E67657212120A046E616D6518012001280952046E616D6512260A0E70617373706F72744E756D626572180220012809520E70617373706F72744E756D62657232770A14466C69676874426F6F6B696E6753657276696365125F0A0A626F6F6B466C6967687412272E666C69676874626F6F6B696E672E466C696768745265736572766174696F6E526571756573741A282E666C69676874626F6F6B696E672E466C696768745265736572766174696F6E526573706F6E7365620670726F746F33"; + +public isolated client class FlightBookingServiceClient { + *grpc:AbstractClientEndpoint; + + private final grpc:Client grpcClient; + + public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? { + self.grpcClient = check new (url, config); + check self.grpcClient.initStub(self, FLIGHT_BOOK_DESC); + } + + isolated remote function bookFlight(FlightReservationRequest|ContextFlightReservationRequest req) returns FlightReservationResponse|grpc:Error { + map headers = {}; + FlightReservationRequest message; + if req is ContextFlightReservationRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("flightbooking.FlightBookingService/bookFlight", message, headers); + [anydata, map] [result, _] = payload; + return result; + } + + isolated remote function bookFlightContext(FlightReservationRequest|ContextFlightReservationRequest req) returns ContextFlightReservationResponse|grpc:Error { + map headers = {}; + FlightReservationRequest message; + if req is ContextFlightReservationRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("flightbooking.FlightBookingService/bookFlight", message, headers); + [anydata, map] [result, respHeaders] = payload; + return {content: result, headers: respHeaders}; + } +} + +public type ContextFlightReservationResponse record {| + FlightReservationResponse content; + map headers; +|}; + +public type ContextFlightReservationRequest record {| + FlightReservationRequest content; + map headers; +|}; + +@protobuf:Descriptor {value: FLIGHT_BOOK_DESC} +public type FlightReservationResponse record {| + string reservationId = ""; +|}; + +@protobuf:Descriptor {value: FLIGHT_BOOK_DESC} +public type FlightReservationRequest record {| + Passenger[] passengers = []; +|}; + +@protobuf:Descriptor {value: FLIGHT_BOOK_DESC} +public type Passenger record {| + string name = ""; + string passportNumber = ""; +|}; + diff --git a/travel_integration/travel/hotel_book_pb.bal b/travel_integration/travel/hotel_book_pb.bal new file mode 100644 index 0000000..06919d3 --- /dev/null +++ b/travel_integration/travel/hotel_book_pb.bal @@ -0,0 +1,68 @@ +import ballerina/grpc; +import ballerina/protobuf; + +public const string HOTEL_BOOK_DESC = "0A10686F74656C5F626F6F6B2E70726F746F1209686F74656C626F6F6B22B5010A13486F74656C426F6F6B696E6752657175657374121C0A09686F74656C4E616D651801200128095209686F74656C4E616D65121C0A0967756573744E616D65180220012809520967756573744E616D6512200A0B636865636B696E44617465180320012809520B636865636B696E4461746512220A0C636865636B6F757444617465180420012809520C636865636B6F757444617465121C0A096E6F4F66526F6F6D7318052001280552096E6F4F66526F6F6D7322400A18486F74656C5265736572766174696F6E526573706F6E736512240A0D7265736572766174696F6E4964180120012809520D7265736572766174696F6E496432670A13486F74656C426F6F6B696E675365727669636512500A09626F6F6B486F74656C121E2E686F74656C626F6F6B2E486F74656C426F6F6B696E67526571756573741A232E686F74656C626F6F6B2E486F74656C5265736572766174696F6E526573706F6E7365620670726F746F33"; + +public isolated client class HotelBookingServiceClient { + *grpc:AbstractClientEndpoint; + + private final grpc:Client grpcClient; + + public isolated function init(string url, *grpc:ClientConfiguration config) returns grpc:Error? { + self.grpcClient = check new (url, config); + check self.grpcClient.initStub(self, HOTEL_BOOK_DESC); + } + + isolated remote function bookHotel(HotelBookingRequest|ContextHotelBookingRequest req) returns HotelReservationResponse|grpc:Error { + map headers = {}; + HotelBookingRequest message; + if req is ContextHotelBookingRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("hotelbook.HotelBookingService/bookHotel", message, headers); + [anydata, map] [result, _] = payload; + return result; + } + + isolated remote function bookHotelContext(HotelBookingRequest|ContextHotelBookingRequest req) returns ContextHotelReservationResponse|grpc:Error { + map headers = {}; + HotelBookingRequest message; + if req is ContextHotelBookingRequest { + message = req.content; + headers = req.headers; + } else { + message = req; + } + var payload = check self.grpcClient->executeSimpleRPC("hotelbook.HotelBookingService/bookHotel", message, headers); + [anydata, map] [result, respHeaders] = payload; + return {content: result, headers: respHeaders}; + } +} + +public type ContextHotelReservationResponse record {| + HotelReservationResponse content; + map headers; +|}; + +public type ContextHotelBookingRequest record {| + HotelBookingRequest content; + map headers; +|}; + +@protobuf:Descriptor {value: HOTEL_BOOK_DESC} +public type HotelReservationResponse record {| + string reservationId = ""; +|}; + +@protobuf:Descriptor {value: HOTEL_BOOK_DESC} +public type HotelBookingRequest record {| + string hotelName = ""; + string guestName = ""; + string checkinDate = ""; + string checkoutDate = ""; + int noOfRooms = 0; +|}; + diff --git a/travel_integration/travel/service.bal b/travel_integration/travel/service.bal new file mode 100644 index 0000000..44799d6 --- /dev/null +++ b/travel_integration/travel/service.bal @@ -0,0 +1,44 @@ +import ballerina/http; + +final VehicleRentServiceClient carEp = check new ("http://localhost:9091"); +final FlightBookingServiceClient flightEp = check new ("http://localhost:9092"); +final HotelBookingServiceClient hotelEp = check new ("http://localhost:9093"); + +type VehicleReservation record { + VEHICLE_TYPE 'type; + int count; +}; + +type Booking record { + string username; + Passenger[] passengers; + VehicleReservation vehicle; + HotelBookingRequest hotelBooking; +}; + +type BookingResponse record { + string username; + string vehicleResId; + string flightResId; + string hotelResId; +}; + +service / on new http:Listener(9090) { + resource function post booking(@http:Payload Booking booking) returns BookingResponse|error { + + VehicleRentRequest rentCarRequest = {'type: booking.vehicle.'type, count: booking.vehicle.count}; + VehicleReservationResponse rentCarResponse = check carEp->rentCar(rentCarRequest); + + FlightReservationRequest bookFlightRequest = {passengers: booking.passengers}; + FlightReservationResponse bookFlightResponse = check flightEp->bookFlight(bookFlightRequest); + + HotelReservationResponse bookHotelResponse = check hotelEp->bookHotel(booking.hotelBooking); + + return { + flightResId: bookFlightResponse.reservationId, + hotelResId: bookHotelResponse.reservationId, + vehicleResId: rentCarResponse.reservationId, + username: booking.username + }; + } +} diff --git a/weather_forecast_api/.devcontainer.json b/weather_forecast_api/.devcontainer.json new file mode 100644 index 0000000..c4667c3 --- /dev/null +++ b/weather_forecast_api/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.5.0", + "extensions": ["WSO2.ballerina"], +} diff --git a/weather_forecast_api/.gitignore b/weather_forecast_api/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/weather_forecast_api/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/weather_forecast_api/Ballerina.toml b/weather_forecast_api/Ballerina.toml new file mode 100644 index 0000000..40581ac --- /dev/null +++ b/weather_forecast_api/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "anjana" +name = "weather_forecast" +version = "0.1.0" +distribution = "2201.5.0" + +[build-options] +observabilityIncluded = true diff --git a/weather_forecast_api/Dependencies.toml b/weather_forecast_api/Dependencies.toml new file mode 100644 index 0000000..50fdc9b --- /dev/null +++ b/weather_forecast_api/Dependencies.toml @@ -0,0 +1,297 @@ +# 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.6.0" + +[[package]] +org = "anjana" +name = "weather_forecast" +version = "0.1.0" +dependencies = [ + {org = "ballerina", name = "http"}, + {org = "ballerina", name = "io"}, + {org = "ballerinai", name = "observe"} +] +modules = [ + {org = "anjana", packageName = "weather_forecast", moduleName = "weather_forecast"} +] + +[[package]] +org = "ballerina" +name = "auth" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"} +] + +[[package]] +org = "ballerina" +name = "cache" +version = "3.5.0" +dependencies = [ + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "task"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "constraint" +version = "1.2.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "crypto" +version = "2.3.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "file" +version = "1.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "os"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "http" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "auth"}, + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "constraint"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "file"}, + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "jwt"}, + {org = "ballerina", name = "lang.array"}, + {org = "ballerina", name = "lang.decimal"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.regexp"}, + {org = "ballerina", name = "lang.runtime"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "mime"}, + {org = "ballerina", name = "oauth2"}, + {org = "ballerina", name = "observe"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] +modules = [ + {org = "ballerina", packageName = "http", moduleName = "http"} +] + +[[package]] +org = "ballerina" +name = "io" +version = "1.4.1" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"} +] +modules = [ + {org = "ballerina", packageName = "io", moduleName = "io"} +] + +[[package]] +org = "ballerina" +name = "jballerina.java" +version = "0.0.0" + +[[package]] +org = "ballerina" +name = "jwt" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"}, + {org = "ballerina", name = "lang.string"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"} +] + +[[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.array" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.__internal"} +] + +[[package]] +org = "ballerina" +name = "lang.decimal" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[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.regexp" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.runtime" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "lang.string" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.regexp"} +] + +[[package]] +org = "ballerina" +name = "lang.value" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "log" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.value"}, + {org = "ballerina", name = "observe"} +] + +[[package]] +org = "ballerina" +name = "mime" +version = "2.7.1" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "lang.int"} +] + +[[package]] +org = "ballerina" +name = "oauth2" +version = "2.8.0" +dependencies = [ + {org = "ballerina", name = "cache"}, + {org = "ballerina", name = "crypto"}, + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "log"}, + {org = "ballerina", name = "time"}, + {org = "ballerina", name = "url"} +] + +[[package]] +org = "ballerina" +name = "observe" +version = "1.0.7" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "os" +version = "1.6.0" +dependencies = [ + {org = "ballerina", name = "io"}, + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "task" +version = "2.3.2" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "time"} +] + +[[package]] +org = "ballerina" +name = "time" +version = "2.2.5" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerina" +name = "url" +version = "2.2.4" +dependencies = [ + {org = "ballerina", name = "jballerina.java"} +] + +[[package]] +org = "ballerinai" +name = "observe" +version = "0.0.0" +dependencies = [ + {org = "ballerina", name = "jballerina.java"}, + {org = "ballerina", name = "observe"} +] +modules = [ + {org = "ballerinai", packageName = "observe", moduleName = "observe"} +] + diff --git a/weather_forecast_api/README.md b/weather_forecast_api/README.md new file mode 100644 index 0000000..b958ac3 --- /dev/null +++ b/weather_forecast_api/README.md @@ -0,0 +1,17 @@ +# Weather Forecast with Ballerina +This sample demonstrates how to use Ballerina to talk to an external API and transform the response to a different format to suit the use case. We'll be using the [weatherapi](https://www.weatherapi.com/) to get the weather forecast for a given location. + +## Obtaining the API Key +* Weather API Key - You can obtain a free API key from [weatherapi](https://www.weatherapi.com/my/) + +## Configuring the API Key +* Create a file named `Config.toml` in the root directory of the project and add the following content. +```toml +apiKey = "xxx" +``` + +## Running the Sample +Execute the following command to run this sample. +```bash +bal run +``` \ No newline at end of file diff --git a/weather_forecast_api/main.bal b/weather_forecast_api/main.bal new file mode 100644 index 0000000..e97f414 --- /dev/null +++ b/weather_forecast_api/main.bal @@ -0,0 +1,33 @@ +import ballerina/io; +import ballerina/http; + + +type Prediction record { + float minTemp; + float maxTemp; + string condition; + int chanceOfRain; +}; + +configurable string apiKey = ?; +public function main() returns error? { + string city = "Colombo"; + + http:Client weatherClient = check new ("http://api.weatherapi.com/v1"); + WeatherResponse res = check weatherClient->/forecast\.json(key = apiKey, q = city, days = 2, aqi = "no", alerts = "no"); + + DayConditions forecast = res.forecast.forecastday[1].day; + + Prediction prediction = transform(forecast); + io:println(prediction); +} + + +function transform(DayConditions day) returns Prediction { + return { + chanceOfRain: day.daily_chance_of_rain, + condition: day.condition.text, + maxTemp: day.maxtemp_c, + minTemp: day.mintemp_c + }; +} \ No newline at end of file diff --git a/weather_forecast_api/types.bal b/weather_forecast_api/types.bal new file mode 100644 index 0000000..fdb375e --- /dev/null +++ b/weather_forecast_api/types.bal @@ -0,0 +1,25 @@ + +type WeatherResponse record { + ForecastList forecast; +}; + +type ForecastList record { + Forecast[] forecastday; +}; + +type Forecast record { + string date; + DayConditions day; +}; + +type DayConditions record { + Condition condition; + float mintemp_c; + float maxtemp_c; + int daily_chance_of_rain; +}; + + +type Condition record { + string text; +}; diff --git a/xml_file_routing/.devcontainer.json b/xml_file_routing/.devcontainer.json new file mode 100644 index 0000000..834cba9 --- /dev/null +++ b/xml_file_routing/.devcontainer.json @@ -0,0 +1,4 @@ +{ + "image": "ballerina/ballerina-devcontainer:2201.6.0-20230525-070000-e36fc7ac", + "extensions": ["WSO2.ballerina"], +} diff --git a/xml_file_routing/.gitignore b/xml_file_routing/.gitignore new file mode 100644 index 0000000..7512ebe --- /dev/null +++ b/xml_file_routing/.gitignore @@ -0,0 +1,3 @@ +target +generated +Config.toml diff --git a/xml_file_routing/Ballerina.toml b/xml_file_routing/Ballerina.toml new file mode 100644 index 0000000..a0f6896 --- /dev/null +++ b/xml_file_routing/Ballerina.toml @@ -0,0 +1,8 @@ +[package] +org = "anjana" +name = "xml_file_router" +version = "0.1.0" +distribution = "2201.5.0" + +[build-options] +observabilityIncluded = true diff --git a/xml_file_routing/data/message1.xml b/xml_file_routing/data/message1.xml new file mode 100644 index 0000000..f215844 --- /dev/null +++ b/xml_file_routing/data/message1.xml @@ -0,0 +1,6 @@ + + + James + Strachan + London + \ No newline at end of file diff --git a/xml_file_routing/data/message2.xml b/xml_file_routing/data/message2.xml new file mode 100644 index 0000000..5caa192 --- /dev/null +++ b/xml_file_routing/data/message2.xml @@ -0,0 +1,6 @@ + + + Hiram + Chirino + Tampa + \ No newline at end of file diff --git a/xml_file_routing/main.bal b/xml_file_routing/main.bal new file mode 100644 index 0000000..da82b43 --- /dev/null +++ b/xml_file_routing/main.bal @@ -0,0 +1,21 @@ +import ballerina/file; +import ballerina/io; +import ballerina/log; + +public function main() returns error? { + file:MetaData[] inputDirectory = check file:readDir("data"); + string outputDirectory = "target/messages/"; + foreach file:MetaData readDirResult in inputDirectory { + string xmlFilePath = readDirResult.absPath; + xml message = check io:fileReadXml(xmlFilePath); + string city = (message/**//**/).data(); + string fileName = check file:basename(xmlFilePath); + if city == "London" { + log:printInfo("UK message"); + check io:fileWriteXml(outputDirectory + "uk/" + fileName, message); + } else { + log:printInfo("Other message"); + check io:fileWriteXml(outputDirectory + "others/" + fileName, message); + } + } +}