Skip to content

Commit 2b24431

Browse files
authored
release: 1.9.9 (#218)
2 parents 9582725 + 564b42c commit 2b24431

19 files changed

Lines changed: 438 additions & 2 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ jobs:
8989
"INTERNAL_AUTH_SECRET=${{ secrets.INTERNAL_AUTH_SECRET }}"
9090
"LOGIN_SECRET=${{ secrets.LOGIN_SECRET }}"
9191
"GITANIMALS_ADMIN_TOKEN=${{ secrets.GITANIMALS_ADMIN_TOKEN }}"
92+
"MIXPANEL_PROJECT_TOKEN=${{ secrets.MIXPANEL_PROJECT_TOKEN }}"
9293
9394
- name: build and push filebeat
9495
uses: docker/build-push-action@v4

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ apply from: "gradle/spring.gradle"
3535
apply from: "gradle/logging.gradle"
3636
apply from: "gradle/monitor.gradle"
3737
apply from: "gradle/jetbrains.gradle"
38+
apply from: "gradle/analytics.gradle"
3839

3940
sentry {
4041
includeSourceContext = true

deploy/api/Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ ARG RELAY_APPROVE_TOKEN
2424
ARG INTERNAL_AUTH_SECRET
2525
ARG LOGIN_SECRET
2626
ARG GITANIMALS_ADMIN_TOKEN
27+
ARG MIXPANEL_PROJECT_TOKEN
2728

2829
ARG JAR_FILE=./*.jar
2930
COPY ${JAR_FILE} gitanimals-api.jar
@@ -51,7 +52,8 @@ ENV db_url=${DB_URL} \
5152
relay_approve_token=${RELAY_APPROVE_TOKEN} \
5253
internal_auth_secret=${INTERNAL_AUTH_SECRET} \
5354
login_secret=${LOGIN_SECRET} \
54-
gitanimals_admin_token=${GITANIMALS_ADMIN_TOKEN}
55+
gitanimals_admin_token=${GITANIMALS_ADMIN_TOKEN} \
56+
mixpanel_project_token=${MIXPANEL_PROJECT_TOKEN}
5557

5658
ENTRYPOINT java -Djava.net.preferIPv4Stack=true -jar gitanimals-api.jar \
5759
--spring.datasource.url=${db_url} \

gradle/analytics.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dependencies {
2+
implementation "com.mixpanel:mixpanel-java:1.5.4"
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package org.gitanimals.core.event
2+
3+
interface EventLogger {
4+
5+
/**
6+
* 분석 이벤트를 트래킹한다. 절대 예외를 throw 하지 않으며, 실패는 구현체 내부에서 로깅으로 처리된다.
7+
*/
8+
fun track(eventName: String, distinctId: String, properties: Map<String, Any?> = emptyMap())
9+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.gitanimals.core.event
2+
3+
import com.mixpanel.mixpanelapi.MessageBuilder
4+
import com.mixpanel.mixpanelapi.MixpanelAPI
5+
import org.slf4j.LoggerFactory
6+
import org.springframework.beans.factory.annotation.Value
7+
import org.springframework.context.annotation.Bean
8+
import org.springframework.context.annotation.Configuration
9+
import org.springframework.context.annotation.Profile
10+
11+
@Configuration
12+
class MixpanelConfiguration(
13+
@Value("\${mixpanel.project.token:}") private val token: String,
14+
) {
15+
16+
private val logger = LoggerFactory.getLogger(this::class.java)
17+
18+
@Bean
19+
@Profile("!test")
20+
fun mixpanelEventLogger(): EventLogger {
21+
if (token.isBlank()) {
22+
logger.warn("MIXPANEL_PROJECT_TOKEN is blank — falling back to NoOpEventLogger")
23+
return NoOpEventLogger()
24+
}
25+
return MixpanelEventLogger(MixpanelAPI(), MessageBuilder(token))
26+
}
27+
28+
@Bean
29+
@Profile("test")
30+
fun noOpEventLogger(): EventLogger = NoOpEventLogger()
31+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.gitanimals.core.event
2+
3+
import com.mixpanel.mixpanelapi.ClientDelivery
4+
import com.mixpanel.mixpanelapi.MessageBuilder
5+
import com.mixpanel.mixpanelapi.MixpanelAPI
6+
import org.gitanimals.core.GracefulShutdownDispatcher.gracefulLaunch
7+
import org.json.JSONObject
8+
import org.slf4j.LoggerFactory
9+
10+
class MixpanelEventLogger(
11+
private val mixpanelAPI: MixpanelAPI,
12+
private val messageBuilder: MessageBuilder,
13+
) : EventLogger {
14+
15+
private val logger = LoggerFactory.getLogger(this::class.java)
16+
17+
override fun track(eventName: String, distinctId: String, properties: Map<String, Any?>) {
18+
gracefulLaunch {
19+
runCatching {
20+
val props = JSONObject()
21+
properties.forEach { (k, v) -> if (v != null) props.put(k, v) }
22+
val message = messageBuilder.event(distinctId, eventName, props)
23+
val delivery = ClientDelivery()
24+
delivery.addMessage(message)
25+
mixpanelAPI.deliver(delivery)
26+
}.onFailure {
27+
logger.warn("Failed to track Mixpanel event '{}': {}", eventName, it.message)
28+
}
29+
}
30+
}
31+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.gitanimals.core.event
2+
3+
import org.slf4j.LoggerFactory
4+
5+
class NoOpEventLogger : EventLogger {
6+
7+
private val logger = LoggerFactory.getLogger(this::class.java)
8+
9+
override fun track(eventName: String, distinctId: String, properties: Map<String, Any?>) {
10+
logger.debug("NoOpEventLogger.track: event={}, distinctId={}", eventName, distinctId)
11+
}
12+
}

src/main/kotlin/org/gitanimals/gotcha/controller/GotchaController.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
package org.gitanimals.gotcha.controller
22

3+
import org.gitanimals.core.auth.InternalAuth
34
import org.gitanimals.core.auth.RequiredUserEntryPoints
45
import org.gitanimals.core.auth.UserEntryPoint
6+
import org.gitanimals.core.event.EventLogger
57
import org.gitanimals.gotcha.app.GotchaFacadeV3
68
import org.gitanimals.gotcha.app.response.GotchaResponseV3
79
import org.gitanimals.gotcha.controller.response.ErrorResponse
@@ -13,6 +15,8 @@ import org.springframework.web.bind.annotation.*
1315
@RestController
1416
class GotchaController(
1517
private val gotchaFacadeV3: GotchaFacadeV3,
18+
private val eventLogger: EventLogger,
19+
private val internalAuth: InternalAuth,
1620
) {
1721

1822
@RequiredUserEntryPoints([UserEntryPoint.GITHUB])
@@ -26,6 +30,20 @@ class GotchaController(
2630

2731
val gotchaResponses = gotchaFacadeV3.gotcha(token, gotchaType, count)
2832

33+
internalAuth.findUserId()?.let { userId ->
34+
gotchaResponses.forEach { response ->
35+
eventLogger.track(
36+
eventName = "complete_gotcha",
37+
distinctId = userId.toString(),
38+
properties = mapOf(
39+
"pet_persona" to response.name,
40+
"cost_point" to gotchaType.point,
41+
"user_id" to userId,
42+
),
43+
)
44+
}
45+
}
46+
2947
return mapOf("gotchaResults" to gotchaResponses)
3048
}
3149

src/main/kotlin/org/gitanimals/identity/app/AppleLoginFacade.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.fasterxml.jackson.core.type.TypeReference
44
import com.fasterxml.jackson.databind.ObjectMapper
55
import io.jsonwebtoken.Jwts
66
import org.gitanimals.core.AUTHORIZATION_EXCEPTION
7+
import org.gitanimals.core.event.EventLogger
78
import org.gitanimals.identity.app.AppleOauth2Api.AppleAuthKeyResponse
89
import org.gitanimals.identity.domain.EntryPoint
910
import org.gitanimals.identity.domain.UserService
@@ -23,6 +24,7 @@ class AppleLoginFacade(
2324
private val appleOauth2Api: AppleOauth2Api,
2425
private val objectMapper: ObjectMapper,
2526
@Value("\${login.secret}") private val loginSecret: String,
27+
private val eventLogger: EventLogger,
2628
) {
2729

2830
private val logger = LoggerFactory.getLogger(this::class.simpleName)
@@ -49,6 +51,16 @@ class AppleLoginFacade(
4951
}
5052
}
5153

54+
eventLogger.track(
55+
eventName = "complete_login",
56+
distinctId = user.id.toString(),
57+
properties = mapOf(
58+
"provider" to "apple",
59+
"user_id" to user.id,
60+
"is_new_user" to !isExistsUser,
61+
),
62+
)
63+
5264
return tokenManager.createToken(user).withType()
5365
}
5466

0 commit comments

Comments
 (0)