Skip to content

Commit

Permalink
[3.0.0] Kotlin version (#46)
Browse files Browse the repository at this point in the history
* Added Develop to all pipelines

* Added kotlin jvm plugin

* Added Main.kt

* Fixing warning error

* Migrating Main class to kotlin

* Updated tests

* Implementing spring data jpa for kotlin

* Migrating all repositories to kotlin

* Changed some service and object to kotlin

* Continuing migration

* changing json changelogs to xml changelogs

* removing unused import

* create gradle.properties to avoid metaspace error in github action

* Finishing migrating business code to kotlin

* [LT-1] Removed MainClass test

* Added tests for LootService class

* Added jacoco report for sonarcloud

* Added new test for LootController

* [LT-2] Optimizing the loot controller (#43)

* [LT-3] Cleaning the gradle.build file (#44)

* [LT-4] Update README.md (#45)

* [LT-2] Optimizing the loot controller (#43)

* [LT-3] Cleaning the gradle.build file (#44)

* [LT-4] Update README.md (#45)
  • Loading branch information
bfresnel authored Jan 2, 2023
1 parent e110be9 commit 7901d6d
Show file tree
Hide file tree
Showing 14 changed files with 70 additions and 111 deletions.
23 changes: 7 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# 🎰 LOOT-TABLE 🎰

[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=bfresnel_loot-table-poc&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=bfresnel_loot-table-poc)
[![Java CI with Gradle](https://github.com/bfresnel/loot-table-poc/actions/workflows/gradle.yml/badge.svg?branch=main)](https://github.com/bfresnel/loot-table-poc/actions/workflows/gradle.yml)
[![CodeQL](https://github.com/bfresnel/loot-table-poc/actions/workflows/codeql-analysis.yml/badge.svg?branch=main)](https://github.com/bfresnel/loot-table-poc/actions/workflows/codeql-analysis.yml)

Expand All @@ -8,19 +9,11 @@
This project was created in order to understand how all looting games are working.
This project could also be updated or not.

**_It has many flaws, like no-real logging, no database etc etc... it's just a personal project._**
**_It has many flaws, it's just a personal project._**

## Principle

We have 2 files that contains data :

* ```characters.json``` : Contains all characters and their rarity
* ```drop-chance.json``` : Contains all percentage for each rarity

⚠️ _**These files can be edited. However, there is absolutely no percentage control for the ```drop-chance.json```
file.**_

For each pull, we retrieve 5 lists of 10 characters.
For each pull, we retrieve a list of 10 characters.
In each list -> we integrate as much character as their rarity in a 100-sized array.

For example :
Expand All @@ -35,10 +28,8 @@ If you liked this, don't forget to add a 🌟 👋

## How to use it

You have to install the java JDK with version >= 16.0.2

Go to the folder where the downloaded jar is located then use the following command :
$ cd loot-table
$ docker-compose up -d .

```
$ java -jar loot-table-1.0.0.jar
```
The last command will build everything under docker including the webapp and the database (it includes a Postgresql
image)
27 changes: 2 additions & 25 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ plugins {
}

group = "fr.bfr"
version = "2.1.1"
version = "3.0.0"
java.sourceCompatibility = JavaVersion.VERSION_16


Expand All @@ -22,17 +22,13 @@ configurations {
}
}

sourceSets.main {
java.srcDirs("src/main/kotlin", "src/main/java")
}

repositories {
mavenCentral()
}

dependencies {
compileOnly("org.projectlombok:lombok:1.18.24")
runtimeOnly("org.postgresql:postgresql")

implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0")
implementation("ch.qos.logback:logback-core:1.2.11")
implementation("org.slf4j:slf4j-api:1.7.36")
Expand All @@ -42,47 +38,28 @@ dependencies {
implementation("org.liquibase:liquibase-core:4.18.0")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")

implementation("org.jetbrains.kotlin:kotlin-reflect")

testCompileOnly("org.projectlombok:lombok:1.18.24")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.0")
testImplementation("com.h2database:h2:2.1.214")
testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.0")
testImplementation("org.springframework.boot:spring-boot-starter-test")

annotationProcessor("org.projectlombok:lombok:1.18.24")
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")
testAnnotationProcessor("org.projectlombok:lombok:1.18.24")
}

tasks.getByName<Test>("test") {
useJUnitPlatform()
}

tasks.getByName<Jar>("jar") {
enabled = false
}

tasks.withType<KotlinCompile> {
kotlinOptions {
freeCompilerArgs = listOf("-Xjsr305=strict")
jvmTarget = JavaVersion.VERSION_16.toString()
}
}

tasks.jar {
manifest {
attributes(
"Main-class" to "fr.bfr.Main",
"Class-Path" to configurations.runtimeClasspath.get()
.filter { it.isFile }
.joinToString(" ") { it.name }
)
}
}

tasks.jacocoTestReport {
reports {
xml.required.set(true)
Expand Down
2 changes: 1 addition & 1 deletion dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ RUN gradle clean build
# Stage 2 : Build the wanted image
FROM eclipse-temurin:16-jdk as CREATE_WEBAPP
ENV APP_HOME=/usr/app
ENV APP_NAME=loot-table-2.1.1.jar
ENV APP_NAME=loot-table-3.0.0.jar
WORKDIR $APP_HOME/
COPY --from=BUILD_JAR $APP_HOME/build/libs/$APP_NAME .
ENTRYPOINT exec java -jar $APP_NAME
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m
org.gradle.jvmargs=-Xmx2048m
4 changes: 2 additions & 2 deletions src/main/kotlin/fr/bfr/api/CharacterRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package fr.bfr.api

import fr.bfr.model.Character
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Service
import org.springframework.stereotype.Repository

@Service
@Repository
interface CharacterRepository : JpaRepository<Character, Long> {
override fun findAll(): List<Character>
}
6 changes: 2 additions & 4 deletions src/main/kotlin/fr/bfr/api/DropRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package fr.bfr.api

import fr.bfr.model.DropChance
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.lang.NonNull
import org.springframework.stereotype.Service
import org.springframework.stereotype.Repository

@Service
@Repository
interface DropChanceRepository : JpaRepository<DropChance, Int> {
@NonNull
override fun findAll(): List<DropChance>
}
3 changes: 1 addition & 2 deletions src/main/kotlin/fr/bfr/api/LootApi.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package fr.bfr.api

import fr.bfr.model.Character
import fr.bfr.model.DropChance

interface LootApi {
fun pull(data: List<Character>, dropChances: List<DropChance>, numberOfPull: Int): List<Character>
fun pull(numberOfPull: Int): List<Character>
}
15 changes: 3 additions & 12 deletions src/main/kotlin/fr/bfr/controller/LootController.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package fr.bfr.controller

import fr.bfr.api.CharacterRepository
import fr.bfr.api.DropChanceRepository
import fr.bfr.model.Character
import fr.bfr.services.LootService
import org.slf4j.LoggerFactory
Expand All @@ -14,21 +12,14 @@ import org.springframework.web.bind.annotation.RestController

@RestController
class LootController @Autowired constructor(
val characterRepository: CharacterRepository,
val dropRepository: DropChanceRepository,
val lootService: LootService
) {
@GetMapping(value = ["/pull"], produces = [MediaType.APPLICATION_JSON_VALUE])
fun pull(): ResponseEntity<List<Character>> {
logger.info("/pull endpoint was called !")
val characters = characterRepository.findAll()
val dropChances = dropRepository.findAll()
var lootedCharacters: List<Character> = ArrayList()
if (characters.isNotEmpty() && dropChances.isNotEmpty()) {
logger.info("Retrieving all looted characters...")
lootedCharacters = lootService.pull(characters, dropChances, 10)
logger.info("Characters looted successfully !")
}
logger.info("Retrieving all looted characters...")
val lootedCharacters: List<Character> = lootService.pull(10)

return ResponseEntity(lootedCharacters, HttpStatus.OK)
}

Expand Down
4 changes: 1 addition & 3 deletions src/main/kotlin/fr/bfr/model/Character.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
package fr.bfr.model

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.Id
import javax.persistence.Table

@Entity
@Table(name = "t_character")
class Character(
@Id
@GeneratedValue
var id: Long,
var name: String,
var rarity: Int
var rarity: Int,
)
2 changes: 1 addition & 1 deletion src/main/kotlin/fr/bfr/model/DropChance.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ import javax.persistence.Table
class DropChance(
@Id
var rarity: Int,
var chance: Double
var chance: Int,
)
15 changes: 10 additions & 5 deletions src/main/kotlin/fr/bfr/services/LootService.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
package fr.bfr.services

import fr.bfr.api.CharacterRepository
import fr.bfr.api.DropChanceRepository
import fr.bfr.api.LootApi
import fr.bfr.model.Character
import fr.bfr.model.DropChance
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import java.security.SecureRandom

@Service
class LootService : LootApi {
class LootService @Autowired constructor(
val characterRepository: CharacterRepository,
val dropChanceRepository: DropChanceRepository
) : LootApi {
val secureRandom: SecureRandom = SecureRandom()

override fun pull(
data: List<Character>,
dropChances: List<DropChance>,
numberOfPull: Int
): List<Character> {
val data: List<Character> = characterRepository.findAll();
val dropChanceList: List<DropChance> = dropChanceRepository.findAll();
val pulledCharacters: MutableList<Character> = ArrayList()
val charactersListWithDropChance: MutableList<Character> = ArrayList()
var counter = 0

// Setting an array of 100 characters with number of each character matching the chance
for (dropChance in dropChances) {
for (dropChance in dropChanceList) {
while (counter < dropChance.chance) {
charactersListWithDropChance.add(data[dropChance.rarity - 1])
counter++
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/db/scripts/init-database.sql
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
CREATE TABLE public.t_character (
id int8 NOT NULL GENERATED BY DEFAULT AS IDENTITY,
id int8 NOT NULL,
"name" varchar(255) NULL,
rarity int4 NULL,
CONSTRAINT t_character_pkey PRIMARY KEY (id)
);

CREATE TABLE public.t_drop (
rarity int4 NOT NULL,
chance float8 NULL,
chance int4 NULL,
CONSTRAINT t_drop_pkey PRIMARY KEY (rarity)
);
37 changes: 21 additions & 16 deletions src/test/kotlin/controller/LootControllerTest.kt
Original file line number Diff line number Diff line change
@@ -1,26 +1,31 @@
package controller

import fr.bfr.Main
import fr.bfr.controller.LootController
import fr.bfr.model.Character
import org.assertj.core.api.Assertions
import fr.bfr.services.LootService
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.boot.test.web.client.getForEntity
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.springframework.http.HttpStatus
import org.springframework.test.context.ActiveProfiles
import org.springframework.http.ResponseEntity

@SpringBootTest(classes = [Main::class], webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class LootControllerTest(
@Autowired val restTemplate: TestRestTemplate
) {
class LootControllerTest {

@Test
fun `Integration test of the Pull endpoint`() {
val entity = restTemplate.getForEntity<List<Character>>("/pull")
Assertions.assertThat(entity.statusCode).isEqualTo(HttpStatus.OK)
Assertions.assertThat(entity.body).isEqualTo(emptyList<Character>())
fun `Check if pull endpoint is sending correct data`() {
// Mocking
val mockLootService: LootService = mock()
whenever(mockLootService.pull(any())).thenReturn(listOf(Character(1, "bfr", 1)))
val lootController = LootController(mockLootService)

// Using the method to test
val result: ResponseEntity<List<Character>> = lootController.pull()

// Assertions
Assertions.assertEquals(HttpStatus.OK, result.statusCode)
result.body?.let { Assertions.assertEquals(1, it.size) }
Assertions.assertEquals(true, result.body?.isNotEmpty() ?: false)
}
}
37 changes: 16 additions & 21 deletions src/test/kotlin/services/LootServiceTest.kt
Original file line number Diff line number Diff line change
@@ -1,40 +1,35 @@
package services

import fr.bfr.Main
import fr.bfr.api.CharacterRepository
import fr.bfr.api.DropChanceRepository
import fr.bfr.model.Character
import fr.bfr.model.DropChance
import fr.bfr.services.LootService
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.test.context.ActiveProfiles
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import fr.bfr.model.Character as CharacterBfr

@SpringBootTest(classes = [Main::class])
@ActiveProfiles("test")
class LootServiceTest(@Autowired val lootService: LootService) {
class LootServiceTest {

@Test
fun `Assert pull method is working`() {
//data
val data: List<CharacterBfr> = generateCharacterList()
//DropChance
val dropChance: List<DropChance> = generateDropChanceList()
//NumberOfPull
// Mocking
val mockCharacterRepository: CharacterRepository = mock()
val mockDropChanceRepository: DropChanceRepository = mock()
whenever(mockCharacterRepository.findAll()).thenReturn(listOf(Character(1, "bfr", 1)))
whenever(mockDropChanceRepository.findAll()).thenReturn(listOf(DropChance(1, 100)))
val lootService = LootService(mockCharacterRepository, mockDropChanceRepository)

// NumberOfPull
val numberOfPull = 1
val result: List<CharacterBfr> = lootService.pull(data, dropChance, numberOfPull)
val result: List<CharacterBfr> = lootService.pull(numberOfPull)

// Assertions
Assertions.assertEquals(result.size, 1)
Assertions.assertEquals(result[0].id, 1)
Assertions.assertEquals(result[0].name, "bfr")
Assertions.assertEquals(result[0].rarity, 1)
}

fun generateCharacterList(): List<CharacterBfr> {
return listOf(Character(1, "bfr", 1))
}

fun generateDropChanceList(): List<DropChance> {
return listOf(DropChance(1, 100.0))
}
}

0 comments on commit 7901d6d

Please sign in to comment.