From 90d73264867a9604aa48293ccacdcda201449463 Mon Sep 17 00:00:00 2001
From: xb205 <62425964+devxb@users.noreply.github.com>
Date: Tue, 16 Apr 2024 17:29:41 +0900
Subject: [PATCH 1/4] =?UTF-8?q?docs:=20=EB=B3=B4=EC=97=AC=EC=A7=80?=
=?UTF-8?q?=EB=8A=94=20=ED=8E=AB=EC=9D=84=20=EC=8A=AC=EB=9D=BC=EC=9E=84?=
=?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=9C=EB=8B=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index be1a20d..7e573bc 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
##
-
+
⭐스타를 눌러주세요 개발에 큰 도움이 됩니다!⭐️
From 032bd1b4c6784a9a496f5dadac09ed34ddafcc45 Mon Sep 17 00:00:00 2001
From: devxb
Date: Wed, 17 Apr 2024 12:53:24 +0900
Subject: [PATCH 2/4] =?UTF-8?q?feat:=20GET=20/users/{username}=20api?=
=?UTF-8?q?=EC=97=90=20cors=EB=A5=BC=20=EC=A0=81=EC=9A=A9=ED=95=9C?=
=?UTF-8?q?=EB=8B=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
gradle.properties | 6 ++
gradle/db.gradle | 2 +-
gradle/test.gradle | 3 +
.../render/controller/filter/CorsFilter.kt | 41 ++++++++++++
.../org/gitanimals/render/controller/Api.kt | 15 +++++
.../controller/filter/CorsFilterTest.kt | 63 +++++++++++++++++++
.../gitanimals/render/domain/UserFixture.kt | 3 +-
.../render/supports/RedisContainer.kt | 27 ++++++++
src/test/resources/application.properties | 28 +++++++++
9 files changed, 186 insertions(+), 2 deletions(-)
create mode 100644 src/main/kotlin/org/gitanimals/render/controller/filter/CorsFilter.kt
create mode 100644 src/test/kotlin/org/gitanimals/render/controller/Api.kt
create mode 100644 src/test/kotlin/org/gitanimals/render/controller/filter/CorsFilterTest.kt
create mode 100644 src/test/kotlin/org/gitanimals/render/supports/RedisContainer.kt
create mode 100644 src/test/resources/application.properties
diff --git a/gradle.properties b/gradle.properties
index 91ef374..3370ffa 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -28,3 +28,9 @@ netxVersion=0.4.2
### Sentry ###
sentryVersion=4.4.0
+
+### RestAssured ###
+restAssuredVersion=5.4.0
+
+### H2version ###
+h2Version=1.4.200
diff --git a/gradle/db.gradle b/gradle/db.gradle
index 7d7afdc..61d4ab8 100644
--- a/gradle/db.gradle
+++ b/gradle/db.gradle
@@ -1,5 +1,5 @@
dependencies {
implementation "mysql:mysql-connector-java:${mysqlConnectorVersion}"
- testRuntimeOnly "com.h2database:h2"
+ testRuntimeOnly "com.h2database:h2:${h2Version}"
}
diff --git a/gradle/test.gradle b/gradle/test.gradle
index 60b80e1..bf9f2e1 100644
--- a/gradle/test.gradle
+++ b/gradle/test.gradle
@@ -8,4 +8,7 @@ dependencies {
testImplementation "io.kotest.extensions:kotest-extensions-spring:${kotestExtensionSpringVersion}"
testImplementation "org.testcontainers:testcontainers:${testContainerVersion}"
+
+ testImplementation "io.rest-assured:rest-assured:${restAssuredVersion}"
+
}
diff --git a/src/main/kotlin/org/gitanimals/render/controller/filter/CorsFilter.kt b/src/main/kotlin/org/gitanimals/render/controller/filter/CorsFilter.kt
new file mode 100644
index 0000000..4165d18
--- /dev/null
+++ b/src/main/kotlin/org/gitanimals/render/controller/filter/CorsFilter.kt
@@ -0,0 +1,41 @@
+package org.gitanimals.render.controller.filter
+
+import jakarta.servlet.Filter
+import jakarta.servlet.FilterChain
+import jakarta.servlet.ServletRequest
+import jakarta.servlet.ServletResponse
+import jakarta.servlet.http.HttpServletRequest
+import jakarta.servlet.http.HttpServletResponse
+import org.springframework.http.HttpHeaders
+import org.springframework.stereotype.Component
+
+@Component
+class CorsFilter : Filter {
+
+ override fun doFilter(
+ request: ServletRequest,
+ response: ServletResponse,
+ chain: FilterChain
+ ) {
+ (request as HttpServletRequest)
+ if (regexMatcher.matches(request.requestURI)) {
+ (response as HttpServletResponse).allowCors()
+ }
+ chain.doFilter(request, response)
+ }
+
+ private fun HttpServletResponse.allowCors(): ServletResponse {
+ this.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*")
+ this.addHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "*")
+ this.addHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600")
+ this.addHeader(
+ HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS,
+ "Origin, X-Requested-With, Content-Type, Accept, Authorization"
+ )
+ return this
+ }
+
+ private companion object {
+ private val regexMatcher = Regex("/users/.*")
+ }
+}
diff --git a/src/test/kotlin/org/gitanimals/render/controller/Api.kt b/src/test/kotlin/org/gitanimals/render/controller/Api.kt
new file mode 100644
index 0000000..3f9cf2a
--- /dev/null
+++ b/src/test/kotlin/org/gitanimals/render/controller/Api.kt
@@ -0,0 +1,15 @@
+package org.gitanimals.render.controller
+
+import io.restassured.RestAssured
+import io.restassured.http.ContentType
+import io.restassured.response.ExtractableResponse
+import io.restassured.response.Response
+
+fun users(username: String): ExtractableResponse =
+ RestAssured.given().log().all()
+ .contentType(ContentType.JSON)
+ .accept(ContentType.JSON)
+ .`when`().log().all()
+ .get("/users/$username")
+ .then().log().all()
+ .extract()
diff --git a/src/test/kotlin/org/gitanimals/render/controller/filter/CorsFilterTest.kt b/src/test/kotlin/org/gitanimals/render/controller/filter/CorsFilterTest.kt
new file mode 100644
index 0000000..e3ca0f5
--- /dev/null
+++ b/src/test/kotlin/org/gitanimals/render/controller/filter/CorsFilterTest.kt
@@ -0,0 +1,63 @@
+package org.gitanimals.render.controller.filter
+
+import io.kotest.core.spec.style.DescribeSpec
+import io.kotest.matchers.collections.shouldExistInOrder
+import io.restassured.RestAssured
+import io.restassured.http.Header
+import org.gitanimals.render.controller.users
+import org.gitanimals.render.domain.User
+import org.gitanimals.render.domain.UserRepository
+import org.gitanimals.render.supports.RedisContainer
+import org.junit.jupiter.api.DisplayName
+import org.springframework.boot.test.context.SpringBootTest
+import org.springframework.boot.test.web.server.LocalServerPort
+import org.springframework.test.context.ContextConfiguration
+
+@ContextConfiguration(
+ classes = [
+ RedisContainer::class
+ ]
+)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@DisplayName("Cors 적용 테스트의")
+internal class CorsFilterTest(
+ @LocalServerPort private val port: Int,
+ private val userRepository: UserRepository,
+) : DescribeSpec({
+
+ beforeSpec {
+ RestAssured.port = port
+ }
+
+ afterEach { userRepository.deleteAll() }
+
+ describe("/users/{username} api는") {
+ context("호출되면, ") {
+ it("cors 허용 header들을 추가해서 반환한다.") {
+ val user = userRepository.saveAndFlush(user)
+
+ val response = users(user.name)
+
+ response.headers().shouldExistInOrder(
+ listOf(
+ { it == Header("Access-Control-Allow-Origin", "*") },
+ { it == Header("Access-Control-Allow-Methods", "*") },
+ { it == Header("Access-Control-Max-Age", "3600") },
+ {
+ it == Header(
+ "Access-Control-Allow-Headers",
+ "Origin, X-Requested-With, Content-Type, Accept, Authorization"
+ )
+ }
+ )
+ )
+ }
+ }
+ }
+
+}) {
+
+ private companion object {
+ private val user = User.newUser("devxb", mutableMapOf(2024 to 1000))
+ }
+}
diff --git a/src/test/kotlin/org/gitanimals/render/domain/UserFixture.kt b/src/test/kotlin/org/gitanimals/render/domain/UserFixture.kt
index 2aec862..1438a3a 100644
--- a/src/test/kotlin/org/gitanimals/render/domain/UserFixture.kt
+++ b/src/test/kotlin/org/gitanimals/render/domain/UserFixture.kt
@@ -1,9 +1,10 @@
package org.gitanimals.render.domain
import org.gitanimals.render.domain.value.Contribution
+import kotlin.random.Random
fun user(
- id: Long = 0L,
+ id: Long = Random.nextLong(),
name: String = "devxb",
personas: MutableList = mutableListOf(),
contributions: MutableList = mutableListOf(),
diff --git a/src/test/kotlin/org/gitanimals/render/supports/RedisContainer.kt b/src/test/kotlin/org/gitanimals/render/supports/RedisContainer.kt
new file mode 100644
index 0000000..fda4dbd
--- /dev/null
+++ b/src/test/kotlin/org/gitanimals/render/supports/RedisContainer.kt
@@ -0,0 +1,27 @@
+package org.gitanimals.render.supports
+
+
+import org.springframework.boot.test.context.TestConfiguration
+import org.testcontainers.containers.GenericContainer
+import org.testcontainers.utility.DockerImageName
+
+@TestConfiguration
+internal class RedisContainer {
+ init {
+ val redis: GenericContainer<*> = GenericContainer(DockerImageName.parse("redis:7.2.3"))
+ .withExposedPorts(6379)
+
+ runCatching {
+ redis.start()
+ }.onFailure {
+ if (it is com.github.dockerjava.api.exception.InternalServerErrorException) {
+ redis.start()
+ }
+ }
+
+ System.setProperty(
+ "netx.port",
+ redis.getMappedPort(6379).toString()
+ )
+ }
+}
diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties
new file mode 100644
index 0000000..f509631
--- /dev/null
+++ b/src/test/resources/application.properties
@@ -0,0 +1,28 @@
+spring.datasource.driver-class-name=org.h2.Driver
+spring.datasource.url=jdbc:h2:mem:test;MODE=MySQL
+spring.jpa.hibernated.ddl-auto=create
+spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
+spring.datasource.hikari.maximum-pool-size=4
+spring.datasource.hikari.pool-name=H2_TEST_POOL
+
+### FOR DEBUGGING ###
+logging.level.org.hibernate.SQL=debug
+logging.level.org.hibernate.type.descriptor.sql=trace
+spring.jpa.properties.hibernate.format_sql=true
+spring.jpa.properties.hibernate.highlight_sql=true
+spring.jpa.properties.hibernate.use_sql_comments=true
+
+### NETX ###
+netx.mode=redis
+netx.host=localhost
+netx.port=
+netx.group=render
+netx.node-id=1
+netx.node-name=render-1
+netx.recovery-milli=1000
+netx.orphan-milli=60000
+netx.backpressure=40
+netx.logging.level=info
+
+### GITHUB ###
+github.token=1234
From 8d54512e67c4dd5235a85eab4350d2d4b400bce1 Mon Sep 17 00:00:00 2001
From: devxb
Date: Wed, 17 Apr 2024 17:56:03 +0900
Subject: [PATCH 3/4] =?UTF-8?q?feat:=20lines=EB=AA=A8=EB=93=9C=EC=97=90?=
=?UTF-8?q?=EC=84=9C=20total=20contributions=EB=A5=BC=20=EB=B3=B4=EC=97=AC?=
=?UTF-8?q?=EC=A3=BC=EB=8F=84=EB=A1=9D=20=ED=95=9C=EB=8B=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../gitanimals/render/app/AnimationFacade.kt | 7 +-
.../render/controller/AnimationController.kt | 10 +-
.../org/gitanimals/render/domain/Mode.kt | 8 +
.../org/gitanimals/render/domain/Persona.kt | 6 +-
.../gitanimals/render/domain/PersonaType.kt | 124 +++--
.../org/gitanimals/render/domain/User.kt | 8 +-
.../gitanimals/render/domain/UserService.kt | 4 +-
.../resources/persona/animal/flamingo.svg | 11 +
.../resources/persona/animal/goose-java.svg | 11 +
.../resources/persona/animal/goose-js.svg | 11 +
.../resources/persona/animal/goose-kotlin.svg | 11 +
.../resources/persona/animal/goose-linux.svg | 11 +
.../resources/persona/animal/goose-node.svg | 11 +
.../resources/persona/animal/goose-spring.svg | 11 +
.../persona/animal/goose-sunglasses.svg | 11 +
.../resources/persona/animal/goose-swift.svg | 11 +
src/main/resources/persona/animal/goose.svg | 11 +
.../persona/animal/little-chick-java.svg | 11 +
.../persona/animal/little-chick-js.svg | 11 +
.../persona/animal/little-chick-kotlin.svg | 12 +
.../persona/animal/little-chick-linux.svg | 12 +
.../persona/animal/little-chick-node.svg | 12 +
.../persona/animal/little-chick-spring.svg | 12 +
.../animal/little-chick-sunglasses.svg | 12 +
.../persona/animal/little-chick-swift.svg | 12 +
.../resources/persona/animal/little-chick.svg | 11 +
.../resources/persona/animal/penguin-java.svg | 11 +
.../resources/persona/animal/penguin-js.svg | 11 +
.../persona/animal/penguin-kotlin.svg | 11 +
.../persona/animal/penguin-linux.svg | 11 +
.../resources/persona/animal/penguin-node.svg | 11 +
.../persona/animal/penguin-spring.svg | 11 +
.../persona/animal/penguin-sunglasses.svg | 11 +
.../persona/animal/penguin-swift.svg | 11 +
src/main/resources/persona/animal/penguin.svg | 11 +
.../resources/persona/animal/pig-java.svg | 11 +
src/main/resources/persona/animal/pig-js.svg | 11 +
.../resources/persona/animal/pig-kotlin.svg | 11 +
.../resources/persona/animal/pig-linux.svg | 11 +
.../resources/persona/animal/pig-node.svg | 11 +
.../resources/persona/animal/pig-spring.svg | 11 +
.../persona/animal/pig-sunglasses.svg | 11 +
.../resources/persona/animal/pig-swift.svg | 11 +
src/main/resources/persona/animal/pig.svg | 11 +
.../resources/persona/animal/slime-blue.svg | 11 +
.../resources/persona/animal/slime-green.svg | 11 +
.../persona/animal/slime-red-java.svg | 11 +
.../resources/persona/animal/slime-red-js.svg | 11 +
.../persona/animal/slime-red-kotlin.svg | 11 +
.../persona/animal/slime-red-linux.svg | 11 +
.../persona/animal/slime-red-node.svg | 11 +
.../persona/animal/slime-red-swift.svg | 11 +
.../resources/persona/animal/slime-red.svg | 11 +
src/test/resources/persona/goose/test.svg | 439 ++++++++++++++----
54 files changed, 975 insertions(+), 143 deletions(-)
create mode 100644 src/main/kotlin/org/gitanimals/render/domain/Mode.kt
diff --git a/src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt b/src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt
index fd5d15d..3af8dae 100644
--- a/src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt
+++ b/src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt
@@ -1,5 +1,6 @@
package org.gitanimals.render.app
+import org.gitanimals.render.domain.Mode
import org.gitanimals.render.domain.User
import org.gitanimals.render.domain.UserService
import org.gitanimals.render.domain.event.Visited
@@ -29,17 +30,17 @@ class AnimationFacade(
}
}
- fun getLineAnimation(username: String, personaId: Long): String {
+ fun getLineAnimation(username: String, personaId: Long, mode: Mode): String {
return when (userService.existsByName(username)) {
true -> {
- val svgAnimation = userService.getLineAnimationByUsername(username, personaId)
+ val svgAnimation = userService.getLineAnimationByUsername(username, personaId, mode)
sagaManager.startSync(Visited(username))
svgAnimation
}
false -> {
val user = createNewUser(username)
- userService.getLineAnimationByUsername(user.name, personaId)
+ userService.getLineAnimationByUsername(user.name, personaId, mode)
}
}
}
diff --git a/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt b/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt
index 0fd4e2d..4b3cd59 100644
--- a/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt
+++ b/src/main/kotlin/org/gitanimals/render/controller/AnimationController.kt
@@ -2,6 +2,7 @@ package org.gitanimals.render.controller
import jakarta.servlet.http.HttpServletResponse
import org.gitanimals.render.app.AnimationFacade
+import org.gitanimals.render.domain.Mode
import org.springframework.http.HttpHeaders
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
@@ -26,10 +27,17 @@ class AnimationController(
fun getLineSvgAnimation(
@PathVariable("username") username: String,
@RequestParam(name = "pet-id", defaultValue = "0") personaId: Long,
+ @RequestParam(name = "contribution-view", defaultValue = "true") contributionView: Boolean,
response: HttpServletResponse,
): String {
response.cacheControl(3600)
- return animationFacade.getLineAnimation(deleteBrackets(username), personaId)
+
+ val mode = when (contributionView) {
+ true -> Mode.LINE
+ false -> Mode.LINE_NO_CONTRIBUTION
+ }
+
+ return animationFacade.getLineAnimation(deleteBrackets(username), personaId, mode)
}
private fun deleteBrackets(username: String): String {
diff --git a/src/main/kotlin/org/gitanimals/render/domain/Mode.kt b/src/main/kotlin/org/gitanimals/render/domain/Mode.kt
new file mode 100644
index 0000000..4c08dfe
--- /dev/null
+++ b/src/main/kotlin/org/gitanimals/render/domain/Mode.kt
@@ -0,0 +1,8 @@
+package org.gitanimals.render.domain
+
+enum class Mode {
+ FARM,
+ LINE,
+ LINE_NO_CONTRIBUTION,
+ ;
+}
diff --git a/src/main/kotlin/org/gitanimals/render/domain/Persona.kt b/src/main/kotlin/org/gitanimals/render/domain/Persona.kt
index 7f90b2d..5ddad62 100644
--- a/src/main/kotlin/org/gitanimals/render/domain/Persona.kt
+++ b/src/main/kotlin/org/gitanimals/render/domain/Persona.kt
@@ -35,13 +35,13 @@ class Persona(
) : this(type = type, level = Level(level), visible = visible)
- fun toSvgForce(): String = type.load(this)
+ fun toSvgForce(mode: Mode): String = type.load(user!!, this, mode)
- fun toSvg(): String {
+ fun toSvg(mode: Mode): String {
if (!visible) {
return ""
}
- return type.load(this)
+ return type.load(user!!, this, mode)
}
fun level(): Long = level.value
diff --git a/src/main/kotlin/org/gitanimals/render/domain/PersonaType.kt b/src/main/kotlin/org/gitanimals/render/domain/PersonaType.kt
index ea5ac10..f88adb6 100644
--- a/src/main/kotlin/org/gitanimals/render/domain/PersonaType.kt
+++ b/src/main/kotlin/org/gitanimals/render/domain/PersonaType.kt
@@ -7,7 +7,7 @@ import kotlin.random.Random
enum class PersonaType(private val weight: Double) {
GOOSE(1.0) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val goose = gooseSvg.replace("*{act}", act(persona.id))
@@ -30,7 +30,7 @@ enum class PersonaType(private val weight: Double) {
},
GOOSE_SUNGLASSES(0.05) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val goose = gooseSunglassesSvg.replace("*{act}", act(persona.id))
@@ -53,7 +53,7 @@ enum class PersonaType(private val weight: Double) {
},
GOOSE_KOTLIN(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val goose = gooseKotlinSvg.replace("*{act}", act(persona.id))
@@ -76,7 +76,7 @@ enum class PersonaType(private val weight: Double) {
},
GOOSE_JAVA(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val goose = gooseJavaSvg.replace("*{act}", act(persona.id))
@@ -99,7 +99,7 @@ enum class PersonaType(private val weight: Double) {
},
GOOSE_JS(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val goose = gooseJsSvg.replace("*{act}", act(persona.id))
@@ -122,7 +122,7 @@ enum class PersonaType(private val weight: Double) {
},
GOOSE_NODE(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val goose = gooseNodeSvg.replace("*{act}", act(persona.id))
@@ -145,7 +145,7 @@ enum class PersonaType(private val weight: Double) {
},
GOOSE_SWIFT(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val goose = gooseSwiftSvg.replace("*{act}", act(persona.id))
@@ -168,7 +168,7 @@ enum class PersonaType(private val weight: Double) {
},
GOOSE_LINUX(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val goose = gooseLinuxSvg.replace("*{act}", act(persona.id))
@@ -191,7 +191,7 @@ enum class PersonaType(private val weight: Double) {
},
GOOSE_SPRING(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val goose = gooseSpringSvg.replace("*{act}", act(persona.id))
@@ -214,7 +214,7 @@ enum class PersonaType(private val weight: Double) {
},
LITTLE_CHICK(0.9) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val littleChick = littleChickSvg.replace("*{act}", act(persona.id))
@@ -237,7 +237,7 @@ enum class PersonaType(private val weight: Double) {
},
LITTLE_CHICK_SUNGLASSES(0.4) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val littleChick = littleChickSunglassesSvg.replace("*{act}", act(persona.id))
@@ -260,7 +260,7 @@ enum class PersonaType(private val weight: Double) {
},
LITTLE_CHICK_KOTLIN(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val littleChick = littleChickKotlinSvg.replace("*{act}", act(persona.id))
@@ -283,7 +283,7 @@ enum class PersonaType(private val weight: Double) {
},
LITTLE_CHICK_JAVA(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val littleChick = littleChickJavaSvg.replace("*{act}", act(persona.id))
@@ -306,7 +306,7 @@ enum class PersonaType(private val weight: Double) {
},
LITTLE_CHICK_JS(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val littleChick = littleChickJsSvg.replace("*{act}", act(persona.id))
@@ -329,7 +329,7 @@ enum class PersonaType(private val weight: Double) {
},
LITTLE_CHICK_NODE(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val littleChick = littleChickNodeSvg.replace("*{act}", act(persona.id))
@@ -352,7 +352,7 @@ enum class PersonaType(private val weight: Double) {
},
LITTLE_CHICK_SWIFT(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val littleChick = littleChickSwiftSvg.replace("*{act}", act(persona.id))
@@ -375,7 +375,7 @@ enum class PersonaType(private val weight: Double) {
},
LITTLE_CHICK_LINUX(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val littleChick = littleChickLinuxSvg.replace("*{act}", act(persona.id))
@@ -398,7 +398,7 @@ enum class PersonaType(private val weight: Double) {
},
LITTLE_CHICK_SPRING(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
val littleChick = littleChickSpringSvg.replace("*{act}", act(persona.id))
@@ -421,7 +421,7 @@ enum class PersonaType(private val weight: Double) {
},
PENGUIN(0.5) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return penguinSvg.replace("*{act}", act(persona.id))
@@ -440,7 +440,7 @@ enum class PersonaType(private val weight: Double) {
},
PENGUIN_SUNGLASSES(0.2) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return penguinSunglassesSvg.replace("*{act}", act(persona.id))
@@ -459,7 +459,7 @@ enum class PersonaType(private val weight: Double) {
},
PENGUIN_KOTLIN(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return penguinKotlinSvg.replace("*{act}", act(persona.id))
@@ -478,7 +478,7 @@ enum class PersonaType(private val weight: Double) {
},
PENGUIN_JAVA(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return penguinJavaSvg.replace("*{act}", act(persona.id))
@@ -497,7 +497,7 @@ enum class PersonaType(private val weight: Double) {
},
PENGUIN_JS(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return penguinJsSvg.replace("*{act}", act(persona.id))
@@ -516,7 +516,7 @@ enum class PersonaType(private val weight: Double) {
},
PENGUIN_NODE(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return penguinNodeSvg.replace("*{act}", act(persona.id))
@@ -535,7 +535,7 @@ enum class PersonaType(private val weight: Double) {
},
PENGUIN_SWIFT(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return penguinSwiftSvg.replace("*{act}", act(persona.id))
@@ -554,7 +554,7 @@ enum class PersonaType(private val weight: Double) {
},
PENGUIN_LINUX(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return penguinLinuxSvg.replace("*{act}", act(persona.id))
@@ -573,7 +573,7 @@ enum class PersonaType(private val weight: Double) {
},
PENGUIN_SPRING(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return penguinSpringSvg.replace("*{act}", act(persona.id))
@@ -592,7 +592,7 @@ enum class PersonaType(private val weight: Double) {
},
PIG(0.2) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return pigSvg.replace("*{act}", act(persona.id))
@@ -610,7 +610,7 @@ enum class PersonaType(private val weight: Double) {
},
PIG_SUNGLASSES(0.08) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return pigSunglassesSvg.replace("*{act}", act(persona.id))
@@ -628,7 +628,7 @@ enum class PersonaType(private val weight: Double) {
},
PIG_KOTLIN(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return pigKotlinSvg.replace("*{act}", act(persona.id))
@@ -646,7 +646,7 @@ enum class PersonaType(private val weight: Double) {
},
PIG_JAVA(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return pigJavaSvg.replace("*{act}", act(persona.id))
@@ -664,7 +664,7 @@ enum class PersonaType(private val weight: Double) {
},
PIG_JS(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return pigJsSvg.replace("*{act}", act(persona.id))
@@ -682,7 +682,7 @@ enum class PersonaType(private val weight: Double) {
},
PIG_NODE(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return pigNodeSvg.replace("*{act}", act(persona.id))
@@ -700,7 +700,7 @@ enum class PersonaType(private val weight: Double) {
},
PIG_SWIFT(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return pigSwiftSvg.replace("*{act}", act(persona.id))
@@ -718,7 +718,7 @@ enum class PersonaType(private val weight: Double) {
},
PIG_LINUX(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return pigLinuxSvg.replace("*{act}", act(persona.id))
@@ -736,7 +736,7 @@ enum class PersonaType(private val weight: Double) {
},
PIG_SPRING(0.01) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return pigSpringSvg.replace("*{act}", act(persona.id))
@@ -754,7 +754,7 @@ enum class PersonaType(private val weight: Double) {
},
SLIME_RED(0.1) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return slimeRedSvg.replace("*{act}", act(persona.id))
@@ -772,7 +772,7 @@ enum class PersonaType(private val weight: Double) {
},
SLIME_RED_KOTLIN(0.001) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return slimeRedKotlinSvg.replace("*{act}", act(persona.id))
@@ -790,7 +790,7 @@ enum class PersonaType(private val weight: Double) {
},
SLIME_RED_JAVA(0.001) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return slimeRedJavaSvg.replace("*{act}", act(persona.id))
@@ -808,7 +808,7 @@ enum class PersonaType(private val weight: Double) {
},
SLIME_RED_JS(0.001) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return slimeRedJsSvg.replace("*{act}", act(persona.id))
@@ -826,7 +826,7 @@ enum class PersonaType(private val weight: Double) {
},
SLIME_RED_NODE(0.001) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return slimeRedNodeSvg.replace("*{act}", act(persona.id))
@@ -844,7 +844,7 @@ enum class PersonaType(private val weight: Double) {
},
SLIME_RED_SWIFT(0.001) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return slimeRedSwiftSvg.replace("*{act}", act(persona.id))
@@ -862,7 +862,7 @@ enum class PersonaType(private val weight: Double) {
},
SLIME_RED_LINUX(0.001) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return slimeRedLinuxSvg.replace("*{act}", act(persona.id))
@@ -880,7 +880,7 @@ enum class PersonaType(private val weight: Double) {
},
SLIME_BLUE(0.1) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return slimeBlueSvg.replace("*{act}", act(persona.id))
@@ -898,7 +898,7 @@ enum class PersonaType(private val weight: Double) {
},
SLIME_GREEN(0.1) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return slimeGreenSvg.replace("*{act}", act(persona.id))
@@ -916,7 +916,7 @@ enum class PersonaType(private val weight: Double) {
},
FLAMINGO(0.05) {
- override fun load(persona: Persona): String {
+ override fun loadSvg(user: User, persona: Persona, mode: Mode): String {
check(persona.id != null) { "Save persona first before call load()" }
return flamingoSvg.replace("*{position}", act(persona.id))
@@ -944,10 +944,32 @@ enum class PersonaType(private val weight: Double) {
require(weight in 0.001..1.0) { "PersonaType's weight should be between 0.01 to 1.0" }
}
- abstract fun load(persona: Persona): String
+ fun load(user: User, persona: Persona, mode: Mode): String =
+ loadSvg(user, persona, mode)
+ .drawContribution(mode, user)
+
+ abstract fun loadSvg(user: User, persona: Persona, mode: Mode): String
protected abstract fun act(id: Long): String
+ protected fun String.drawContribution(
+ mode: Mode,
+ user: User
+ ): String {
+ return if (mode == Mode.LINE) {
+ this.replace(
+ "*{contributionx}",
+ (12 + (-1 * (user.contributionCount().toString().length))).toString()
+ )
+ .replace(
+ "*{contribution}",
+ user.contributionCount().toSvg(0.0, 2.0)
+ ).replace("*{contribution-display}", "default")
+ } else {
+ this.replace("*{contribution-display}", "none")
+ }
+ }
+
companion object {
private val maxWeight = lazy {
@@ -1040,15 +1062,19 @@ enum class PersonaType(private val weight: Double) {
}
private fun Long.toSvg(levelStartX: Double, xIncrease: Double): String {
+ return this.toSvgWithY(-1.0, levelStartX, xIncrease)
+ }
+
+ private fun Long.toSvgWithY(startY: Double, startX: Double, xIncrease: Double): String {
val level = this.toString()
- var currentX = levelStartX
+ var currentX = startX
val builder = StringBuilder()
level.withIndex().forEach {
val index = it.index
val number = it.value.digitToInt()
val numberSvg = numberSvgs[number]
- builder.append("")
+ builder.append("")
.append(numberSvg)
.append("")
diff --git a/src/main/kotlin/org/gitanimals/render/domain/User.kt b/src/main/kotlin/org/gitanimals/render/domain/User.kt
index fd48946..ec87260 100644
--- a/src/main/kotlin/org/gitanimals/render/domain/User.kt
+++ b/src/main/kotlin/org/gitanimals/render/domain/User.kt
@@ -120,12 +120,12 @@ class User(
visit += 1
}
- fun createLineAnimation(personaId: Long): String {
+ fun createLineAnimation(personaId: Long, mode: Mode): String {
val builder = StringBuilder().openLine()
val persona = personas.find { it.id!! >= personaId }
?: throw IllegalArgumentException("Cannot find persona by id \"$personaId\"")
- builder.append(persona.toSvgForce())
+ builder.append(persona.toSvgForce(mode))
return builder.closeSvg()
}
@@ -139,13 +139,15 @@ class User(
.append(field.fillBackground())
personas.asSequence()
- .forEach { builder.append(it.toSvg()) }
+ .forEach { builder.append(it.toSvg(Mode.FARM)) }
return builder.append(field.loadComponent(name, contributions.totalCount()))
.append(field.drawBorder())
.closeSvg()
}
+ fun contributionCount(): Long = contributions.totalCount()
+
private fun List.totalCount(): Long {
var totalCount = 0L
this.forEach { totalCount += it.contribution }
diff --git a/src/main/kotlin/org/gitanimals/render/domain/UserService.kt b/src/main/kotlin/org/gitanimals/render/domain/UserService.kt
index 66df64e..006c315 100644
--- a/src/main/kotlin/org/gitanimals/render/domain/UserService.kt
+++ b/src/main/kotlin/org/gitanimals/render/domain/UserService.kt
@@ -17,8 +17,8 @@ class UserService(
return getUserByName(username).createFarmAnimation()
}
- fun getLineAnimationByUsername(username: String, personaId: Long): String {
- return getUserByName(username).createLineAnimation(personaId)
+ fun getLineAnimationByUsername(username: String, personaId: Long, mode: Mode): String {
+ return getUserByName(username).createLineAnimation(personaId, mode)
}
@Retryable(retryFor = [ObjectOptimisticLockingFailureException::class], maxAttempts = 10)
diff --git a/src/main/resources/persona/animal/flamingo.svg b/src/main/resources/persona/animal/flamingo.svg
index 60f0b67..1a22fe8 100644
--- a/src/main/resources/persona/animal/flamingo.svg
+++ b/src/main/resources/persona/animal/flamingo.svg
@@ -23,6 +23,17 @@
+
+
+
+