Skip to content

Commit 832099f

Browse files
authored
Merge pull request #5 from fractalliter/upgrade_dependencies
Upgrade dependencies
2 parents 7521288 + 7db7c0e commit 832099f

File tree

8 files changed

+178
-110
lines changed

8 files changed

+178
-110
lines changed

.env-example

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
WEB_PORT=8080
2+
WEB_DB_URL=jdbc:postgresql://db:5432
3+
WEB_DATABASE=personia
4+
WEB_DB_USERNAME=postgres
5+
WEB_DB_PASSWORD=postgres
6+
JWT_SECRET=secret
7+
JWT_ISSUER=http://0.0.0.0:8080/
8+
JWT_AUDIENCE=http://0.0.0.0:8080/hierarchy
9+
JWT_REALM=hierarchy
10+
JWT_EXPIRATION=31536000
11+
AUTH_DB_VENDOR=POSTGRES
12+
AUTH_DB_ADDRESS=auth-db
13+
AUTH_DB_DATABASE=keycloak
14+
AUTH_DB_USER=keycloak
15+
AUTH_DB_SCHEMA=public
16+
AUTH_DB_PASSWORD=password
17+
AUTH_SERVER_USER=manager
18+
AUTH_SERVER_PASSWORD=manager
19+
AUTH_ADMIN_USER=admin
20+
AUTH_ADMIN_PASSWORD=admin

.github/pull_request_template.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
### All Submissions:
2+
3+
- [ ] The commit message follows our guidelines
4+
- [ ] Tests for the changes have been added (for bug fixes/features)
5+
- [ ] Docs have been added / updated (for bug fixes / features)
6+
7+
<!-- You can erase any parts of this template not applicable to your Pull Request. -->
8+
9+
### How to test
10+
<!-- Add description about the tests -->
11+
12+
### New Feature Submissions:
13+
14+
1. [ ] Does your submission pass tests?
15+
2. [ ] Have you cleaned up your code locally before submission?
16+
17+
### Changes to Core Features:
18+
19+
* [ ] Have you added an explanation of what your changes do and why you'd like us to include them?
20+
* [ ] Have you written new tests for your core changes, as applicable?
21+
* [ ] Have you successfully run tests with your changes locally?

README.md

Lines changed: 82 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,87 @@
1-
# Kotlin-Ktor and postgres backend
2-
3-
## Introduction
4-
5-
Here lays a demonstration of my growing interest in Kotlin language and Ktor server framework. It's an example with
6-
complex Graph algorithms.
1+
# Kotlin Ktor and postgres backend
2+
Here lays a Kotlin web project with Ktor framework, Postgres database, and JWT Authentication.
3+
4+
The project comprises following ingredients:
5+
- [Ktor](https://ktor.io/) server includes [JSON serializers](https://ktor.io/docs/serialization.html), [Authentication](https://ktor.io/docs/authentication.html), and [Testing](https://ktor.io/docs/testing.html)
6+
- [Netty](https://netty.io/) web server
7+
- [Postgres](https://www.postgresql.org/) as database
8+
- [Exposed](https://github.com/JetBrains/Exposed) as ORM
9+
- [Hikari Connection Pool](https://github.com/brettwooldridge/HikariCP)
10+
- [Logback](https://logback.qos.ch/) for logging purposes
11+
- [JBCrypt](https://www.mindrot.org/projects/jBCrypt/) for hashing passwords(No salting yet)
12+
13+
There is a simple implementation of Graph search and traverse with DFS algorithm but if you aim for a solid solution
14+
better go for [GUAVA Graph](https://github.com/google/guava/wiki/GraphsExplained) from Google.
15+
16+
Project is agnostic about database, you can dynamically change Postgres to any other databases that Exposed JDBC
17+
supports by changing a couple of variables:
18+
- the database driver version in `gradle.properties`
19+
- the database driver dependency in `build.gradle.kts`
20+
- the **WEB_DB_URL** variable in `.env` file
721

822
## Flow
23+
1. deploy the docker compose with `docker compose up -d` command
24+
2. sign up to the system `/signup` with a username and password(not hardened enough)
25+
3. log in to with username and password to get access token `/login`
26+
4. send post request with payload and token to `/hirearchy` to create the hierarchy of the organization
27+
5. send get request with token to `/hierarchy/{name}/supervisors` to fetch the supervisors of the current user
928

10-
To start the application you need to:
11-
12-
1. deploy the docker compose
13-
2. sign up to the system `/signup`
14-
3. log in to get access token `/login`
15-
4. send post request with token to `/hirearchy` to create the hierarchy of the organization
16-
5. send post request with token to `/hierarchy/{name}/supervisors` to get the supervisors of the user
29+
## How to use
1730

18-
## Starting the application
31+
> You need **root access** for docker
1932
20-
Go to the root directory of the project and deploy the application with following command:
33+
Go to the root directory of the project where `docker-compose.yml` is and change the environment variables in
34+
`.env-example` with yours and rename the file to `mv .env-example .env` then deploy the application with following command:
2135

2236
```bash
2337
docker-compose up
2438
```
2539

26-
for shutting down the deployment:
40+
for shutting down the deployment run following command where the `docker-compose.yml` file resides:
2741

2842
```bash
2943
docker-compose down -v
3044
```
3145

32-
You might need root user access as well
33-
3446
### What it does?
3547

36-
We have REST API to post the JSON below. This JSON represents an Person -> Person relationship that looks like this:
48+
We have REST API to post the JSON below. This JSON represents a Person -> Person relationship that looks like this:
3749

38-
```json
39-
{
50+
```json
51+
{
4052
"Pete": "Nick",
4153
"Barbara": "Nick",
4254
"Nick": "Sophie",
4355
"Sophie": "Jonas"
4456
}
45-
```
57+
```
4658

4759
In this case, Nick is a supervisor of Pete and Barbara, Sophie supervises Nick. The supervisor list is
4860
not always in order.
4961

50-
```bash
51-
curl --request POST -sLv \
52-
--url 'http://localhost:3000/hierarchy' \
53-
--header "Content-Type: application/json" \
54-
--header "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vMC4wLjAuMDo4MDgwL2hpZXJhcmNoeSIsImlzcyI6Imh0dHA6Ly8wLjAuMC4wOjgwODAvIiwiZXhwIjoxNjUwNDkwNzUwLCJ1c2VybmFtZSI6ImphbmUifQ.Xfn4JEOHo-Px7vy0TVyo3malCFlj3eFvzAJejqlefPM" \
55-
--data '{"Nick":"Barbara","Barbara":"Nick","Elias":"Levi"}'
56-
```
62+
```bash
63+
curl --request POST -sLv \
64+
--url 'http://localhost:3000/hierarchy' \
65+
--header "Content-Type: application/json" \
66+
--header "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vMC4wLjAuMDo4MDgwL2hpZXJhcmNoeSIsImlzcyI6Imh0dHA6Ly8wLjAuMC4wOjgwODAvIiwiZXhwIjoxNjUwNDkwNzUwLCJ1c2VybmFtZSI6ImphbmUifQ.Xfn4JEOHo-Px7vy0TVyo3malCFlj3eFvzAJejqlefPM" \
67+
--data '{"Nick":"Barbara","Barbara":"Nick","Elias":"Levi"}'
68+
69+
```
5770

5871
The response to querying the endpoint where the root is at the top of the JSON nested dictionary. For instance, previous
5972
input would result in:
6073

6174
```bash
62-
curl --request GET -sLv \
63-
--url 'http://localhost:3000/hierarchy' \
64-
--header "Content-Type: application/json" \
65-
--header "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vMC4wLjAuMDo4MDgwL2hpZXJhcmNoeSIsImlzcyI6Imh0dHA6Ly8wLjAuMC4wOjgwODAvIiwiZXhwIjoxNjUwNDkwNzUwLCJ1c2VybmFtZSI6ImphbmUifQ.Xfn4JEOHo-Px7vy0TVyo3malCFlj3eFvzAJejqlefPM"
66-
```
75+
curl --request GET -sLv \
76+
--url 'http://localhost:3000/hierarchy' \
77+
--header "Content-Type: application/json" \
78+
--header "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vMC4wLjAuMDo4MDgwL2hpZXJhcmNoeSIsImlzcyI6Imh0dHA6Ly8wLjAuMC4wOjgwODAvIiwiZXhwIjoxNjUwNDkwNzUwLCJ1c2VybmFtZSI6ImphbmUifQ.Xfn4JEOHo-Px7vy0TVyo3malCFlj3eFvzAJejqlefPM"
79+
```
6780

6881
the response will be:
6982

70-
```json
71-
{
83+
```json
84+
{
7285
"Jonas": {
7386
"Sophie": {
7487
"Nick": {
@@ -78,28 +91,28 @@ the response will be:
7891
}
7992
}
8093
}
81-
```
94+
```
8295

8396
Query for a specific Person it's the hierarchy:
8497

85-
```bash
86-
curl --request GET -sLv \
87-
--url 'http://localhost:3000/hierarchy/Nick/supervisors'\
88-
--header "Content-Type: application/json" \
89-
--header "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vMC4wLjAuMDo4MDgwL2hpZXJhcmNoeSIsImlzcyI6Imh0dHA6Ly8wLjAuMC4wOjgwODAvIiwiZXhwIjoxNjUwNDkwNzUwLCJ1c2VybmFtZSI6ImphbmUifQ.Xfn4JEOHo-Px7vy0TVyo3malCFlj3eFvzAJejqlefPM"
90-
```
98+
```bash
99+
curl --request GET -sLv \
100+
--url 'http://localhost:3000/hierarchy/Nick/supervisors'\
101+
--header "Content-Type: application/json" \
102+
--header "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vMC4wLjAuMDo4MDgwL2hpZXJhcmNoeSIsImlzcyI6Imh0dHA6Ly8wLjAuMC4wOjgwODAvIiwiZXhwIjoxNjUwNDkwNzUwLCJ1c2VybmFtZSI6ImphbmUifQ.Xfn4JEOHo-Px7vy0TVyo3malCFlj3eFvzAJejqlefPM"
103+
```
91104

92105
the response of the query will be:
93106

94-
```json
95-
{
107+
```json
108+
{
96109
"Nick": {
97110
"Sophie": {
98111
"Jonas": {}
99112
}
100113
}
101114
}
102-
```
115+
```
103116

104117
Sophie is the supervisor of the Nick and Jonas is supervisor of the supervisor of the Nick
105118

@@ -108,34 +121,41 @@ Sophie is the supervisor of the Nick and Jonas is supervisor of the supervisor o
108121
Sign up to the system
109122

110123
```bash
111-
curl --request POST -sL \
112-
--url 'http://localhost:3000/signup'\
113-
--header "Content-Type: application/json" \
114-
--data '{"username":"jane","password":"doe"}'
124+
curl --request POST -sL \
125+
--url 'http://localhost:3000/signup'\
126+
--header "Content-Type: application/json" \
127+
--data '{"username":"jane","password":"doe"}'
115128
```
116129

117130
login to the system
118131

119132
```bash
120-
curl --request POST -sL \
121-
--url 'http://localhost:3000/login'\
122-
--header "Content-Type: application/json" \
123-
--data '{"username":"jane","password":"doe"}'
124-
```
133+
curl --request POST -sL \
134+
--url 'http://localhost:3000/login'\
135+
--header "Content-Type: application/json" \
136+
--data '{"username":"jane","password":"doe"}'
137+
```
125138

126139
The response will be the access token
127140

128141
```json
129-
{
142+
{
130143
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiJodHRwOi8vMC4wLjAuMDo4MDgwL2hpZXJhcmNoeSIsImlzcyI6Imh0dHA6Ly8wLjAuMC4wOjgwODAvIiwiZXhwIjoxNjUwMTU3NjIxLCJ1c2VybmFtZSI6ImpvaG4ifQ.LSJUte7oy9Kv7qkozI3APBzPxHVZ56GID-n0lRIKvdY"
131144
}
132145
```
133146

134-
Also, you can use following [Postman JSON collection file](/postman_collection.json) and test the application from
135-
Postman
147+
## How to test locally
148+
For testing the project locally you can run docker compose with `docker-compose-test.yml` file. It will run the tests
149+
against a test database.
150+
```bash
151+
docker-compose --file docker-compose-test.yml up
152+
```
153+
After finishing the tests you can clean test data nd shut the containers down with following command:
136154

137-
To deploy the test environment and test the application:
155+
```bash
156+
docker-compose --file docker-compose-test.yml down -v
157+
```
138158

139-
```bash
140-
docker-compose --file docker-compose-test.yml up
141-
```
159+
## Continues Integration
160+
For continues integration, the CI workflow prepares the database, run the gradle build with tests, and generates report
161+
to Codacy about the quality of code.

build.gradle.kts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ val ktor_version: String by project
22
val kotlin_version: String by project
33
val logback_version: String by project
44
val exposed_version: String by project
5-
val postgresql_version: String by project
5+
val database_driver_version: String by project
66
val gson_version: String by project
77
val jbcrypt_version: String by project
88
val hikaricp_version: String by project
@@ -46,7 +46,8 @@ dependencies {
4646
implementation("org.jetbrains.exposed:exposed-core:$exposed_version")
4747
implementation("org.jetbrains.exposed:exposed-dao:$exposed_version")
4848
implementation("org.jetbrains.exposed:exposed-jdbc:$exposed_version")
49-
implementation("org.postgresql:postgresql:$postgresql_version")
49+
// add database driver below, default is postgres
50+
implementation("org.postgresql:postgresql:$database_driver_version")
5051
implementation("com.google.code.gson:gson:$gson_version")
5152
implementation("io.ktor:ktor-server-auth:$ktor_version")
5253
implementation("io.ktor:ktor-server-status-pages:$ktor_version")

gradle.properties

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
ktor_version=2.0.0
1+
ktor_version=2.2.4
22
kotlin_version=1.6.20
33
logback_version=1.2.11
44
kotlin.code.style=official
55
exposed_version=0.37.3
6-
postgresql_version=42.3.3
6+
# Postgresql driver version, it can be any db driver. Also change the equivalent dependency
7+
database_driver_version=42.5.1
78
gson_version=2.9.0
89
jbcrypt_version=0.4
910
hikaricp_version=5.0.1

src/main/kotlin/com/personia/routes/HierarchyRoutes.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import io.ktor.server.response.*
1010
import io.ktor.server.routing.*
1111

1212
fun Route.hierarchyRouting(nodeService: NodeService) {
13+
val applicationJson = ContentType("application", "json")
1314
val gson = Gson()
1415

1516
route("hierarchy") {
@@ -38,19 +39,13 @@ fun Route.hierarchyRouting(nodeService: NodeService) {
3839

3940
val response = nodeService.retrieveSupervisors(name, level)
4041
val json: String = gson.toJson(response)
41-
call.respondText(
42-
text = json,
43-
contentType = ContentType("application", "json")
44-
)
42+
call.respondText(text = json,contentType = applicationJson)
4543
}
4644
post {
4745
val hierarchy = call.receive<Map<String, String>>()
4846
val response = nodeService.createHierarchy(hierarchy)
4947
val json = gson.toJson(response)
50-
call.respondText(
51-
text = json,
52-
contentType = ContentType("application", "json")
53-
)
48+
call.respondText(text = json,contentType = applicationJson)
5449
}
5550
}
5651
}

0 commit comments

Comments
 (0)