From 1dda7852eef010d8fdc8f7cf10b76187f5143b8a Mon Sep 17 00:00:00 2001 From: kwakjoohyeong Date: Mon, 26 Feb 2024 17:13:09 +0900 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20AuthInterceptor=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DefaultTokenPreferenceDataSource.kt | 1 + .../di/PreferenceDataSourceModule.kt | 19 ++++++ core/interceptor/.gitignore | 1 + core/interceptor/build.gradle.kts | 15 +++++ core/interceptor/consumer-rules.pro | 0 core/interceptor/proguard-rules.pro | 21 ++++++ .../interceptor/ExampleInstrumentedTest.kt | 24 +++++++ core/interceptor/src/main/AndroidManifest.xml | 4 ++ .../core/interceptor/AuthInterceptor.kt | 65 +++++++++++++++++++ .../core/interceptor/ExampleUnitTest.kt | 17 +++++ .../core/network/di/AuthInterceptor.kt | 0 settings.gradle.kts | 1 + 12 files changed, 168 insertions(+) create mode 100644 core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt create mode 100644 core/interceptor/.gitignore create mode 100644 core/interceptor/build.gradle.kts create mode 100644 core/interceptor/consumer-rules.pro create mode 100644 core/interceptor/proguard-rules.pro create mode 100644 core/interceptor/src/androidTest/java/com/withpeace/withpeace/core/interceptor/ExampleInstrumentedTest.kt create mode 100644 core/interceptor/src/main/AndroidManifest.xml create mode 100644 core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt create mode 100644 core/interceptor/src/test/java/com/withpeace/withpeace/core/interceptor/ExampleUnitTest.kt create mode 100644 core/network/src/main/java/com/withpeace/withpeace/core/network/di/AuthInterceptor.kt diff --git a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/DefaultTokenPreferenceDataSource.kt b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/DefaultTokenPreferenceDataSource.kt index a1f7b504..07d3980b 100644 --- a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/DefaultTokenPreferenceDataSource.kt +++ b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/DefaultTokenPreferenceDataSource.kt @@ -5,6 +5,7 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import javax.inject.Inject import javax.inject.Named diff --git a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt new file mode 100644 index 00000000..403f48d2 --- /dev/null +++ b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt @@ -0,0 +1,19 @@ +package com.withpeace.withpeace.core.datastore.di + +import com.withpeace.withpeace.core.datastore.dataStore.DefaultTokenPreferenceDataSource +import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSource +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +interface PreferenceDataSourceModule { + @Binds + @Singleton + fun bindsTokenPreferenceDataSource( + defaultTokenPreferenceDataSource: DefaultTokenPreferenceDataSource, + ): TokenPreferenceDataSource +} \ No newline at end of file diff --git a/core/interceptor/.gitignore b/core/interceptor/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/core/interceptor/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/core/interceptor/build.gradle.kts b/core/interceptor/build.gradle.kts new file mode 100644 index 00000000..fd32aed5 --- /dev/null +++ b/core/interceptor/build.gradle.kts @@ -0,0 +1,15 @@ +plugins { + id("com.android.library") + id("convention.android.base") + id("convention.android.hilt") +} + +android { + namespace = "com.withpeace.withpeace.core.interceptor" +} + +dependencies { + implementation(libs.retrofit.core) + implementation(libs.okhttp.logging) + implementation(project(":core:datastore")) +} \ No newline at end of file diff --git a/core/interceptor/consumer-rules.pro b/core/interceptor/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/core/interceptor/proguard-rules.pro b/core/interceptor/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/core/interceptor/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/core/interceptor/src/androidTest/java/com/withpeace/withpeace/core/interceptor/ExampleInstrumentedTest.kt b/core/interceptor/src/androidTest/java/com/withpeace/withpeace/core/interceptor/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..27dda136 --- /dev/null +++ b/core/interceptor/src/androidTest/java/com/withpeace/withpeace/core/interceptor/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.withpeace.withpeace.core.interceptor + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.withpeace.withpeace.core.interceptor.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/core/interceptor/src/main/AndroidManifest.xml b/core/interceptor/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/core/interceptor/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt b/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt new file mode 100644 index 00000000..737ef641 --- /dev/null +++ b/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt @@ -0,0 +1,65 @@ +package com.withpeace.withpeace.core.interceptor + +import android.content.Context +import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSource +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.android.EntryPointAccessors +import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.runBlocking +import okhttp3.Interceptor +import okhttp3.Request +import okhttp3.Response + +class AuthInterceptor(private val context: Context) : Interceptor { + private val tokenPreferenceDataSource = EntryPointAccessors + .fromApplication(context) + .getTokenPreferenceDataSource() + + override fun intercept(chain: Interceptor.Chain): Response = runBlocking { + val accessToken = tokenPreferenceDataSource.accessToken.firstOrNull() + val tokenAddedRequest = chain.request().putAccessToken(accessToken) + val response = chain.proceed(tokenAddedRequest) + + if (response.isAccessTokenInvalid()) { + val refreshToken = tokenPreferenceDataSource.refreshToken.firstOrNull() + if (refreshToken == null /*|| 유효하지 않을 때*/) { + // 로그인 화면 + } else { + // refresh API 호출 + // API 실패시 로그인 화면 이동 + // 성공시 토큰을 넣어준다. + } + // refresh API 호출 + } + // 401시에 + response + } + + private fun Response.isAccessTokenInvalid(): Boolean = (code == 401) + private fun Request.putAccessToken(token: String?): Request = + putHeader(ACCESS_TOKEN_HEADER, ACCESS_TOKEN_FORMAT.format(token)) + + private fun Request.putHeader( + key: String, + value: String, + ): Request = newBuilder().addHeader(key, value).build() + + private fun navigateToLogin() { +// val loginStartIntent = Intent(context, LoginActivity::class.java) +// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) +// context.startActivity(loginStartIntent) + } + + @EntryPoint + @InstallIn(SingletonComponent::class) + interface AuthInterceptorEntryPoint { + fun getTokenPreferenceDataSource(): TokenPreferenceDataSource + } + + companion object { + private const val ACCESS_TOKEN_HEADER = "Authorization" + private const val ACCESS_TOKEN_FORMAT = "Bearer %s" + } +} \ No newline at end of file diff --git a/core/interceptor/src/test/java/com/withpeace/withpeace/core/interceptor/ExampleUnitTest.kt b/core/interceptor/src/test/java/com/withpeace/withpeace/core/interceptor/ExampleUnitTest.kt new file mode 100644 index 00000000..b1acf7a0 --- /dev/null +++ b/core/interceptor/src/test/java/com/withpeace/withpeace/core/interceptor/ExampleUnitTest.kt @@ -0,0 +1,17 @@ +package com.withpeace.withpeace.core.interceptor + +import org.junit.Test + +import org.junit.Assert.* + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/AuthInterceptor.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/AuthInterceptor.kt new file mode 100644 index 00000000..e69de29b diff --git a/settings.gradle.kts b/settings.gradle.kts index 35250bdc..7ae33a31 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,3 +22,4 @@ include(":core:network") include(":core:data") include(":core:domain") include(":core:datastore") +include(":core:interceptor") From 0c656506af3f8abc09c33947731f3eff1b84105d Mon Sep 17 00:00:00 2001 From: kwakjoohyeong Date: Mon, 26 Feb 2024 17:16:41 +0900 Subject: [PATCH 02/10] =?UTF-8?q?chore:=20=EC=BD=94=EB=93=9C=20=ED=8F=AC?= =?UTF-8?q?=EB=A7=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 6 +- .github/pull_request_template.md | 3 + .github/workflows/android.yml | 72 +++++++++---------- .../withpeace/ExampleInstrumentedTest.kt | 6 +- .../withpeace/WithPeaceApplication.kt | 2 +- .../com/withpeace/withpeace/ui/theme/Theme.kt | 8 +-- .../com/withpeace/withpeace/ui/theme/Type.kt | 4 +- .../withpeace/withpeace/ExampleUnitTest.kt | 3 +- .../core/data/ExampleInstrumentedTest.kt | 6 +- core/data/src/main/AndroidManifest.xml | 2 +- .../withpeace/core/data/mapper/TokenMapper.kt | 2 +- .../data/repository/DefaultTokenRepository.kt | 2 +- .../withpeace/core/data/ExampleUnitTest.kt | 3 +- .../core/datastore/ExampleInstrumentedTest.kt | 6 +- core/datastore/src/main/AndroidManifest.xml | 2 +- .../DefaultTokenPreferenceDataSource.kt | 21 +++--- .../core/datastore/di/DataStoreModule.kt | 2 +- .../core/datastore/ExampleUnitTest.kt | 3 +- .../withpeace/core/domain/model/Token.kt | 2 +- .../interceptor/ExampleInstrumentedTest.kt | 6 +- core/interceptor/src/main/AndroidManifest.xml | 2 +- .../core/interceptor/ExampleUnitTest.kt | 3 +- .../core/network/ExampleInstrumentedTest.kt | 6 +- core/network/src/main/AndroidManifest.xml | 2 +- .../core/network/di/NetworkModule.kt | 2 +- .../withpeace/core/network/ExampleUnitTest.kt | 3 +- gradle/libs.versions.toml | 2 +- 27 files changed, 84 insertions(+), 97 deletions(-) diff --git a/.editorconfig b/.editorconfig index e5586d73..77c75f79 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ [*.{kt,kts}] -ij_kotlin_allow_trailing_comma=true -ij_kotlin_allow_trailing_comma_on_call_site=true -ktlint_function_naming_ignore_when_annotated_with=Composable, Test +ij_kotlin_allow_trailing_comma = true +ij_kotlin_allow_trailing_comma_on_call_site = true +ktlint_function_naming_ignore_when_annotated_with = Composable, Test diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index ca22c10d..f7a94a9f 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,8 +1,11 @@ ## 관련 이슈번호 +
close # ## 작업 사항 +
## 기타 사항 +
diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index dacc8332..70852541 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -9,39 +9,39 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: '17' - distribution: 'temurin' - cache: gradle - - - name: Grant execute permission for gradlew - run: chmod +x gradlew - - - name: Build with Gradle - run: ./gradlew build - - - name: Access Google Client Id - env: - GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} - run: | - echo "GOOGLE_CLIENT_ID=\"$GOOGLE_CLIENT_ID\"" >> local.properties - - # Gradle 캐싱: 빌드 시간과 네트워크 통신을 줄이기 위해 의존성 패키지들을 캐싱하여 재사용 - - name: Cache Gradle packages - uses: actions/cache@v2 - with: - path: | - ~/.gradle/caches - ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/buildSrc/**/*.kt') }} - restore-keys: | - ${{ runner.os }}-gradle- - - - name: Run test - run: ./gradlew test - - - name: Run ktlint - run: ./gradlew ktlintCheck + - uses: actions/checkout@v3 + - name: set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build + + - name: Access Google Client Id + env: + GOOGLE_CLIENT_ID: ${{ secrets.GOOGLE_CLIENT_ID }} + run: | + echo "GOOGLE_CLIENT_ID=\"$GOOGLE_CLIENT_ID\"" >> local.properties + + # Gradle 캐싱: 빌드 시간과 네트워크 통신을 줄이기 위해 의존성 패키지들을 캐싱하여 재사용 + - name: Cache Gradle packages + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/buildSrc/**/*.kt') }} + restore-keys: | + ${{ runner.os }}-gradle- + + - name: Run test + run: ./gradlew test + + - name: Run ktlint + run: ./gradlew ktlintCheck diff --git a/app/src/androidTest/java/com/withpeace/withpeace/ExampleInstrumentedTest.kt b/app/src/androidTest/java/com/withpeace/withpeace/ExampleInstrumentedTest.kt index a855b0aa..fabc1c37 100644 --- a/app/src/androidTest/java/com/withpeace/withpeace/ExampleInstrumentedTest.kt +++ b/app/src/androidTest/java/com/withpeace/withpeace/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.withpeace.withpeace -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/app/src/main/java/com/withpeace/withpeace/WithPeaceApplication.kt b/app/src/main/java/com/withpeace/withpeace/WithPeaceApplication.kt index cd59d3f1..8390979d 100644 --- a/app/src/main/java/com/withpeace/withpeace/WithPeaceApplication.kt +++ b/app/src/main/java/com/withpeace/withpeace/WithPeaceApplication.kt @@ -4,5 +4,5 @@ import android.app.Application import dagger.hilt.android.HiltAndroidApp @HiltAndroidApp -class WithPeaceApplication: Application() { +class WithPeaceApplication : Application() { } \ No newline at end of file diff --git a/app/src/main/java/com/withpeace/withpeace/ui/theme/Theme.kt b/app/src/main/java/com/withpeace/withpeace/ui/theme/Theme.kt index 278371c2..1da7c3b6 100644 --- a/app/src/main/java/com/withpeace/withpeace/ui/theme/Theme.kt +++ b/app/src/main/java/com/withpeace/withpeace/ui/theme/Theme.kt @@ -18,13 +18,13 @@ import androidx.core.view.WindowCompat private val DarkColorScheme = darkColorScheme( primary = Purple80, secondary = PurpleGrey80, - tertiary = Pink80 + tertiary = Pink80, ) private val LightColorScheme = lightColorScheme( primary = Purple40, secondary = PurpleGrey40, - tertiary = Pink40 + tertiary = Pink40, /* Other default colors to override background = Color(0xFFFFFBFE), @@ -42,7 +42,7 @@ fun WithpeaceTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, - content: @Composable () -> Unit + content: @Composable () -> Unit, ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { @@ -65,6 +65,6 @@ fun WithpeaceTheme( MaterialTheme( colorScheme = colorScheme, typography = Typography, - content = content + content = content, ) } \ No newline at end of file diff --git a/app/src/main/java/com/withpeace/withpeace/ui/theme/Type.kt b/app/src/main/java/com/withpeace/withpeace/ui/theme/Type.kt index bf02778e..afec9d8a 100644 --- a/app/src/main/java/com/withpeace/withpeace/ui/theme/Type.kt +++ b/app/src/main/java/com/withpeace/withpeace/ui/theme/Type.kt @@ -13,8 +13,8 @@ val Typography = Typography( fontWeight = FontWeight.Normal, fontSize = 16.sp, lineHeight = 24.sp, - letterSpacing = 0.5.sp - ) + letterSpacing = 0.5.sp, + ), /* Other default text styles to override titleLarge = TextStyle( fontFamily = FontFamily.Default, diff --git a/app/src/test/java/com/withpeace/withpeace/ExampleUnitTest.kt b/app/src/test/java/com/withpeace/withpeace/ExampleUnitTest.kt index 7386af31..944b793d 100644 --- a/app/src/test/java/com/withpeace/withpeace/ExampleUnitTest.kt +++ b/app/src/test/java/com/withpeace/withpeace/ExampleUnitTest.kt @@ -1,9 +1,8 @@ package com.withpeace.withpeace +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). * diff --git a/core/data/src/androidTest/java/com/withpeace/withpeace/core/data/ExampleInstrumentedTest.kt b/core/data/src/androidTest/java/com/withpeace/withpeace/core/data/ExampleInstrumentedTest.kt index b3b26a3a..410df2d5 100644 --- a/core/data/src/androidTest/java/com/withpeace/withpeace/core/data/ExampleInstrumentedTest.kt +++ b/core/data/src/androidTest/java/com/withpeace/withpeace/core/data/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.withpeace.withpeace.core.data -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/core/data/src/main/AndroidManifest.xml b/core/data/src/main/AndroidManifest.xml index a5918e68..44008a43 100644 --- a/core/data/src/main/AndroidManifest.xml +++ b/core/data/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/mapper/TokenMapper.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/mapper/TokenMapper.kt index e7d7c9c2..b95516fa 100644 --- a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/mapper/TokenMapper.kt +++ b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/mapper/TokenMapper.kt @@ -6,6 +6,6 @@ import com.withpeace.withpeace.core.network.di.response.TokenResponse fun TokenResponse.toDomain(): Token { return Token( accessToken = accessToken, - refreshToken = refreshToken + refreshToken = refreshToken, ) } \ No newline at end of file diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt index c725e557..054e81dd 100644 --- a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt +++ b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt @@ -17,7 +17,7 @@ import javax.inject.Inject class DefaultTokenRepository @Inject constructor( private val tokenPreferenceDataSource: TokenPreferenceDataSource, - private val authService: AuthService + private val authService: AuthService, ) : TokenRepository { override fun getAccessToken(): Flow { diff --git a/core/data/src/test/java/com/withpeace/withpeace/core/data/ExampleUnitTest.kt b/core/data/src/test/java/com/withpeace/withpeace/core/data/ExampleUnitTest.kt index 61be3bed..2dce1955 100644 --- a/core/data/src/test/java/com/withpeace/withpeace/core/data/ExampleUnitTest.kt +++ b/core/data/src/test/java/com/withpeace/withpeace/core/data/ExampleUnitTest.kt @@ -1,9 +1,8 @@ package com.withpeace.withpeace.core.data +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). * diff --git a/core/datastore/src/androidTest/java/com/withpeace/withpeace/core/datastore/ExampleInstrumentedTest.kt b/core/datastore/src/androidTest/java/com/withpeace/withpeace/core/datastore/ExampleInstrumentedTest.kt index 70ef8ee6..1e831671 100644 --- a/core/datastore/src/androidTest/java/com/withpeace/withpeace/core/datastore/ExampleInstrumentedTest.kt +++ b/core/datastore/src/androidTest/java/com/withpeace/withpeace/core/datastore/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.withpeace.withpeace.core.datastore -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/core/datastore/src/main/AndroidManifest.xml b/core/datastore/src/main/AndroidManifest.xml index a5918e68..44008a43 100644 --- a/core/datastore/src/main/AndroidManifest.xml +++ b/core/datastore/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/DefaultTokenPreferenceDataSource.kt b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/DefaultTokenPreferenceDataSource.kt index 07d3980b..6b5cca17 100644 --- a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/DefaultTokenPreferenceDataSource.kt +++ b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/dataStore/DefaultTokenPreferenceDataSource.kt @@ -5,37 +5,36 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import javax.inject.Inject import javax.inject.Named class DefaultTokenPreferenceDataSource @Inject constructor( - @Named("auth") private val dataStore: DataStore -): TokenPreferenceDataSource { + @Named("auth") private val dataStore: DataStore, +) : TokenPreferenceDataSource { - override val accessToken: Flow = dataStore.data.map{ preferences -> + override val accessToken: Flow = dataStore.data.map { preferences -> preferences[ACCESS_TOKEN] } - override val refreshToken: Flow = dataStore.data.map{preferences -> + override val refreshToken: Flow = dataStore.data.map { preferences -> preferences[REFRESH_TOKEN] } - override suspend fun updateAccessToken(accessToken: String) { - dataStore.edit{preferences-> + override suspend fun updateAccessToken(accessToken: String) { + dataStore.edit { preferences -> preferences[ACCESS_TOKEN] = accessToken } } override suspend fun updateRefreshToken(refreshToken: String) { - dataStore.edit{preferences-> + dataStore.edit { preferences -> preferences[REFRESH_TOKEN] = refreshToken } } - companion object{ - private val ACCESS_TOKEN= stringPreferencesKey("ACCESS_TOKEN") - private val REFRESH_TOKEN= stringPreferencesKey("REFRESH_TOKEN") + companion object { + private val ACCESS_TOKEN = stringPreferencesKey("ACCESS_TOKEN") + private val REFRESH_TOKEN = stringPreferencesKey("REFRESH_TOKEN") } } \ No newline at end of file diff --git a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/DataStoreModule.kt b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/DataStoreModule.kt index d670e80e..8215224f 100644 --- a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/DataStoreModule.kt +++ b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/DataStoreModule.kt @@ -24,6 +24,6 @@ object DataStoreModule { @Singleton @Named("auth") fun providesTokenDataStore( - @ApplicationContext context: Context + @ApplicationContext context: Context, ): DataStore = context.authDataStore } \ No newline at end of file diff --git a/core/datastore/src/test/java/com/withpeace/withpeace/core/datastore/ExampleUnitTest.kt b/core/datastore/src/test/java/com/withpeace/withpeace/core/datastore/ExampleUnitTest.kt index ca1f94c0..c98a0772 100644 --- a/core/datastore/src/test/java/com/withpeace/withpeace/core/datastore/ExampleUnitTest.kt +++ b/core/datastore/src/test/java/com/withpeace/withpeace/core/datastore/ExampleUnitTest.kt @@ -1,9 +1,8 @@ package com.withpeace.withpeace.core.datastore +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). * diff --git a/core/domain/src/main/java/com/withpeace/withpeace/core/domain/model/Token.kt b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/model/Token.kt index 8fddfc8b..f8ca3553 100644 --- a/core/domain/src/main/java/com/withpeace/withpeace/core/domain/model/Token.kt +++ b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/model/Token.kt @@ -2,5 +2,5 @@ package com.withpeace.withpeace.core.domain.model data class Token( val accessToken: String, - val refreshToken: String + val refreshToken: String, ) \ No newline at end of file diff --git a/core/interceptor/src/androidTest/java/com/withpeace/withpeace/core/interceptor/ExampleInstrumentedTest.kt b/core/interceptor/src/androidTest/java/com/withpeace/withpeace/core/interceptor/ExampleInstrumentedTest.kt index 27dda136..5d4149a9 100644 --- a/core/interceptor/src/androidTest/java/com/withpeace/withpeace/core/interceptor/ExampleInstrumentedTest.kt +++ b/core/interceptor/src/androidTest/java/com/withpeace/withpeace/core/interceptor/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.withpeace.withpeace.core.interceptor -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/core/interceptor/src/main/AndroidManifest.xml b/core/interceptor/src/main/AndroidManifest.xml index a5918e68..44008a43 100644 --- a/core/interceptor/src/main/AndroidManifest.xml +++ b/core/interceptor/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/core/interceptor/src/test/java/com/withpeace/withpeace/core/interceptor/ExampleUnitTest.kt b/core/interceptor/src/test/java/com/withpeace/withpeace/core/interceptor/ExampleUnitTest.kt index b1acf7a0..03abd607 100644 --- a/core/interceptor/src/test/java/com/withpeace/withpeace/core/interceptor/ExampleUnitTest.kt +++ b/core/interceptor/src/test/java/com/withpeace/withpeace/core/interceptor/ExampleUnitTest.kt @@ -1,9 +1,8 @@ package com.withpeace.withpeace.core.interceptor +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). * diff --git a/core/network/src/androidTest/java/com/withpeace/withpeace/core/network/ExampleInstrumentedTest.kt b/core/network/src/androidTest/java/com/withpeace/withpeace/core/network/ExampleInstrumentedTest.kt index 75fdeedb..dd396ff4 100644 --- a/core/network/src/androidTest/java/com/withpeace/withpeace/core/network/ExampleInstrumentedTest.kt +++ b/core/network/src/androidTest/java/com/withpeace/withpeace/core/network/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.withpeace.withpeace.core.network -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/core/network/src/main/AndroidManifest.xml b/core/network/src/main/AndroidManifest.xml index a5918e68..44008a43 100644 --- a/core/network/src/main/AndroidManifest.xml +++ b/core/network/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt index e84b59a3..996cfab0 100644 --- a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt @@ -62,7 +62,7 @@ object NetworkModule { @Singleton fun provideRetrofitClient( okHttpClient: OkHttpClient, - converterFactory: Converter.Factory + converterFactory: Converter.Factory, ): Retrofit { return Retrofit.Builder() .client(okHttpClient) diff --git a/core/network/src/test/java/com/withpeace/withpeace/core/network/ExampleUnitTest.kt b/core/network/src/test/java/com/withpeace/withpeace/core/network/ExampleUnitTest.kt index 9b8a7dd4..3a01bec8 100644 --- a/core/network/src/test/java/com/withpeace/withpeace/core/network/ExampleUnitTest.kt +++ b/core/network/src/test/java/com/withpeace/withpeace/core/network/ExampleUnitTest.kt @@ -1,9 +1,8 @@ package com.withpeace.withpeace.core.network +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). * diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a183a317..b620c3c7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -139,7 +139,7 @@ mockk = { group = "io.mockk", name = "mockk", version.ref = "mockk" } multidex = { group = "androidx.multidex", name = "multidex", version.ref = "multidex" } -google-login = { group = "com.google.android.libraries.identity.googleid", name = "googleid", version.ref = "google-login"} +google-login = { group = "com.google.android.libraries.identity.googleid", name = "googleid", version.ref = "google-login" } # verify verify-detektFormatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } From 6462676d0a69722509434c7905f1fdce988eb54d Mon Sep 17 00:00:00 2001 From: boogiwoogi Date: Mon, 26 Feb 2024 19:18:50 +0900 Subject: [PATCH 03/10] =?UTF-8?q?feat:=20=EB=84=A4=ED=8A=B8=EC=9B=8C?= =?UTF-8?q?=ED=81=AC=20=ED=86=B5=EC=8B=A0=EC=9D=84=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=EC=85=89=ED=84=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle.kts | 5 + app/src/main/AndroidManifest.xml | 1 + .../com/withpeace/withpeace/MainActivity.kt | 18 ++++ .../main/kotlin/convention.feature.gradle.kts | 5 + .../core/data/di/DataSourceModule.kt | 18 ---- .../core/data/di/RepositoryModule.kt | 18 ++++ .../data/repository/DefaultTokenRepository.kt | 2 + .../di/PreferenceDataSourceModule.kt | 1 + core/interceptor/build.gradle.kts | 2 + .../core/interceptor/AuthInterceptor.kt | 95 ++++++++++++++----- .../core/interceptor/InterceptorModule.kt | 20 ++++ .../core/network/di/NetworkModule.kt | 19 ++-- .../core/network/di/response/TokenResponse.kt | 3 + .../core/network/di/service/AuthService.kt | 8 +- .../withpeace/feature/login/LoginScreen.kt | 1 + 15 files changed, 156 insertions(+), 60 deletions(-) delete mode 100644 core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/DataSourceModule.kt create mode 100644 core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/RepositoryModule.kt create mode 100644 core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/InterceptorModule.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8c373c05..97337c7c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -20,4 +20,9 @@ android { dependencies { implementation(project(":feature:login")) + implementation(project(":core:interceptor")) + implementation(project(":core:data")) + implementation(project(":core:network")) + implementation(project(":core:datastore")) + implementation(project(":core:domain")) } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 33e48a87..ab29bc8c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" + android:usesCleartextTraffic="true" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" diff --git a/app/src/main/java/com/withpeace/withpeace/MainActivity.kt b/app/src/main/java/com/withpeace/withpeace/MainActivity.kt index a44bd222..79104daf 100644 --- a/app/src/main/java/com/withpeace/withpeace/MainActivity.kt +++ b/app/src/main/java/com/withpeace/withpeace/MainActivity.kt @@ -1,20 +1,38 @@ package com.withpeace.withpeace import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.ui.Modifier +import androidx.lifecycle.lifecycleScope +import com.withpeace.withpeace.core.data.repository.DefaultTokenRepository +import com.withpeace.withpeace.core.domain.repository.TokenRepository import com.withpeace.withpeace.feature.login.LoginScreen import com.withpeace.withpeace.ui.theme.WithpeaceTheme import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.launch +import javax.inject.Inject @AndroidEntryPoint class MainActivity : ComponentActivity() { + + @Inject + lateinit var repository: TokenRepository + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + lifecycleScope.launch { + repository.googleLogin { + Log.e("woogi", "onCreate: hello", ) + }.collect{ + Log.d("woogi", "token: $it") + } + } + setContent { WithpeaceTheme { Surface( diff --git a/build-logic/src/main/kotlin/convention.feature.gradle.kts b/build-logic/src/main/kotlin/convention.feature.gradle.kts index 551b7340..c5bbe686 100644 --- a/build-logic/src/main/kotlin/convention.feature.gradle.kts +++ b/build-logic/src/main/kotlin/convention.feature.gradle.kts @@ -6,3 +6,8 @@ plugins { id("convention.android.compose") id("convention.android.hilt") } + +dependencies{ + implementation(project(":core:data")) + implementation(project(":core:domain")) +} \ No newline at end of file diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/DataSourceModule.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/DataSourceModule.kt deleted file mode 100644 index c2816b1e..00000000 --- a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/DataSourceModule.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.withpeace.withpeace.core.data.di - -import com.withpeace.withpeace.core.datastore.dataStore.DefaultTokenPreferenceDataSource -import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSource -import dagger.Binds -import dagger.Module -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -@Module -@InstallIn(SingletonComponent::class) -abstract class DataSourceModule { - - @Binds - @Singleton - abstract fun bindsTokenDataSource(tokenPreferenceDataSource: DefaultTokenPreferenceDataSource): TokenPreferenceDataSource -} \ No newline at end of file diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/RepositoryModule.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/RepositoryModule.kt new file mode 100644 index 00000000..9d9305a0 --- /dev/null +++ b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/RepositoryModule.kt @@ -0,0 +1,18 @@ +package com.withpeace.withpeace.core.data.di + +import com.withpeace.withpeace.core.data.repository.DefaultTokenRepository +import com.withpeace.withpeace.core.domain.repository.TokenRepository +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +interface RepositoryModule { + + @Binds + @Singleton + fun bindsTokenRepository(defaultTokenRepository: DefaultTokenRepository): TokenRepository +} \ No newline at end of file diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt index 054e81dd..aa161434 100644 --- a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt +++ b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt @@ -1,5 +1,6 @@ package com.withpeace.withpeace.core.data.repository +import android.util.Log import com.skydoves.sandwich.message import com.skydoves.sandwich.suspendMapSuccess import com.skydoves.sandwich.suspendOnError @@ -43,6 +44,7 @@ class DefaultTokenRepository @Inject constructor( updateAccessToken(data.accessToken) updateRefreshToken(data.refreshToken) }.suspendOnError { + Log.d("woogi", "googleLogin: ${this.statusCode.code}") onError(message()) } }.flowOn(Dispatchers.IO) diff --git a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt index 403f48d2..e0beaa82 100644 --- a/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt +++ b/core/datastore/src/main/java/com/withpeace/withpeace/core/datastore/di/PreferenceDataSourceModule.kt @@ -11,6 +11,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) interface PreferenceDataSourceModule { + @Binds @Singleton fun bindsTokenPreferenceDataSource( diff --git a/core/interceptor/build.gradle.kts b/core/interceptor/build.gradle.kts index fd32aed5..1967e9b0 100644 --- a/core/interceptor/build.gradle.kts +++ b/core/interceptor/build.gradle.kts @@ -9,7 +9,9 @@ android { } dependencies { + implementation(libs.kotlinx.serialization.json) implementation(libs.retrofit.core) implementation(libs.okhttp.logging) implementation(project(":core:datastore")) + implementation(project(":core:network")) } \ No newline at end of file diff --git a/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt b/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt index 737ef641..db35c079 100644 --- a/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt +++ b/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt @@ -1,55 +1,96 @@ package com.withpeace.withpeace.core.interceptor import android.content.Context +import android.util.Log import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSource +import com.withpeace.withpeace.core.network.di.response.TokenResponse import dagger.hilt.EntryPoint import dagger.hilt.InstallIn import dagger.hilt.android.EntryPointAccessors import dagger.hilt.components.SingletonComponent +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import kotlinx.serialization.json.Json import okhttp3.Interceptor +import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response -class AuthInterceptor(private val context: Context) : Interceptor { +class AuthInterceptor(context: Context) : Interceptor { + private val tokenPreferenceDataSource = EntryPointAccessors .fromApplication(context) .getTokenPreferenceDataSource() - override fun intercept(chain: Interceptor.Chain): Response = runBlocking { - val accessToken = tokenPreferenceDataSource.accessToken.firstOrNull() - val tokenAddedRequest = chain.request().putAccessToken(accessToken) - val response = chain.proceed(tokenAddedRequest) + private val client = OkHttpClient.Builder().build() + + override fun intercept(chain: Interceptor.Chain): Response { + val accessToken = runBlocking { tokenPreferenceDataSource.accessToken.firstOrNull() } + val tokenAddedRequest = chain.request() + .newBuilder() + .addHeader( + ACCESS_TOKEN_HEADER, + TOKEN_FORMAT.format(accessToken), + ).build() + var response = chain.proceed(tokenAddedRequest) + + if (response.code == 401) { + val refreshToken = runBlocking { tokenPreferenceDataSource.refreshToken.firstOrNull() } - if (response.isAccessTokenInvalid()) { - val refreshToken = tokenPreferenceDataSource.refreshToken.firstOrNull() - if (refreshToken == null /*|| 유효하지 않을 때*/) { - // 로그인 화면 + if (refreshToken == null) { + navigateToLogin() } else { - // refresh API 호출 - // API 실패시 로그인 화면 이동 - // 성공시 토큰을 넣어준다. + runCatching { + refreshAccessToken(refreshToken) + }.onSuccess { tokenResponse -> + runBlocking { + tokenPreferenceDataSource.updateAccessToken(tokenResponse.accessToken) + tokenPreferenceDataSource.updateRefreshToken(tokenResponse.refreshToken) + } + response = chain.proceed( + chain.request().newBuilder().addHeader( + ACCESS_TOKEN_HEADER, + TOKEN_FORMAT.format(tokenResponse.accessToken), + ).build() + ) + }.onFailure { + navigateToLogin() + } } - // refresh API 호출 } - // 401시에 - response + return response + } + + private fun refreshAccessToken(refreshToken: String): TokenResponse { + val response: Response = runBlocking { + withContext(Dispatchers.IO) { + client.newCall(createAccessTokenRefreshRequest(refreshToken)).execute() + } + } + if (response.isSuccessful) { + return response.toDto() + } + throw IllegalArgumentException() } - private fun Response.isAccessTokenInvalid(): Boolean = (code == 401) - private fun Request.putAccessToken(token: String?): Request = - putHeader(ACCESS_TOKEN_HEADER, ACCESS_TOKEN_FORMAT.format(token)) - private fun Request.putHeader( - key: String, - value: String, - ): Request = newBuilder().addHeader(key, value).build() + private fun createAccessTokenRefreshRequest(refreshToken: String): Request { + return Request.Builder() + .url(REFRESH_URL) + .addHeader(ACCESS_TOKEN_HEADER, TOKEN_FORMAT.format(refreshToken)) + .build() + } + + private inline fun Response.toDto(): T { + body?.let { + return Json.decodeFromString(it.string()) + } ?: throw IllegalArgumentException() + } private fun navigateToLogin() { -// val loginStartIntent = Intent(context, LoginActivity::class.java) -// .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) -// context.startActivity(loginStartIntent) + Log.e("woogi", "navigateToLogin: 로그인화면으로 이동" ) } @EntryPoint @@ -59,7 +100,9 @@ class AuthInterceptor(private val context: Context) : Interceptor { } companion object { + private const val REFRESH_URL = "http://49.50.160.170:8080/api/v1/auth/refresh" + private const val REFRESH_TOKEN_FORMAT = "ReAuthorization" private const val ACCESS_TOKEN_HEADER = "Authorization" - private const val ACCESS_TOKEN_FORMAT = "Bearer %s" + private const val TOKEN_FORMAT = "Bearer %s" } } \ No newline at end of file diff --git a/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/InterceptorModule.kt b/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/InterceptorModule.kt new file mode 100644 index 00000000..71b74afa --- /dev/null +++ b/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/InterceptorModule.kt @@ -0,0 +1,20 @@ +package com.withpeace.withpeace.core.interceptor + +import android.content.Context +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import okhttp3.Interceptor +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +object InterceptorModule { + + @Provides + @Singleton + fun provideHeaderInterceptor(@ApplicationContext context: Context): Interceptor = + AuthInterceptor(context) +} \ No newline at end of file diff --git a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt index 996cfab0..9c959aba 100644 --- a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt @@ -7,6 +7,7 @@ import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import kotlinx.serialization.json.Json +import okhttp3.Interceptor import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor @@ -39,20 +40,14 @@ object NetworkModule { } } -// @Provides -// @Singleton -// fun provideHeaderInterceptor(chain: Interceptor.Chain) { -// val requestBuilder = chain.request().newBuilder() -// var apiKey = BuildConfig.X_RIOT_TOKEN -// requestBuilder.addHeader("X-Riot-Token", apiKey) -// chain.proceed(requestBuilder.build()) -// } - @Singleton @Provides - fun provideOkhttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient { + fun provideOkhttpClient( + authInterceptor: Interceptor, + httpLoggingInterceptor: HttpLoggingInterceptor, + ): OkHttpClient { return OkHttpClient.Builder().apply { - // addInterceptor(AccessTokenInterceptor) TODO("토큰 인터셉터 할당") + addInterceptor(authInterceptor) addInterceptor(httpLoggingInterceptor) }.build() } @@ -66,7 +61,7 @@ object NetworkModule { ): Retrofit { return Retrofit.Builder() .client(okHttpClient) - .baseUrl("https://asia.api.riotgames.com/") // TODO("BaseUrl 수정") + .baseUrl("http://49.50.160.170:8080/") .addConverterFactory(converterFactory) .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) .build() diff --git a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/response/TokenResponse.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/response/TokenResponse.kt index 26350bd7..7f0c414f 100644 --- a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/response/TokenResponse.kt +++ b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/response/TokenResponse.kt @@ -1,5 +1,8 @@ package com.withpeace.withpeace.core.network.di.response +import kotlinx.serialization.Serializable + +@Serializable data class TokenResponse( val accessToken: String, val refreshToken: String, diff --git a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/AuthService.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/AuthService.kt index 546e1ed0..182c386d 100644 --- a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/AuthService.kt +++ b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/AuthService.kt @@ -9,14 +9,14 @@ import retrofit2.http.POST interface AuthService { @POST("/api/v1/auth/google") - fun googleLogin(): ApiResponse> + suspend fun googleLogin(): ApiResponse> @POST("/api/v1/auth/register") - fun signUp(@Body signUpRequest: SignUpRequest): ApiResponse> + suspend fun signUp(@Body signUpRequest: SignUpRequest): ApiResponse> @POST("/api/v1/auth/refresh") - fun refreshAccessToken(): ApiResponse> + suspend fun refreshAccessToken(): ApiResponse> @POST("/api/v1/auth/logout") - fun logout(): ApiResponse> + suspend fun logout(): ApiResponse> } \ No newline at end of file diff --git a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt index 8640a2b1..64329c52 100644 --- a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt +++ b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.viewmodel.compose.viewModel import com.withpeace.withpeace.googlelogin.GoogleLoginManager @Composable From cc87a91bd5b01f6bf287894c0c2c77bf777afded Mon Sep 17 00:00:00 2001 From: boogiwoogi Date: Mon, 26 Feb 2024 19:39:57 +0900 Subject: [PATCH 04/10] =?UTF-8?q?feat:=20=EA=B5=AC=EA=B8=80=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=EC=9D=84=20=ED=86=B5=ED=95=B4=20access=20?= =?UTF-8?q?=ED=86=A0=EA=B7=BC=EC=9D=84=20=EB=B0=9B=EC=95=84=EC=98=A4?= =?UTF-8?q?=EB=8A=94=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/withpeace/withpeace/MainActivity.kt | 8 ------ .../data/repository/DefaultTokenRepository.kt | 14 ++++++++--- .../core/domain/repository/TokenRepository.kt | 2 +- .../core/network/di/{ => di}/NetworkModule.kt | 24 +++++++++++++++--- .../core/network/di}/di/ServiceModule.kt | 11 ++++++-- .../core/network/di/service/AuthService.kt | 8 +++--- .../core/network/di/service/LoginService.kt | 16 ++++++++++++ .../withpeace/feature/login/LoginScreen.kt | 8 +++++- .../withpeace/feature/login/LoginViewModel.kt | 25 +++++++++++++++++++ 9 files changed, 94 insertions(+), 22 deletions(-) rename core/network/src/main/java/com/withpeace/withpeace/core/network/di/{ => di}/NetworkModule.kt (78%) rename core/{data/src/main/kotlin/com/withpeace/withpeace/core/data => network/src/main/java/com/withpeace/withpeace/core/network/di}/di/ServiceModule.kt (50%) create mode 100644 core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/LoginService.kt create mode 100644 feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt diff --git a/app/src/main/java/com/withpeace/withpeace/MainActivity.kt b/app/src/main/java/com/withpeace/withpeace/MainActivity.kt index 79104daf..7eb479dc 100644 --- a/app/src/main/java/com/withpeace/withpeace/MainActivity.kt +++ b/app/src/main/java/com/withpeace/withpeace/MainActivity.kt @@ -25,14 +25,6 @@ class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - lifecycleScope.launch { - repository.googleLogin { - Log.e("woogi", "onCreate: hello", ) - }.collect{ - Log.d("woogi", "token: $it") - } - } - setContent { WithpeaceTheme { Surface( diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt index aa161434..0bab7fee 100644 --- a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt +++ b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt @@ -9,6 +9,7 @@ import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSourc import com.withpeace.withpeace.core.domain.model.Token import com.withpeace.withpeace.core.domain.repository.TokenRepository import com.withpeace.withpeace.core.network.di.service.AuthService +import com.withpeace.withpeace.core.network.di.service.LoginService import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -18,6 +19,7 @@ import javax.inject.Inject class DefaultTokenRepository @Inject constructor( private val tokenPreferenceDataSource: TokenPreferenceDataSource, + private val loginService: LoginService, private val authService: AuthService, ) : TokenRepository { @@ -37,15 +39,21 @@ class DefaultTokenRepository @Inject constructor( tokenPreferenceDataSource.updateRefreshToken(refreshToken) } - override fun googleLogin(onError: (String?) -> Unit): Flow = flow { - authService.googleLogin() + override fun googleLogin( + idToken: String, + onError: (String?) -> Unit, + ): Flow = flow { + loginService.googleLogin(AUTHORIZATION_FORMAT.format(idToken)) .suspendMapSuccess { emit(data.toDomain()) updateAccessToken(data.accessToken) updateRefreshToken(data.refreshToken) }.suspendOnError { - Log.d("woogi", "googleLogin: ${this.statusCode.code}") onError(message()) } }.flowOn(Dispatchers.IO) + + companion object { + private const val AUTHORIZATION_FORMAT = "Bearer %s" + } } \ No newline at end of file diff --git a/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/TokenRepository.kt b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/TokenRepository.kt index 56f6c7f0..2bb86a67 100644 --- a/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/TokenRepository.kt +++ b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/TokenRepository.kt @@ -13,5 +13,5 @@ interface TokenRepository { suspend fun updateRefreshToken(refreshToken: String) - fun googleLogin(onError: (message: String?) -> Unit): Flow + fun googleLogin(idToken: String, onError: (message: String?) -> Unit): Flow } \ No newline at end of file diff --git a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/di/NetworkModule.kt similarity index 78% rename from core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt rename to core/network/src/main/java/com/withpeace/withpeace/core/network/di/di/NetworkModule.kt index 9c959aba..dae7fe46 100644 --- a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/NetworkModule.kt +++ b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/di/NetworkModule.kt @@ -1,4 +1,4 @@ -package com.withpeace.withpeace.core.network.di +package com.withpeace.withpeace.core.network.di.di import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory import com.skydoves.sandwich.adapters.ApiResponseCallAdapterFactory @@ -13,6 +13,7 @@ import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Converter import retrofit2.Retrofit +import javax.inject.Named import javax.inject.Singleton @Module @@ -52,10 +53,10 @@ object NetworkModule { }.build() } - + @Named("general") @Provides @Singleton - fun provideRetrofitClient( + fun provideTokenRetrofitClient( okHttpClient: OkHttpClient, converterFactory: Converter.Factory, ): Retrofit { @@ -66,4 +67,21 @@ object NetworkModule { .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) .build() } + + + /** + * todo: 네이밍 수정 + */ + @Named("initial") + @Provides + @Singleton + fun provideRetrofitClient( + converterFactory: Converter.Factory, + ): Retrofit { + return Retrofit.Builder() + .baseUrl("http://49.50.160.170:8080/") + .addConverterFactory(converterFactory) + .addCallAdapterFactory(ApiResponseCallAdapterFactory.create()) + .build() + } } \ No newline at end of file diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/ServiceModule.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/di/ServiceModule.kt similarity index 50% rename from core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/ServiceModule.kt rename to core/network/src/main/java/com/withpeace/withpeace/core/network/di/di/ServiceModule.kt index 1b2f6fb1..4c598414 100644 --- a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/di/ServiceModule.kt +++ b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/di/ServiceModule.kt @@ -1,11 +1,13 @@ -package com.withpeace.withpeace.core.data.di +package com.withpeace.withpeace.core.network.di.di import com.withpeace.withpeace.core.network.di.service.AuthService +import com.withpeace.withpeace.core.network.di.service.LoginService import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import retrofit2.Retrofit +import javax.inject.Named import javax.inject.Singleton @Module @@ -14,6 +16,11 @@ object ServiceModule { @Provides @Singleton - fun providesAuthService(retrofit: Retrofit): AuthService = + fun providesAuthService(@Named("general") retrofit: Retrofit): AuthService = retrofit.create(AuthService::class.java) + + @Provides + @Singleton + fun providesLoginService(@Named("initial") retrofit: Retrofit): LoginService = + retrofit.create(LoginService::class.java) } \ No newline at end of file diff --git a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/AuthService.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/AuthService.kt index 182c386d..fffa79ad 100644 --- a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/AuthService.kt +++ b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/AuthService.kt @@ -5,14 +5,14 @@ import com.withpeace.withpeace.core.network.di.request.SignUpRequest import com.withpeace.withpeace.core.network.di.response.BaseResponse import com.withpeace.withpeace.core.network.di.response.TokenResponse import retrofit2.http.Body +import retrofit2.http.Header import retrofit2.http.POST interface AuthService { - @POST("/api/v1/auth/google") - suspend fun googleLogin(): ApiResponse> - @POST("/api/v1/auth/register") - suspend fun signUp(@Body signUpRequest: SignUpRequest): ApiResponse> + suspend fun signUp( + @Body signUpRequest: SignUpRequest, + ): ApiResponse> @POST("/api/v1/auth/refresh") suspend fun refreshAccessToken(): ApiResponse> diff --git a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/LoginService.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/LoginService.kt new file mode 100644 index 00000000..deee313d --- /dev/null +++ b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/service/LoginService.kt @@ -0,0 +1,16 @@ +package com.withpeace.withpeace.core.network.di.service + +import com.skydoves.sandwich.ApiResponse +import com.withpeace.withpeace.core.network.di.response.BaseResponse +import com.withpeace.withpeace.core.network.di.response.TokenResponse +import retrofit2.http.Header +import retrofit2.http.POST + +interface LoginService { + + @POST("/api/v1/auth/google") + suspend fun googleLogin( + @Header("Authorization") + idToken: String, + ): ApiResponse> +} \ No newline at end of file diff --git a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt index 64329c52..c7c07eed 100644 --- a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt +++ b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt @@ -11,11 +11,14 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview +import androidx.lifecycle.lifecycleScope import androidx.lifecycle.viewmodel.compose.viewModel import com.withpeace.withpeace.googlelogin.GoogleLoginManager +import kotlinx.coroutines.launch @Composable fun LoginScreen() { + val viewModel: LoginViewModel = viewModel() val coroutineScope = rememberCoroutineScope() val googleLoginManager = GoogleLoginManager(LocalContext.current) Surface(modifier = Modifier.fillMaxSize()) { @@ -24,7 +27,10 @@ fun LoginScreen() { onClick = { googleLoginManager.startLogin( coroutineScope, - onSuccessLogin = { Log.d("Wooseok", it) }, + onSuccessLogin = { + Log.d("woogi", "idToken: $it") + viewModel.googleLogin(it) + }, onFailLogin = { Log.d("wooseok", it.toString()) }, ) }, diff --git a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt new file mode 100644 index 00000000..a1524a62 --- /dev/null +++ b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt @@ -0,0 +1,25 @@ +package com.withpeace.withpeace.feature.login + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.withpeace.withpeace.core.domain.repository.TokenRepository +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class LoginViewModel @Inject constructor( + private val tokenRepository: TokenRepository +): ViewModel() { + + fun googleLogin(idToken: String){ + viewModelScope.launch { + tokenRepository.googleLogin(idToken){ + Log.e("woogi", "googleLogin: 로그인 안됨", ) + }.collect{ + Log.d("woogi", "googleLogin: $it") + } + } + } +} \ No newline at end of file From 1a1ee2db846c244e5456207666bdff5295971153 Mon Sep 17 00:00:00 2001 From: boogiwoogi Date: Mon, 26 Feb 2024 19:52:07 +0900 Subject: [PATCH 05/10] =?UTF-8?q?feat:=20signUP=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/DefaultTokenRepository.kt | 11 ++++++ .../core/domain/repository/TokenRepository.kt | 2 + .../core/network/di/request/SignUpRequest.kt | 3 ++ .../withpeace/feature/login/LoginScreen.kt | 39 +++++++++++-------- .../withpeace/feature/login/LoginViewModel.kt | 26 +++++++++---- 5 files changed, 58 insertions(+), 23 deletions(-) diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt index 0bab7fee..58ac39e4 100644 --- a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt +++ b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt @@ -8,6 +8,7 @@ import com.withpeace.withpeace.core.data.mapper.toDomain import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSource import com.withpeace.withpeace.core.domain.model.Token import com.withpeace.withpeace.core.domain.repository.TokenRepository +import com.withpeace.withpeace.core.network.di.request.SignUpRequest import com.withpeace.withpeace.core.network.di.service.AuthService import com.withpeace.withpeace.core.network.di.service.LoginService import kotlinx.coroutines.Dispatchers @@ -39,6 +40,16 @@ class DefaultTokenRepository @Inject constructor( tokenPreferenceDataSource.updateRefreshToken(refreshToken) } + override suspend fun signUp(email: String, nickname: String, deviceToken: String?) { + authService.signUp( + SignUpRequest( + email = email, + nickname = nickname, + deviceToken = deviceToken, + ) + ) + } + override fun googleLogin( idToken: String, onError: (String?) -> Unit, diff --git a/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/TokenRepository.kt b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/TokenRepository.kt index 2bb86a67..893b0953 100644 --- a/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/TokenRepository.kt +++ b/core/domain/src/main/java/com/withpeace/withpeace/core/domain/repository/TokenRepository.kt @@ -13,5 +13,7 @@ interface TokenRepository { suspend fun updateRefreshToken(refreshToken: String) + suspend fun signUp(email: String, nickname: String, deviceToken: String?) + fun googleLogin(idToken: String, onError: (message: String?) -> Unit): Flow } \ No newline at end of file diff --git a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/request/SignUpRequest.kt b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/request/SignUpRequest.kt index af72b92d..d69487fb 100644 --- a/core/network/src/main/java/com/withpeace/withpeace/core/network/di/request/SignUpRequest.kt +++ b/core/network/src/main/java/com/withpeace/withpeace/core/network/di/request/SignUpRequest.kt @@ -1,5 +1,8 @@ package com.withpeace.withpeace.core.network.di.request +import kotlinx.serialization.Serializable + +@Serializable data class SignUpRequest( val email: String, val nickname: String, diff --git a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt index c7c07eed..8cc21785 100644 --- a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt +++ b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginScreen.kt @@ -2,6 +2,8 @@ package com.withpeace.withpeace.feature.login import android.util.Log import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.Button import androidx.compose.material3.Surface @@ -21,22 +23,27 @@ fun LoginScreen() { val viewModel: LoginViewModel = viewModel() val coroutineScope = rememberCoroutineScope() val googleLoginManager = GoogleLoginManager(LocalContext.current) - Surface(modifier = Modifier.fillMaxSize()) { - Box { - Button( - onClick = { - googleLoginManager.startLogin( - coroutineScope, - onSuccessLogin = { - Log.d("woogi", "idToken: $it") - viewModel.googleLogin(it) - }, - onFailLogin = { Log.d("wooseok", it.toString()) }, - ) - }, - ) { - Text(text = "Login") - } + Row(modifier = Modifier.fillMaxSize()) { + Button( + onClick = { + googleLoginManager.startLogin( + coroutineScope, + onSuccessLogin = { + Log.d("woogi", "idToken: $it") + viewModel.googleLogin(it) + }, + onFailLogin = { Log.d("wooseok", it.toString()) }, + ) + }, + ) { + Text(text = "Login") + } + Button( + onClick = { + viewModel.signUp() + }, + ) { + Text(text = "signup") } } } diff --git a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt index a1524a62..677f1db2 100644 --- a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt +++ b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt @@ -10,16 +10,28 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( - private val tokenRepository: TokenRepository -): ViewModel() { + private val tokenRepository: TokenRepository, +) : ViewModel() { - fun googleLogin(idToken: String){ + + fun googleLogin(idToken: String) { viewModelScope.launch { - tokenRepository.googleLogin(idToken){ - Log.e("woogi", "googleLogin: 로그인 안됨", ) - }.collect{ - Log.d("woogi", "googleLogin: $it") + tokenRepository.googleLogin(idToken) { + Log.e("woogi", "googleLogin: 로그인 안됨") + }.collect { token -> + tokenRepository.updateAccessToken(token.accessToken) + tokenRepository.updateRefreshToken(token.refreshToken) } } } + + fun signUp() { + viewModelScope.launch { + tokenRepository.signUp( + email = "email", + nickname = "asdas", + deviceToken = null, + ) + } + } } \ No newline at end of file From f3cd72ddc6289b16bf99c72dfdfb50e8f428469e Mon Sep 17 00:00:00 2001 From: chws0508 Date: Tue, 27 Feb 2024 20:00:21 +0900 Subject: [PATCH 06/10] =?UTF-8?q?feat=20:=20navigateToLogin=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/interceptor/AuthInterceptor.kt | 60 ++++++++----------- .../withpeace/feature/login/LoginViewModel.kt | 42 ++++++------- 2 files changed, 47 insertions(+), 55 deletions(-) diff --git a/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt b/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt index db35c079..cb211e25 100644 --- a/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt +++ b/core/interceptor/src/main/java/com/withpeace/withpeace/core/interceptor/AuthInterceptor.kt @@ -1,7 +1,6 @@ package com.withpeace.withpeace.core.interceptor import android.content.Context -import android.util.Log import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSource import com.withpeace.withpeace.core.network.di.response.TokenResponse import dagger.hilt.EntryPoint @@ -19,29 +18,27 @@ import okhttp3.Request import okhttp3.Response class AuthInterceptor(context: Context) : Interceptor { - - private val tokenPreferenceDataSource = EntryPointAccessors - .fromApplication(context) - .getTokenPreferenceDataSource() + private val tokenPreferenceDataSource = + EntryPointAccessors + .fromApplication(context) + .getTokenPreferenceDataSource() private val client = OkHttpClient.Builder().build() override fun intercept(chain: Interceptor.Chain): Response { val accessToken = runBlocking { tokenPreferenceDataSource.accessToken.firstOrNull() } - val tokenAddedRequest = chain.request() - .newBuilder() - .addHeader( - ACCESS_TOKEN_HEADER, - TOKEN_FORMAT.format(accessToken), - ).build() + val tokenAddedRequest = + chain.request() + .newBuilder() + .addHeader( + ACCESS_TOKEN_HEADER, + TOKEN_FORMAT.format(accessToken), + ).build() var response = chain.proceed(tokenAddedRequest) if (response.code == 401) { val refreshToken = runBlocking { tokenPreferenceDataSource.refreshToken.firstOrNull() } - - if (refreshToken == null) { - navigateToLogin() - } else { + if (refreshToken != null) { runCatching { refreshAccessToken(refreshToken) }.onSuccess { tokenResponse -> @@ -49,14 +46,13 @@ class AuthInterceptor(context: Context) : Interceptor { tokenPreferenceDataSource.updateAccessToken(tokenResponse.accessToken) tokenPreferenceDataSource.updateRefreshToken(tokenResponse.refreshToken) } - response = chain.proceed( - chain.request().newBuilder().addHeader( - ACCESS_TOKEN_HEADER, - TOKEN_FORMAT.format(tokenResponse.accessToken), - ).build() - ) - }.onFailure { - navigateToLogin() + response = + chain.proceed( + chain.request().newBuilder().addHeader( + ACCESS_TOKEN_HEADER, + TOKEN_FORMAT.format(tokenResponse.accessToken), + ).build(), + ) } } } @@ -64,22 +60,22 @@ class AuthInterceptor(context: Context) : Interceptor { } private fun refreshAccessToken(refreshToken: String): TokenResponse { - val response: Response = runBlocking { - withContext(Dispatchers.IO) { - client.newCall(createAccessTokenRefreshRequest(refreshToken)).execute() + val response: Response = + runBlocking { + withContext(Dispatchers.IO) { + client.newCall(createAccessTokenRefreshRequest(refreshToken)).execute() + } } - } if (response.isSuccessful) { return response.toDto() } throw IllegalArgumentException() } - private fun createAccessTokenRefreshRequest(refreshToken: String): Request { return Request.Builder() .url(REFRESH_URL) - .addHeader(ACCESS_TOKEN_HEADER, TOKEN_FORMAT.format(refreshToken)) + .addHeader(REFRESH_TOKEN_FORMAT, TOKEN_FORMAT.format(refreshToken)) .build() } @@ -89,10 +85,6 @@ class AuthInterceptor(context: Context) : Interceptor { } ?: throw IllegalArgumentException() } - private fun navigateToLogin() { - Log.e("woogi", "navigateToLogin: 로그인화면으로 이동" ) - } - @EntryPoint @InstallIn(SingletonComponent::class) interface AuthInterceptorEntryPoint { @@ -105,4 +97,4 @@ class AuthInterceptor(context: Context) : Interceptor { private const val ACCESS_TOKEN_HEADER = "Authorization" private const val TOKEN_FORMAT = "Bearer %s" } -} \ No newline at end of file +} diff --git a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt index 677f1db2..fbcb6caf 100644 --- a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt +++ b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt @@ -9,29 +9,29 @@ import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class LoginViewModel @Inject constructor( - private val tokenRepository: TokenRepository, -) : ViewModel() { - - - fun googleLogin(idToken: String) { - viewModelScope.launch { - tokenRepository.googleLogin(idToken) { - Log.e("woogi", "googleLogin: 로그인 안됨") - }.collect { token -> - tokenRepository.updateAccessToken(token.accessToken) - tokenRepository.updateRefreshToken(token.refreshToken) +class LoginViewModel + @Inject + constructor( + private val tokenRepository: TokenRepository, + ) : ViewModel() { + fun googleLogin(idToken: String) { + viewModelScope.launch { + tokenRepository.googleLogin(idToken) { + Log.e("woogi", "googleLogin: 로그인 안됨") + }.collect { token -> + tokenRepository.updateAccessToken(token.accessToken) + tokenRepository.updateRefreshToken(token.refreshToken) + } } } - } - fun signUp() { - viewModelScope.launch { - tokenRepository.signUp( - email = "email", - nickname = "asdas", - deviceToken = null, - ) + fun signUp() { + viewModelScope.launch { + tokenRepository.signUp( + email = "wooseok", + nickname = "haha", + deviceToken = null, + ) + } } } -} \ No newline at end of file From 83e8e8e3c71875f3ed88a9ea320f74f189a759a7 Mon Sep 17 00:00:00 2001 From: chws0508 Date: Wed, 28 Feb 2024 01:44:43 +0900 Subject: [PATCH 07/10] =?UTF-8?q?feat=20:=20=ED=85=8C=EB=A7=88=20=ED=8C=A8?= =?UTF-8?q?=ED=82=A4=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/com/withpeace/withpeace/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/withpeace/withpeace/MainActivity.kt b/app/src/main/java/com/withpeace/withpeace/MainActivity.kt index a44bd222..ee7d4113 100644 --- a/app/src/main/java/com/withpeace/withpeace/MainActivity.kt +++ b/app/src/main/java/com/withpeace/withpeace/MainActivity.kt @@ -7,8 +7,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.ui.Modifier +import com.withpeace.withpeace.core.designsystem.theme.WithpeaceTheme import com.withpeace.withpeace.feature.login.LoginScreen -import com.withpeace.withpeace.ui.theme.WithpeaceTheme import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint From c284c4cc44ea08bfe4e6955032212fa1d2170ef2 Mon Sep 17 00:00:00 2001 From: chws0508 Date: Wed, 28 Feb 2024 01:50:20 +0900 Subject: [PATCH 08/10] =?UTF-8?q?feat=20:=20suspendOnError=EB=A5=BC=20susp?= =?UTF-8?q?endOnFailure=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/repository/DefaultTokenRepository.kt | 96 ++++++++++--------- 1 file changed, 50 insertions(+), 46 deletions(-) diff --git a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt index 58ac39e4..e853f8b3 100644 --- a/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt +++ b/core/data/src/main/kotlin/com/withpeace/withpeace/core/data/repository/DefaultTokenRepository.kt @@ -1,9 +1,8 @@ package com.withpeace.withpeace.core.data.repository -import android.util.Log -import com.skydoves.sandwich.message +import com.skydoves.sandwich.messageOrNull import com.skydoves.sandwich.suspendMapSuccess -import com.skydoves.sandwich.suspendOnError +import com.skydoves.sandwich.suspendOnFailure import com.withpeace.withpeace.core.data.mapper.toDomain import com.withpeace.withpeace.core.datastore.dataStore.TokenPreferenceDataSource import com.withpeace.withpeace.core.domain.model.Token @@ -17,54 +16,59 @@ import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn import javax.inject.Inject +class DefaultTokenRepository + @Inject + constructor( + private val tokenPreferenceDataSource: TokenPreferenceDataSource, + private val loginService: LoginService, + private val authService: AuthService, + ) : TokenRepository { + override fun getAccessToken(): Flow { + return tokenPreferenceDataSource.accessToken + } -class DefaultTokenRepository @Inject constructor( - private val tokenPreferenceDataSource: TokenPreferenceDataSource, - private val loginService: LoginService, - private val authService: AuthService, -) : TokenRepository { + override fun getRefreshToken(): Flow { + return tokenPreferenceDataSource.refreshToken + } - override fun getAccessToken(): Flow { - return tokenPreferenceDataSource.accessToken - } - - override fun getRefreshToken(): Flow { - return tokenPreferenceDataSource.refreshToken - } + override suspend fun updateAccessToken(accessToken: String) { + tokenPreferenceDataSource.updateAccessToken(accessToken) + } - override suspend fun updateAccessToken(accessToken: String) { - tokenPreferenceDataSource.updateAccessToken(accessToken) - } + override suspend fun updateRefreshToken(refreshToken: String) { + tokenPreferenceDataSource.updateRefreshToken(refreshToken) + } - override suspend fun updateRefreshToken(refreshToken: String) { - tokenPreferenceDataSource.updateRefreshToken(refreshToken) - } - - override suspend fun signUp(email: String, nickname: String, deviceToken: String?) { - authService.signUp( - SignUpRequest( - email = email, - nickname = nickname, - deviceToken = deviceToken, + override suspend fun signUp( + email: String, + nickname: String, + deviceToken: String?, + ) { + authService.signUp( + SignUpRequest( + email = email, + nickname = nickname, + deviceToken = deviceToken, + ), ) - ) - } + } - override fun googleLogin( - idToken: String, - onError: (String?) -> Unit, - ): Flow = flow { - loginService.googleLogin(AUTHORIZATION_FORMAT.format(idToken)) - .suspendMapSuccess { - emit(data.toDomain()) - updateAccessToken(data.accessToken) - updateRefreshToken(data.refreshToken) - }.suspendOnError { - onError(message()) - } - }.flowOn(Dispatchers.IO) + override fun googleLogin( + idToken: String, + onError: (String?) -> Unit, + ): Flow = + flow { + loginService.googleLogin(AUTHORIZATION_FORMAT.format(idToken)) + .suspendMapSuccess { + emit(data.toDomain()) + updateAccessToken(data.accessToken) + updateRefreshToken(data.refreshToken) + }.suspendOnFailure { + onError(messageOrNull) + } + }.flowOn(Dispatchers.IO) - companion object { - private const val AUTHORIZATION_FORMAT = "Bearer %s" + companion object { + private const val AUTHORIZATION_FORMAT = "Bearer %s" + } } -} \ No newline at end of file From f9366935c84a8484f1dac2f02a2ab9a09f94cf37 Mon Sep 17 00:00:00 2001 From: chws0508 Date: Wed, 28 Feb 2024 02:59:37 +0900 Subject: [PATCH 09/10] =?UTF-8?q?feat=20:=20=EA=B5=AC=EA=B8=80=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=97=90=EB=9F=AC=EC=8B=9C=20error?= =?UTF-8?q?=EB=A9=94=EC=84=B8=EC=A7=80=EB=A5=BC=20=EB=9D=84=EC=9A=B0?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/withpeace/withpeace/feature/login/LoginViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt index fbcb6caf..d88918d3 100644 --- a/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt +++ b/feature/login/src/main/java/com/withpeace/withpeace/feature/login/LoginViewModel.kt @@ -17,7 +17,7 @@ class LoginViewModel fun googleLogin(idToken: String) { viewModelScope.launch { tokenRepository.googleLogin(idToken) { - Log.e("woogi", "googleLogin: 로그인 안됨") + Log.e("woogi", it ?: "메시지 없음") }.collect { token -> tokenRepository.updateAccessToken(token.accessToken) tokenRepository.updateRefreshToken(token.refreshToken) From 79ce0d837a4ba6aa1dfe80e061618958ea3f08f6 Mon Sep 17 00:00:00 2001 From: Choi Woo Seok Date: Wed, 28 Feb 2024 03:06:57 +0900 Subject: [PATCH 10/10] =?UTF-8?q?ci=20=EB=AC=B8=EB=B2=95=20=EC=98=A4?= =?UTF-8?q?=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/android.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 7bb2a3aa..af241e66 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: ubuntu-latest - steps: + steps: - uses: actions/checkout@v3 - name: set up JDK 17 uses: actions/setup-java@v3 @@ -45,4 +45,4 @@ jobs: - name: Run ktlint run: ./gradlew ktlintCheck - \ No newline at end of file +