Skip to content

Commit

Permalink
Merge branch 'develop' into feature/#538-member-profile-image-change
Browse files Browse the repository at this point in the history
# Conflicts:
#	android/Staccato_AN/app/src/main/java/com/on/staccato/presentation/memorycreation/viewmodel/MemoryCreationViewModel.kt
#	android/Staccato_AN/app/src/main/java/com/on/staccato/presentation/memoryupdate/viewmodel/MemoryUpdateViewModel.kt
#	android/Staccato_AN/app/src/main/java/com/on/staccato/presentation/staccatocreation/viewmodel/StaccatoCreationViewModel.kt
#	android/Staccato_AN/app/src/main/java/com/on/staccato/presentation/staccatoupdate/viewmodel/StaccatoUpdateViewModel.kt
  • Loading branch information
s6m1n committed Jan 31, 2025
2 parents 51757e7 + 76848c4 commit ea3f407
Show file tree
Hide file tree
Showing 106 changed files with 3,102 additions and 984 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/backend-ci-cd-dev.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
name: Backend CI/CD dev

on:
push:
paths: [ 'backend/**', '.github/**' ]
branches: [ "develop-be", "develop" ]
workflow_dispatch:

jobs:
ci:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Backend CI/CD multi prod
name: Backend CI/CD multi dev

on:
push:
Expand Down
19 changes: 19 additions & 0 deletions android/Staccato_AN/app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import java.io.FileInputStream
import java.util.Properties

Expand All @@ -20,6 +21,7 @@ plugins {
alias(libs.plugins.firebaseCrashlytics)
alias(libs.plugins.mapsplatformSecretsGradlePlugin)
alias(libs.plugins.hiltAndroid)
alias(libs.plugins.androidJunit5)
}

android {
Expand All @@ -34,6 +36,8 @@ android {
versionName = "1.2.1"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments["runnerBuilder"] =
"de.mannodermaus.junit5.AndroidJUnit5Builder"

buildConfigField("String", "TOKEN", "${localProperties["token"]}")
}
Expand Down Expand Up @@ -102,6 +106,13 @@ dependencies {
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)

// JUnit5
testImplementation(libs.junit.jupiter)
testRuntimeOnly(libs.junit.vintage.engine)

// AssertJ
testImplementation(libs.assertj.core)

// Glide
implementation(libs.glide)

Expand All @@ -118,6 +129,7 @@ dependencies {

// OkHttp
implementation(libs.okhttp.logging.interceptor)
testImplementation(libs.okhttp.mockwebserver)

// Lifecycle
implementation(libs.lifecycle.viewmodel)
Expand Down Expand Up @@ -188,3 +200,10 @@ secrets {
ignoreList.add("keyToIgnore")
ignoreList.add("sdk.*")
}

tasks.withType<Test> {
testLogging {
events("started", "passed", "skipped", "failed", "standardError", "standardOut")
exceptionFormat = TestExceptionFormat.FULL
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ package com.on.staccato
import android.app.Application
import com.google.android.libraries.places.api.net.PlacesClient
import com.on.staccato.data.PlacesClientProvider
import com.on.staccato.data.StaccatoClient
import com.on.staccato.data.UserInfoPreferencesManager
import dagger.hilt.android.HiltAndroidApp
import retrofit2.Retrofit

@HiltAndroidApp
class StaccatoApplication : Application() {
override fun onCreate() {
super.onCreate()
// AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
retrofit = StaccatoClient.initialize()
userInfoPrefsManager = UserInfoPreferencesManager(applicationContext)
placesClient = PlacesClientProvider.getClient(this)
}

companion object {
lateinit var retrofit: Retrofit
lateinit var userInfoPrefsManager: UserInfoPreferencesManager
lateinit var placesClient: PlacesClient
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.on.staccato.data

import com.on.staccato.data.dto.Status

sealed interface ApiResult<T : Any>

class Success<T : Any>(val data: T) : ApiResult<T>

class ServerError<T : Any>(val status: Status, val message: String) : ApiResult<T>

sealed class Exception<T : Any> : ApiResult<T> {
class NetworkError<T : Any> : Exception<T>()

class UnknownError<T : Any> : Exception<T>()
}

inline fun <T : Any, R : Any> ApiResult<T>.handle(convert: (T) -> R): ApiResult<R> =
when (this) {
is Exception.NetworkError -> Exception.NetworkError()
is Exception.UnknownError -> Exception.UnknownError()
is ServerError -> ServerError(status, message)
is Success -> Success(convert(data))
}

fun ApiResult<Unit>.handle(): ApiResult<Unit> =
when (this) {
is Exception.NetworkError -> Exception.NetworkError()
is Exception.UnknownError -> Exception.UnknownError()
is ServerError -> ServerError(status, message)
is Success -> Success(data)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package com.on.staccato.data

import com.on.staccato.StaccatoApplication.Companion.retrofit
import com.on.staccato.data.StaccatoClient.getErrorResponse
import com.on.staccato.data.dto.ErrorResponse
import com.on.staccato.data.dto.Status
import okhttp3.Request
import okhttp3.ResponseBody
import okio.Timeout
import retrofit2.Call
import retrofit2.HttpException
import retrofit2.Response
import java.io.IOException

class ApiResultCall<T : Any>(
private val delegate: Call<T>,
) : Call<ApiResult<T>> {
override fun enqueue(callback: retrofit2.Callback<ApiResult<T>>) {
delegate.enqueue(
object : retrofit2.Callback<T> {
override fun onResponse(
call: Call<T>,
response: Response<T>,
) {
val networkResult: ApiResult<T> = handleApiResponse { response }
callback.onResponse(this@ApiResultCall, Response.success(networkResult))
}

override fun onFailure(
call: Call<T>,
throwable: Throwable,
) {
val exception = handleException<T>(throwable)
callback.onResponse(this@ApiResultCall, Response.success(exception))
}
},
)
}

override fun execute(): Response<ApiResult<T>> = throw NotImplementedError()

override fun clone(): Call<ApiResult<T>> = ApiResultCall(delegate.clone())

override fun isExecuted(): Boolean = delegate.isExecuted

override fun cancel() {
delegate.cancel()
}

override fun isCanceled(): Boolean = delegate.isCanceled

override fun request(): Request = delegate.request()

override fun timeout(): Timeout = delegate.timeout()
}

private const val CREATED = 201
private const val NOT_FOUND_ERROR_BODY = "errorBody를 찾을 수 없습니다."

private fun <T : Any> handleApiResponse(execute: () -> Response<T>): ApiResult<T> {
return try {
val response: Response<T> = execute()
val body: T? = response.body()

when {
response.isSuccessful && response.code() == CREATED -> Success(body as T)
response.isSuccessful && body != null -> Success(body)
else -> {
val errorBody: ResponseBody =
response.errorBody()
?: throw IllegalArgumentException(NOT_FOUND_ERROR_BODY)
val errorResponse: ErrorResponse = retrofit.getErrorResponse(errorBody)
ServerError(
status = Status.Message(errorResponse.status),
message = errorResponse.message,
)
}
}
} catch (httpException: HttpException) {
ServerError(status = Status.Code(httpException.code()), message = httpException.message())
} catch (throwable: Throwable) {
handleException<T>(throwable)
}
}

private fun <T : Any> handleException(throwable: Throwable) =
when (throwable) {
is IOException -> Exception.NetworkError<T>()
else -> Exception.UnknownError<T>()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.on.staccato.data

import retrofit2.Call
import retrofit2.CallAdapter
import java.lang.reflect.Type

class ApiResultCallAdapter(
private val resultType: Type,
) : CallAdapter<Type, Call<ApiResult<Type>>> {
override fun responseType(): Type = resultType

override fun adapt(call: Call<Type>): Call<ApiResult<Type>> = ApiResultCall(call)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.on.staccato.data

import retrofit2.Call
import retrofit2.CallAdapter
import retrofit2.Retrofit
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type

class ApiResultCallAdapterFactory : CallAdapter.Factory() {
override fun get(
returnType: Type,
annotations: Array<out Annotation>,
retrofit: Retrofit,
): CallAdapter<*, *>? {
if (getRawType(returnType) != Call::class.java) {
return null
}

val responseType = getParameterUpperBound(0, returnType as ParameterizedType)

if (getRawType(responseType) != ApiResult::class.java) {
return null
}

val resultType = getParameterUpperBound(0, responseType as ParameterizedType)
return ApiResultCallAdapter(resultType)
}

companion object {
fun create(): ApiResultCallAdapterFactory = ApiResultCallAdapterFactory()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.on.staccato.data

import com.on.staccato.presentation.util.ExceptionState

inline fun <T : Any> ApiResult<T>.onSuccess(action: (T) -> Unit): ApiResult<T> =
apply {
if (this is Success<T>) action(data)
}

inline fun <T : Any> ApiResult<T>.onServerError(action: (message: String) -> Unit): ApiResult<T> =
apply {
if (this is ServerError<T>) action(message)
}

inline fun <T : Any> ApiResult<T>.onException(action: (exceptionState: ExceptionState) -> Unit): ApiResult<T> =
apply {
if (this is Exception<T>) {
when (this) {
is Exception.NetworkError -> action(ExceptionState.NetworkError)
is Exception.UnknownError -> action(ExceptionState.UnknownError)
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,19 @@ object StaccatoClient {

private val jsonBuilder = Json { coerceInputValues = true }

private val provideRetrofit =
fun initialize(): Retrofit =
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(provideHttpClient)
.addConverterFactory(
jsonBuilder.asConverterFactory("application/json".toMediaType()),
)
.addCallAdapterFactory(ApiResultCallAdapterFactory.create())
.build()

fun getErrorResponse(errorBody: ResponseBody): ErrorResponse {
return provideRetrofit.responseBodyConverter<ErrorResponse>(
fun Retrofit.getErrorResponse(errorBody: ResponseBody): ErrorResponse =
responseBodyConverter<ErrorResponse>(
ErrorResponse::class.java,
ErrorResponse::class.java.annotations,
).convert(errorBody) ?: throw IllegalArgumentException("errorBody를 변환할 수 없습니다.")
}

fun <T> create(service: Class<T>): T {
return provideRetrofit.create(service)
}
}
Loading

0 comments on commit ea3f407

Please sign in to comment.