Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/main/kotlin/org/gitanimals/render/app/AnimationFacade.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,19 +47,25 @@ class AnimationFacade(
}
}

fun getLineAnimation(username: String, personaId: Long, mode: Mode): String {
fun getLineAnimation(
username: String,
personaId: Long,
mode: Mode,
backgroundColor: String = "transparent",
): String {
return when (userService.existsByName(username)) {
true -> {
setUserAuthInfoIfNotSet(username)

val svgAnimation = userService.getLineAnimationByUsername(username, personaId, mode)
val svgAnimation =
userService.getLineAnimationByUsername(username, personaId, mode, backgroundColor)
eventPublisher.publishEvent(Visited(username, MDC.get(TRACE_ID)))
svgAnimation
}

false -> {
val user = createOrUpdateUser(username)
userService.getLineAnimationByUsername(user.getName(), personaId, mode)
userService.getLineAnimationByUsername(user.getName(), personaId, mode, backgroundColor)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class AnimationController(
@PathVariable("username") username: String,
@RequestParam(name = "pet-id", defaultValue = "0") personaId: String,
@RequestParam(name = "contribution-view", defaultValue = "true") contributionView: Boolean,
@RequestParam(name = "background-color", defaultValue = "transparent") backgroundColor: String,
response: HttpServletResponse,
): String {
response.cacheControl(3600)
Expand All @@ -45,6 +46,7 @@ class AnimationController(
username = username.deleteBrackets(),
personaId = personaId.trimNotDigitCharacters().toLong(),
mode = mode,
backgroundColor = backgroundColor,
)
}

Expand Down
37 changes: 33 additions & 4 deletions src/main/kotlin/org/gitanimals/render/domain/User.kt
Original file line number Diff line number Diff line change
Expand Up @@ -273,8 +273,12 @@ class User(
visit += 1
}

fun createLineAnimation(personaId: Long, mode: Mode): String {
val builder = StringBuilder().openLine()
fun createLineAnimation(
personaId: Long,
mode: Mode,
backgroundColor: String = "transparent",
): String {
val builder = StringBuilder().openLine(backgroundColor.normalizeLineBackgroundColor())

val persona = personas.find { it.id >= personaId }
?: throw IllegalArgumentException("Cannot find persona by id \"$personaId\"")
Expand All @@ -283,8 +287,33 @@ class User(
return builder.closeSvg()
}

private fun StringBuilder.openLine(): StringBuilder {
return this.append("<svg fill=\"none\" overflow=\"visible\" xmlns=\"http://www.w3.org/2000/svg\">")
private fun StringBuilder.openLine(backgroundColor: String): StringBuilder {
this.append("<svg fill=\"none\" overflow=\"visible\" xmlns=\"http://www.w3.org/2000/svg\">")

if (backgroundColor != "transparent") {
this.append("<rect width=\"100%\" height=\"100%\" fill=\"$backgroundColor\"/>")
}

return this
}

private fun String.normalizeLineBackgroundColor(): String {
val backgroundColor = trim()

if (backgroundColor.equals("transparent", ignoreCase = true)) {
return "transparent"
}

if (backgroundColor.equals("white", ignoreCase = true)) {
return "white"
}

val hexColor = backgroundColor.removePrefix("#")
require(hexColor.length in setOf(3, 4, 6, 8) && hexColor.all { it.isDigit() || it.lowercaseChar() in 'a'..'f' }) {
"background-color must be 'white', 'transparent', or a 3/4/6/8 digit hex color"
}

return "#$hexColor"
}


Expand Down
9 changes: 7 additions & 2 deletions src/main/kotlin/org/gitanimals/render/domain/UserService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ class UserService(
return getUserByName(username).createFarmAnimation()
}

fun getLineAnimationByUsername(username: String, personaId: Long, mode: Mode): String {
return getUserByName(username).createLineAnimation(personaId, mode)
fun getLineAnimationByUsername(
username: String,
personaId: Long,
mode: Mode,
backgroundColor: String = "transparent",
): String {
return getUserByName(username).createLineAnimation(personaId, mode, backgroundColor)
}

@Retryable(retryFor = [ObjectOptimisticLockingFailureException::class], maxAttempts = 10)
Expand Down
44 changes: 44 additions & 0 deletions src/test/kotlin/org/gitanimals/render/domain/UserTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,50 @@ internal class UserTest(
}
}

describe("createLineAnimation 메소드는") {
context("personaId와 line mode를 받으면") {
val personaId = 1L
val user = user().apply {
personas.add(persona(id = personaId, user = this))
}

it("배경색을 지정하지 않으면 기존처럼 투명 배경의 line SVG를 생성한다") {
val animation = user.createLineAnimation(personaId, Mode.LINE)

animation.contains("<rect width=\"100%\" height=\"100%\"") shouldBe false
animation.contains("<g id=\"cat-$personaId\"") shouldBe true
}

it("white 배경을 지정하면 흰색 배경을 포함한다") {
val animation = user.createLineAnimation(personaId, Mode.LINE, "white")

animation.startsWith(
"<svg fill=\"none\" overflow=\"visible\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"100%\" height=\"100%\" fill=\"white\"/>"
) shouldBe true
}

it("hex 배경색을 지정하면 해당 색상을 포함한다") {
val animation = user.createLineAnimation(personaId, Mode.LINE, "0d1117")

animation.startsWith(
"<svg fill=\"none\" overflow=\"visible\" xmlns=\"http://www.w3.org/2000/svg\"><rect width=\"100%\" height=\"100%\" fill=\"#0d1117\"/>"
) shouldBe true
}

it("transparent 배경을 지정하면 배경 rect를 추가하지 않는다") {
val animation = user.createLineAnimation(personaId, Mode.LINE, "transparent")

animation.contains("<rect width=\"100%\" height=\"100%\"") shouldBe false
}

it("지원하지 않는 배경색을 지정하면 예외를 던진다") {
shouldThrowExactly<IllegalArgumentException> {
user.createLineAnimation(personaId, Mode.LINE, "url(#malicious)")
}
}
}
}

describe("mergePersona 메소드는") {
context("increasePersonaId와 deletePersonaId를 받아서,") {
val user = User.newUser("devxb", mapOf())
Expand Down