refactor(android): share auth and clean uploaded signatures
This commit is contained in:
@@ -4,15 +4,12 @@ import android.content.Context
|
||||
import com.korexlabs.dhinspeccion.BuildConfig
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.MultipartBody
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.asRequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import retrofit2.HttpException
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import retrofit2.http.Body
|
||||
@@ -303,14 +300,10 @@ private interface MobileActsApi {
|
||||
@Path("visitId") visitId: String,
|
||||
@Body request: MobileCloseVisitRequest,
|
||||
): VisitDetail
|
||||
|
||||
@POST("auth/mobile/refresh")
|
||||
suspend fun refresh(@Body request: RefreshRequest): MobileSessionResponse
|
||||
}
|
||||
|
||||
class MobileActsRepository(context: Context) {
|
||||
private val store = SecureSessionStore(context.applicationContext)
|
||||
private val refreshMutex = Mutex()
|
||||
private val sessions = MobileSessionCoordinator.get(context.applicationContext)
|
||||
private val moshi = Moshi.Builder().addLast(KotlinJsonAdapterFactory()).build()
|
||||
private val api: MobileActsApi = Retrofit.Builder()
|
||||
.baseUrl(BuildConfig.API_BASE_URL)
|
||||
@@ -319,11 +312,11 @@ class MobileActsRepository(context: Context) {
|
||||
.build()
|
||||
.create(MobileActsApi::class.java)
|
||||
|
||||
suspend fun list(visitId: String): MobileActListResponse = authorized { session ->
|
||||
suspend fun list(visitId: String): MobileActListResponse = sessions.authorized { session ->
|
||||
api.listActs("Bearer ${session.accessToken}", visitId)
|
||||
}
|
||||
|
||||
suspend fun get(actId: String): MobileActDetail = authorized { session ->
|
||||
suspend fun get(actId: String): MobileActDetail = sessions.authorized { session ->
|
||||
api.act("Bearer ${session.accessToken}", actId)
|
||||
}
|
||||
|
||||
@@ -332,7 +325,7 @@ class MobileActsRepository(context: Context) {
|
||||
assetId: String,
|
||||
visitCode: String,
|
||||
urgency: String = "NON_URGENT",
|
||||
): MobileActDetail = authorized { session ->
|
||||
): MobileActDetail = sessions.authorized { session ->
|
||||
api.createAct(
|
||||
"Bearer ${session.accessToken}",
|
||||
visitId,
|
||||
@@ -350,20 +343,20 @@ class MobileActsRepository(context: Context) {
|
||||
val detail = get(actId)
|
||||
if (detail.assets.any { it.id == assetId }) return detail
|
||||
val ids = (detail.assets.map { it.id } + assetId).distinct()
|
||||
return authorized { session ->
|
||||
return sessions.authorized { session ->
|
||||
api.updateAct("Bearer ${session.accessToken}", actId, UpdateMobileActRequest(ids))
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun closure(actId: String): MobileActClosure = authorized { session ->
|
||||
suspend fun closure(actId: String): MobileActClosure = sessions.authorized { session ->
|
||||
api.closure("Bearer ${session.accessToken}", actId)
|
||||
}
|
||||
|
||||
suspend fun setResponsible(actId: String, request: MobileResponsibleRequest): MobileActClosure = authorized { session ->
|
||||
suspend fun setResponsible(actId: String, request: MobileResponsibleRequest): MobileActClosure = sessions.authorized { session ->
|
||||
api.responsible("Bearer ${session.accessToken}", actId, request)
|
||||
}
|
||||
|
||||
suspend fun lock(actId: String): MobileActClosure = authorized { session ->
|
||||
suspend fun lock(actId: String): MobileActClosure = sessions.authorized { session ->
|
||||
api.lock("Bearer ${session.accessToken}", actId)
|
||||
}
|
||||
|
||||
@@ -394,7 +387,7 @@ class MobileActsRepository(context: Context) {
|
||||
statement = statement,
|
||||
)
|
||||
|
||||
suspend fun companyOutcome(actId: String, status: String, reason: String): MobileActClosure = authorized { session ->
|
||||
suspend fun companyOutcome(actId: String, status: String, reason: String): MobileActClosure = sessions.authorized { session ->
|
||||
api.companyOutcome(
|
||||
"Bearer ${session.accessToken}",
|
||||
actId,
|
||||
@@ -402,11 +395,11 @@ class MobileActsRepository(context: Context) {
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun seal(actId: String): MobileActClosure = authorized { session ->
|
||||
suspend fun seal(actId: String): MobileActClosure = sessions.authorized { session ->
|
||||
api.sealAct("Bearer ${session.accessToken}", actId, MobileSealActRequest())
|
||||
}
|
||||
|
||||
suspend fun closeVisit(visitId: String): VisitDetail = authorized { session ->
|
||||
suspend fun closeVisit(visitId: String): VisitDetail = sessions.authorized { session ->
|
||||
api.closeVisit("Bearer ${session.accessToken}", visitId, MobileCloseVisitRequest())
|
||||
}
|
||||
|
||||
@@ -419,53 +412,35 @@ class MobileActsRepository(context: Context) {
|
||||
company: Boolean,
|
||||
manifestation: String? = null,
|
||||
statement: String? = null,
|
||||
): MobileActClosure = authorized { session ->
|
||||
val text = "text/plain".toMediaType()
|
||||
val file = MultipartBody.Part.createFormData(
|
||||
"file",
|
||||
png.name,
|
||||
png.asRequestBody("image/png".toMediaType()),
|
||||
)
|
||||
val consent = "true".toRequestBody(text)
|
||||
val signedAt = Instant.now().toString().toRequestBody(text)
|
||||
val device = "DH Android".toRequestBody(text)
|
||||
val lat = latitude?.toString()?.toRequestBody(text)
|
||||
val lon = longitude?.toString()?.toRequestBody(text)
|
||||
val accuracy = accuracyM?.toString()?.toRequestBody(text)
|
||||
if (company) {
|
||||
api.signCompany(
|
||||
"Bearer ${session.accessToken}", actId, file, consent, signedAt,
|
||||
lat, lon, accuracy, device,
|
||||
manifestation?.toRequestBody(text),
|
||||
statement?.trim()?.takeIf { it.isNotBlank() }?.toRequestBody(text),
|
||||
)
|
||||
} else {
|
||||
api.signInspector(
|
||||
"Bearer ${session.accessToken}", actId, file, consent, signedAt,
|
||||
lat, lon, accuracy, device,
|
||||
): MobileActClosure {
|
||||
val response = sessions.authorized { session ->
|
||||
val text = "text/plain".toMediaType()
|
||||
val filePart = MultipartBody.Part.createFormData(
|
||||
"file",
|
||||
png.name,
|
||||
png.asRequestBody("image/png".toMediaType()),
|
||||
)
|
||||
val consent = "true".toRequestBody(text)
|
||||
val signedAt = Instant.now().toString().toRequestBody(text)
|
||||
val device = "DH Android".toRequestBody(text)
|
||||
val lat = latitude?.toString()?.toRequestBody(text)
|
||||
val lon = longitude?.toString()?.toRequestBody(text)
|
||||
val accuracy = accuracyM?.toString()?.toRequestBody(text)
|
||||
if (company) {
|
||||
api.signCompany(
|
||||
"Bearer ${session.accessToken}", actId, filePart, consent, signedAt,
|
||||
lat, lon, accuracy, device,
|
||||
manifestation?.toRequestBody(text),
|
||||
statement?.trim()?.takeIf { it.isNotBlank() }?.toRequestBody(text),
|
||||
)
|
||||
} else {
|
||||
api.signInspector(
|
||||
"Bearer ${session.accessToken}", actId, filePart, consent, signedAt,
|
||||
lat, lon, accuracy, device,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun <T> authorized(block: suspend (StoredSession) -> T): T {
|
||||
var session = store.load() ?: throw IllegalStateException("Sesión no iniciada")
|
||||
try {
|
||||
return block(session)
|
||||
} catch (error: HttpException) {
|
||||
if (error.code() != 401) throw error
|
||||
}
|
||||
session = refresh(session.refreshToken)
|
||||
return block(session)
|
||||
}
|
||||
|
||||
private suspend fun refresh(previousRefreshToken: String): StoredSession = refreshMutex.withLock {
|
||||
val latest = store.load() ?: throw IllegalStateException("Sesión no iniciada")
|
||||
if (latest.refreshToken != previousRefreshToken) return@withLock latest
|
||||
try {
|
||||
store.save(api.refresh(RefreshRequest(previousRefreshToken)))
|
||||
} catch (error: Throwable) {
|
||||
store.clear()
|
||||
throw error
|
||||
}
|
||||
png.delete()
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user