-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Springboot vs Ballerina comparison sample code #29
Open
ayeshLK
wants to merge
24
commits into
ballerina-guides:main
Choose a base branch
from
ayeshLK:springboot-v-ballerina
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
5d267ab
Add initial structure for ballerina rest-social-media service
ayeshLK 8a2b674
Add spring-boot social-media service
ayeshLK b1e5629
Disable tests for time-being
ayeshLK eda8dd0
Add spring-boot-reactive social-media service
ayeshLK 6f4cbaf
Update the social-media service configurations
ayeshLK e2fc083
Add db-init scripts
ayeshLK 462c9b6
Add the readme file
ayeshLK 4861e60
Add relevant docker compose files
ayeshLK 14788d2
Add relevant .http files for VSCode REST client
ayeshLK d52aff6
Refactor the MySQL init script
ayeshLK 05dbbdd
Refactor the DB entity
ayeshLK 4e2d9c8
Add missing image
ayeshLK 066942e
Add missing config annotation
ayeshLK 090c45c
Update docker compose configurations
ayeshLK b3352a4
Add retry config
ayeshLK 81b3010
Fix configurations
ayeshLK 3fdaba9
Fix controller method mapping
ayeshLK 745a0ae
Update VSCode REST client configs
ayeshLK 2f758f7
Update SQL queries
ayeshLK 1564c07
Refactor SQL queries
ayeshLK 025a652
Merge remote-tracking branch 'upstream/main' into springboot-v-ballerina
ayeshLK 8711761
Incorporate review suggestions
ayeshLK b9b3316
Refactor response-error interceptor
ayeshLK d5a67e3
Update social-media service type
ayeshLK File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Springboot and Ballerina | ||
|
||
A sample code base which touches key features of each technology. The sample is based on a simple API written for a social-media site which has users and associated posts. Following is the high level component diagram. | ||
|
||
<img src="springboot-and-ballerina.png" alt="drawing" width='500'/> | ||
|
||
Following are the features used for the implementation | ||
|
||
1. Configuring verbs and URLs | ||
2. Error handlers for sending customized error messages | ||
3. Adding constraints/validations | ||
4. OpenAPI specification for Generating API docs | ||
5. Accessing database | ||
6. Configurability | ||
7. HTTP client | ||
8. Resiliency - Retry | ||
9. Docker image generation | ||
|
||
# Setting up each environment | ||
|
||
## Spring boot | ||
Run the `springboot-docker-compose.yml` docker compose setup. | ||
```sh | ||
docker compose -f springboot-docker-compose.yml up | ||
``` | ||
|
||
## Spring boot (Reactive) | ||
Run the `springboot-reactive-docker-compose.yml` docker compose setup. | ||
```sh | ||
docker compose -f springboot-reactive-docker-compose.yml up | ||
``` | ||
|
||
## Ballerina | ||
Run the `ballerina-docker-compose.yml` docker compose setup. | ||
```sh | ||
docker compose -f ballerina-docker-compose.yml up | ||
``` | ||
|
||
# Try out | ||
## Spring boot (Default and Reactive) | ||
- To send request open `springboot-social-media.http` file using VS Code with `REST Client` extension | ||
|
||
## Ballerina | ||
- To send request open `ballerina-social-media.http` file using VS Code with `REST Client` extension | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
version: '2.14.0' | ||
|
||
services: | ||
social-media: | ||
image: 'integrationsamples/ballerina-social-media:0.0.1' | ||
ports: | ||
- '9090:9090' | ||
depends_on: | ||
sentiment-analysis: | ||
condition: service_started | ||
mysql: | ||
condition: service_healthy | ||
network_mode: "host" | ||
|
||
sentiment-analysis: | ||
image: 'shafreen/ballerina-sentiment-api:0.0.1' | ||
ports: | ||
- '9099:9099' | ||
network_mode: "host" | ||
|
||
mysql: | ||
image: 'mysql:8-oracle' | ||
ports: | ||
- '3306:3306' | ||
network_mode: "host" | ||
environment: | ||
- MYSQL_ROOT_PASSWORD=dummypassword | ||
- MYSQL_DATABASE=social_media_database | ||
- MYSQL_USER=social_media_user | ||
- MYSQL_PASSWORD=dummypassword | ||
healthcheck: | ||
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] | ||
timeout: 20s | ||
retries: 10 | ||
volumes: | ||
- "./resources/db/init.sql:/docker-entrypoint-initdb.d/1.sql" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
### Creat a user | ||
POST http://localhost:9090/social-media/users | ||
content-type: application/json | ||
|
||
{ | ||
"birthDate": { | ||
"year": 1987, | ||
"month": 02, | ||
"day": 06 | ||
}, | ||
"name": "Rimas" | ||
} | ||
|
||
### Get users | ||
GET http://localhost:9090/social-media/users | ||
|
||
### Get a specific user | ||
GET http://localhost:9090/social-media/users/1 | ||
|
||
### Get posts | ||
GET http://localhost:9090/social-media/users/3/posts | ||
|
||
### Create a post | ||
POST http://localhost:9090/social-media/users/3/posts | ||
content-type: application/json | ||
|
||
{ | ||
"description": "I wang to learn GCP" | ||
} | ||
|
||
### Delete a user | ||
DELETE http://localhost:9090/social-media/users/1 | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[package] | ||
org = "integration_samples" | ||
name = "ballerina_social_media" | ||
version = "0.0.1" | ||
distribution = "2201.7.0" | ||
|
||
[build-options] | ||
observabilityIncluded = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[container.image] | ||
repository="integrationsamples" | ||
name="ballerina-social-media" | ||
tag="0.0.1" | ||
|
||
[[container.copy.files]] | ||
sourceFile="./Config.toml" | ||
target="./Config.toml" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
moderate = true | ||
|
||
[databaseConfig] | ||
host = "localhost" | ||
port = 3306 | ||
user = "social_media_user" | ||
password = "dummypassword" | ||
database = "social_media_database" |
49 changes: 49 additions & 0 deletions
49
rest-social-media/ballerina/response_error_interceptor.bal
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. | ||
// | ||
// WSO2 LLC. licenses this file to you under the Apache License, | ||
// Version 2.0 (the "License"); you may not use this file except | ||
// in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
import ballerina/http; | ||
|
||
// Handle listener errors | ||
service class ResponseErrorInterceptor { | ||
*http:ResponseErrorInterceptor; | ||
|
||
remote function interceptResponseError(http:Request req, error err) | ||
returns SocialMediaBadReqeust|SocialMediaServerError { | ||
ErrorDetails errorDetails = buildErrorPayload(err.message(), req.rawPath); | ||
|
||
if err is http:PayloadValidationError { | ||
SocialMediaBadReqeust socialMediaBadRequest = { | ||
body: errorDetails | ||
}; | ||
return socialMediaBadRequest; | ||
} else { | ||
SocialMediaServerError socialMediaServerError = { | ||
body: errorDetails | ||
}; | ||
return socialMediaServerError; | ||
} | ||
} | ||
} | ||
|
||
type SocialMediaBadReqeust record {| | ||
*http:BadRequest; | ||
ErrorDetails body; | ||
|}; | ||
|
||
type SocialMediaServerError record {| | ||
*http:InternalServerError; | ||
ErrorDetails body; | ||
|}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. | ||
// | ||
// WSO2 LLC. licenses this file to you under the Apache License, | ||
// Version 2.0 (the "License"); you may not use this file except | ||
// in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
type Probability record { | ||
decimal neg; | ||
decimal neutral; | ||
decimal pos; | ||
}; | ||
|
||
type Sentiment record { | ||
Probability probability; | ||
string label; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
// Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. | ||
// | ||
// WSO2 LLC. licenses this file to you under the Apache License, | ||
// Version 2.0 (the "License"); you may not use this file except | ||
// in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, | ||
// software distributed under the License is distributed on an | ||
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
// KIND, either express or implied. See the License for the | ||
// specific language governing permissions and limitations | ||
// under the License. | ||
|
||
import ballerina/http; | ||
import ballerina/sql; | ||
import ballerina/mime; | ||
import ballerinax/mysql.driver as _; | ||
import ballerinax/mysql; | ||
import ballerina/log; | ||
import ballerina/time; | ||
|
||
configurable boolean moderate = ?; | ||
|
||
type DataBaseConfig record {| | ||
string host; | ||
int port; | ||
string user; | ||
string password; | ||
string database; | ||
|}; | ||
configurable DataBaseConfig databaseConfig = ?; | ||
|
||
listener http:Listener socialMediaListener = new (9090); | ||
|
||
service SocialMedia /social\-media on socialMediaListener { | ||
|
||
final mysql:Client socialMediaDb; | ||
final http:Client sentimentEndpoint; | ||
|
||
public function init() returns error? { | ||
self.socialMediaDb = check new (...databaseConfig); | ||
self.sentimentEndpoint = check new("localhost:9099", | ||
retryConfig = { | ||
interval: 3 | ||
} | ||
); | ||
log:printInfo("Social media service started"); | ||
ayeshLK marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
// Service-level error interceptors can handle errors occurred during the service execution. | ||
public function createInterceptors() returns ResponseErrorInterceptor { | ||
return new ResponseErrorInterceptor(); | ||
} | ||
|
||
# Get all the users | ||
# | ||
# + return - The list of users or error message | ||
resource function get users() returns User[]|error { | ||
stream<User, sql:Error?> userStream = self.socialMediaDb->query(`SELECT * FROM social_media_database.user`); | ||
return from User user in userStream | ||
select user; | ||
} | ||
|
||
# Get a specific user | ||
# | ||
# + id - The user ID of the user to be retrived | ||
# + return - A specific user or error message | ||
resource function get users/[int id]() returns User|UserNotFound|error { | ||
User|error result = self.socialMediaDb->queryRow(`SELECT * FROM social_media_database.user WHERE ID = ${id}`); | ||
if result is sql:NoRowsError { | ||
ErrorDetails errorDetails = buildErrorPayload(string `id: ${id}`, string `users/${id}/posts`); | ||
UserNotFound userNotFound = { | ||
body: errorDetails | ||
}; | ||
return userNotFound; | ||
} | ||
return result; | ||
} | ||
|
||
# Create a new user | ||
# | ||
# + newUser - The user details of the new user | ||
# + return - The created message or error message | ||
resource function post users(NewUser newUser) returns http:Created|error { | ||
_ = check self.socialMediaDb->execute(` | ||
INSERT INTO social_media_database.user(birth_date, name) | ||
VALUES (${newUser.birthDate}, ${newUser.name});`); | ||
return http:CREATED; | ||
} | ||
|
||
# Delete a user | ||
# | ||
# + id - The user ID of the user to be deleted | ||
# + return - The success message or error message | ||
resource function delete users/[int id]() returns http:NoContent|error { | ||
_ = check self.socialMediaDb->execute(` | ||
DELETE FROM social_media_database.user WHERE id = ${id};`); | ||
return http:NO_CONTENT; | ||
} | ||
|
||
# Get posts for a give user | ||
# | ||
# + id - The user ID for which posts are retrieved | ||
# + return - A list of posts or error message | ||
resource function get users/[int id]/posts() returns Post[]|UserNotFound|error { | ||
User|error result = self.socialMediaDb->queryRow(`SELECT * FROM social_media_database.user WHERE id = ${id}`); | ||
if result is sql:NoRowsError { | ||
ErrorDetails errorDetails = buildErrorPayload(string `id: ${id}`, string `users/${id}/posts`); | ||
UserNotFound userNotFound = { | ||
body: errorDetails | ||
}; | ||
return userNotFound; | ||
} | ||
|
||
stream<Post, sql:Error?> postStream = self.socialMediaDb->query(`SELECT id, description FROM social_media_database.post WHERE user_id = ${id}`); | ||
Post[]|error posts = from Post post in postStream | ||
select post; | ||
return posts; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it okay if we just return the query expression here? |
||
} | ||
|
||
# Create a post for a given user | ||
# | ||
# + id - The user ID for which the post is created | ||
# + return - The created message or error message | ||
resource function post users/[int id]/posts(NewPost newPost) returns http:Created|UserNotFound|PostForbidden|error { | ||
User|error result = self.socialMediaDb->queryRow(`SELECT * FROM social_media_database.user WHERE id = ${id}`); | ||
if result is sql:NoRowsError { | ||
ErrorDetails errorDetails = buildErrorPayload(string `id: ${id}`, string `users/${id}/posts`); | ||
UserNotFound userNotFound = { | ||
body: errorDetails | ||
}; | ||
return userNotFound; | ||
} | ||
|
||
Sentiment sentiment = check self.sentimentEndpoint->/text\-processing/api/sentiment.post( | ||
{ text: newPost }, | ||
mediatype = mime:APPLICATION_FORM_URLENCODED | ||
); | ||
if sentiment.label == "neg" { | ||
ErrorDetails errorDetails = buildErrorPayload(string `id: ${id}`, string `users/${id}/posts`); | ||
PostForbidden postForbidden = { | ||
body: errorDetails | ||
}; | ||
return postForbidden; | ||
} | ||
|
||
_ = check self.socialMediaDb->execute(` | ||
INSERT INTO social_media_database.post(description, user_id) | ||
VALUES (${newPost.description}, ${id});`); | ||
return http:CREATED; | ||
} | ||
} | ||
|
||
function buildErrorPayload(string msg, string path) returns ErrorDetails { | ||
ErrorDetails errorDetails = { | ||
message: msg, | ||
timeStamp: time:utcNow(), | ||
details: string `uri=${path}` | ||
}; | ||
return errorDetails; | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.