Skip to content

Commit

Permalink
[Feature] PreSigned 관련 API / Repository 연결 (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
ajou4095 authored Feb 3, 2024
1 parent 97de2d5 commit fb5cfda
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package ac.dnd.bookkeeping.android.data.di

import ac.dnd.bookkeeping.android.data.repository.authentication.MockAuthenticationRepository
import ac.dnd.bookkeeping.android.data.repository.file.MockFileRepository
import ac.dnd.bookkeeping.android.data.repository.member.MockMemberRepository
import ac.dnd.bookkeeping.android.data.repository.sociallogin.KakaoLoginRepositoryImpl
import ac.dnd.bookkeeping.android.domain.repository.AuthenticationRepository
import ac.dnd.bookkeeping.android.domain.repository.FileRepository
import ac.dnd.bookkeeping.android.domain.repository.KakaoLoginRepository
import ac.dnd.bookkeeping.android.domain.repository.MemberRepository
import dagger.Binds
Expand All @@ -28,6 +30,12 @@ internal abstract class RepositoryModule {
memberRepository: MockMemberRepository
): MemberRepository

@Binds
@Singleton
abstract fun bindsFileRepository(
fileRepository: MockFileRepository
): FileRepository

@Binds
@Singleton
abstract fun bindsKakaoLoginRepository(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package ac.dnd.bookkeeping.android.data.remote.network.api

import ac.dnd.bookkeeping.android.data.remote.network.di.AuthHttpClient
import ac.dnd.bookkeeping.android.data.remote.network.di.NoAuthHttpClient
import ac.dnd.bookkeeping.android.data.remote.network.environment.BaseUrlProvider
import ac.dnd.bookkeeping.android.data.remote.network.environment.ErrorMessageMapper
import ac.dnd.bookkeeping.android.data.remote.network.model.file.GetPreSignedUrlRes
import ac.dnd.bookkeeping.android.data.remote.network.util.convert
import android.net.Uri
import android.webkit.MimeTypeMap
import io.ktor.client.HttpClient
import io.ktor.client.request.forms.formData
import io.ktor.client.request.forms.submitFormWithBinaryData
import io.ktor.client.request.get
import io.ktor.client.request.parameter
import io.ktor.http.Headers
import io.ktor.http.HttpHeaders
import java.io.File
import javax.inject.Inject


class FileApi @Inject constructor(
@NoAuthHttpClient private val noAuthClient: HttpClient,
@AuthHttpClient private val client: HttpClient,
private val baseUrlProvider: BaseUrlProvider,
private val errorMessageMapper: ErrorMessageMapper
) {
private val baseUrl: String
get() = baseUrlProvider.get()

suspend fun getPreSignedUrl(
fileName: String
): Result<GetPreSignedUrlRes> {
return client.get("$baseUrl/api/v1/files/presigned") {
parameter("fileName", fileName)
}.convert(errorMessageMapper::map)
}

suspend fun upload(
preSignedUrl: String,
imageUri: String,
fileName: String? = null
): Result<Unit> {
val image = Uri.parse(imageUri)?.path ?: let {
return Result.failure(IllegalArgumentException("Invalid imageUri"))
}
val file = File(image)
val name = fileName ?: file.name
val contentType = getContentType(file.path)

return noAuthClient.submitFormWithBinaryData(
url = preSignedUrl,
formData = formData {
append(
"image",
file.readBytes(),
Headers.build {
contentType?.let { append(HttpHeaders.ContentType, it) }
append(HttpHeaders.ContentDisposition, "filename=$name")
}
)
}
).convert(errorMessageMapper::map)
}

private fun getContentType(
url: String
): String? {
return MimeTypeMap.getFileExtensionFromUrl(url)?.let { fileExtension ->
MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ac.dnd.bookkeeping.android.data.remote.network.model.file

import ac.dnd.bookkeeping.android.data.remote.mapper.DataMapper
import ac.dnd.bookkeeping.android.domain.model.file.PreSignedUrl
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class GetPreSignedUrlRes(
@SerialName("preSignedUrl")
val preSignedUrl: String,
@SerialName("uploadFileUrl")
val uploadFileUrl: String
) : DataMapper<PreSignedUrl> {
override fun toDomain(): PreSignedUrl {
return PreSignedUrl(
preSignedUrl = preSignedUrl,
uploadFileUrl = uploadFileUrl
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ac.dnd.bookkeeping.android.data.repository.file

import ac.dnd.bookkeeping.android.domain.model.file.PreSignedUrl
import ac.dnd.bookkeeping.android.domain.repository.FileRepository
import kotlinx.coroutines.delay
import javax.inject.Inject

class MockFileRepository @Inject constructor() : FileRepository {
override suspend fun getPreSignedUrl(
fileName: String
): Result<PreSignedUrl> {
randomShortDelay()
return Result.success(
PreSignedUrl(
preSignedUrl = "https://example.com",
uploadFileUrl = "https://example.com"
)
)
}

override suspend fun upload(
preSignedUrl: String,
imageUri: String
): Result<Unit> {
randomLongDelay()
return Result.success(Unit)
}

private suspend fun randomShortDelay() {
delay(LongRange(100, 500).random())
}

private suspend fun randomLongDelay() {
delay(LongRange(500, 2000).random())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ac.dnd.bookkeeping.android.data.repository.file

import ac.dnd.bookkeeping.android.data.remote.local.SharedPreferencesManager
import ac.dnd.bookkeeping.android.data.remote.network.api.FileApi
import ac.dnd.bookkeeping.android.data.remote.network.util.toDomain
import ac.dnd.bookkeeping.android.domain.model.file.PreSignedUrl
import ac.dnd.bookkeeping.android.domain.repository.FileRepository
import javax.inject.Inject

class RealFileRepository @Inject constructor(
private val fileApi: FileApi,
private val sharedPreferencesManager: SharedPreferencesManager
) : FileRepository {
override suspend fun getPreSignedUrl(
fileName: String
): Result<PreSignedUrl> {
return fileApi.getPreSignedUrl(fileName).toDomain()
}

override suspend fun upload(
preSignedUrl: String,
imageUri: String
): Result<Unit> {
return fileApi.upload(
preSignedUrl = preSignedUrl,
imageUri = imageUri
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package ac.dnd.bookkeeping.android.domain.model.file

data class PreSignedUrl(
val preSignedUrl: String,
val uploadFileUrl: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ac.dnd.bookkeeping.android.domain.repository

import ac.dnd.bookkeeping.android.domain.model.file.PreSignedUrl

interface FileRepository {
suspend fun getPreSignedUrl(
fileName: String
): Result<PreSignedUrl>

suspend fun upload(
preSignedUrl: String,
imageUri: String
): Result<Unit>
}

0 comments on commit fb5cfda

Please sign in to comment.