projects request and test
This commit is contained in:
parent
7e8fbd3063
commit
c0de6f6606
@ -13,15 +13,20 @@ object ApiFactory {
|
|||||||
|
|
||||||
fun provideRepository(dataStoreRepository: DataStoreRepository): ApiRepository {
|
fun provideRepository(dataStoreRepository: DataStoreRepository): ApiRepository {
|
||||||
val json = Json { ignoreUnknownKeys = true }
|
val json = Json { ignoreUnknownKeys = true }
|
||||||
|
val client = OkHttpClient.Builder()
|
||||||
|
.addInterceptor(HttpLoggingInterceptor().apply {
|
||||||
|
level = HttpLoggingInterceptor.Level.BODY
|
||||||
|
})
|
||||||
|
.addInterceptor(AuthInterceptor(dataStoreRepository))
|
||||||
|
.build()
|
||||||
|
|
||||||
val retrofit = Retrofit.Builder()
|
val retrofit = Retrofit.Builder()
|
||||||
.baseUrl("https://api.matule.ru/api/")
|
.baseUrl("https://api.matule.ru/api/")
|
||||||
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
|
||||||
.client(OkHttpClient.Builder().addInterceptor(HttpLoggingInterceptor().apply {
|
.client(client)
|
||||||
level = HttpLoggingInterceptor.Level.BODY
|
|
||||||
}).build())
|
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|
||||||
return BaseApiRepository(retrofit.create<ApiService>(), dataStoreRepository)
|
return BaseApiRepository(retrofit.create<ApiService>(), dataStoreRepository, Handle.Base())
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@ package com.example.api.core.data.core
|
|||||||
|
|
||||||
import com.example.api.core.data.dto.AuthUserResponseDto
|
import com.example.api.core.data.dto.AuthUserResponseDto
|
||||||
import com.example.api.core.data.dto.RegisterUserDto
|
import com.example.api.core.data.dto.RegisterUserDto
|
||||||
import com.example.api.core.data.dto.SalesAndActionsDto
|
import com.example.api.core.data.dto.ProjectsDto
|
||||||
import com.example.api.core.data.dto.SignInUserDto
|
import com.example.api.core.data.dto.SignInUserDto
|
||||||
import com.example.api.core.data.dto.UserResponseDto
|
import com.example.api.core.data.dto.UserResponseDto
|
||||||
import retrofit2.http.Body
|
import retrofit2.http.Body
|
||||||
@ -23,7 +23,7 @@ internal interface ApiService {
|
|||||||
suspend fun auth(@Body signInUserDto: SignInUserDto): AuthUserResponseDto
|
suspend fun auth(@Body signInUserDto: SignInUserDto): AuthUserResponseDto
|
||||||
|
|
||||||
@GET("collections/project/records")
|
@GET("collections/project/records")
|
||||||
suspend fun salesAndProjects(): SalesAndActionsDto
|
suspend fun projects(): ProjectsDto
|
||||||
|
|
||||||
// @GET("collections/products/records")
|
// @GET("collections/products/records")
|
||||||
// suspend fun products():
|
// suspend fun products():
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.example.api.core.data.core
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import okhttp3.Interceptor
|
||||||
|
import okhttp3.Response
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Автор: Манякин Дмитрий (user5)
|
||||||
|
* Дата создания: 27.05.2025
|
||||||
|
* */
|
||||||
|
|
||||||
|
class AuthInterceptor(private val dataStoreRepository: DataStoreRepository) : Interceptor {
|
||||||
|
override fun intercept(chain: Interceptor.Chain): Response {
|
||||||
|
val token = runBlocking { dataStoreRepository.token().first() }
|
||||||
|
val request = chain.request().newBuilder().apply {
|
||||||
|
token?.let { addHeader("Authorization", "Bearer $token") }
|
||||||
|
}
|
||||||
|
|
||||||
|
return chain.proceed(request.build())
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import com.example.api.core.domain.ApiRepository
|
|||||||
import com.example.api.core.domain.AuthUserResponse
|
import com.example.api.core.domain.AuthUserResponse
|
||||||
import com.example.api.core.domain.FetchResult
|
import com.example.api.core.domain.FetchResult
|
||||||
import com.example.api.core.domain.RegisterUser
|
import com.example.api.core.domain.RegisterUser
|
||||||
|
import com.example.api.core.domain.Projects
|
||||||
import retrofit2.HttpException
|
import retrofit2.HttpException
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
|
|
||||||
@ -12,24 +13,42 @@ import java.net.UnknownHostException
|
|||||||
* Дата создания: 27.05.2025
|
* Дата создания: 27.05.2025
|
||||||
* */
|
* */
|
||||||
|
|
||||||
|
internal interface Handle {
|
||||||
|
suspend fun <T> handle(action: suspend () -> T): FetchResult<T>
|
||||||
|
|
||||||
|
class Base : Handle {
|
||||||
|
override suspend fun <T> handle(action: suspend () -> T): FetchResult<T> {
|
||||||
|
return try {
|
||||||
|
val result = action.invoke()
|
||||||
|
|
||||||
|
FetchResult.Success(result)
|
||||||
|
} catch (e: UnknownHostException) {
|
||||||
|
FetchResult.Error(null, "no internet connection")
|
||||||
|
} catch (e: HttpException) {
|
||||||
|
FetchResult.Error(null, e.message())
|
||||||
|
} catch (e: Exception) {
|
||||||
|
FetchResult.Error(null, e.message!!)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal class BaseApiRepository(
|
internal class BaseApiRepository(
|
||||||
private val service: ApiService,
|
private val service: ApiService,
|
||||||
private val dataStoreRepository: DataStoreRepository
|
private val dataStoreRepository: DataStoreRepository,
|
||||||
|
private val handle: Handle,
|
||||||
) : RemoteMapper(), ApiRepository {
|
) : RemoteMapper(), ApiRepository {
|
||||||
|
|
||||||
override suspend fun auth(registerUser: RegisterUser): FetchResult<AuthUserResponse> {
|
override suspend fun auth(registerUser: RegisterUser): FetchResult<AuthUserResponse> = handle.handle {
|
||||||
return try {
|
service.register(registerUser.toRegisterUserDto())
|
||||||
service.register(registerUser.toRegisterUserDto())
|
val response = service.auth(registerUser.toSignInUserDto())
|
||||||
val response = service.auth(registerUser.toSignInUserDto())
|
dataStoreRepository.saveToken(response.token)
|
||||||
dataStoreRepository.saveToken(response.token)
|
response.toUserResponse()
|
||||||
|
}
|
||||||
|
|
||||||
FetchResult.Success(response.toUserResponse())
|
|
||||||
} catch (e: UnknownHostException) {
|
override suspend fun projects(): FetchResult<Projects> = handle.handle {
|
||||||
FetchResult.Error(null, "no internet connection")
|
service.projects().toDomain()
|
||||||
} catch (e: HttpException) {
|
|
||||||
FetchResult.Error(null, e.message())
|
|
||||||
} catch (e: Exception) {
|
|
||||||
FetchResult.Error(null, e.message!!)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,7 +16,7 @@ interface DataStoreRepository {
|
|||||||
|
|
||||||
suspend fun saveToken(token: String)
|
suspend fun saveToken(token: String)
|
||||||
|
|
||||||
fun token(): Flow<String>
|
fun token(): Flow<String?>
|
||||||
|
|
||||||
class Base(private val preferences: DataStore<Preferences>) : DataStoreRepository {
|
class Base(private val preferences: DataStore<Preferences>) : DataStoreRepository {
|
||||||
override suspend fun saveToken(token: String) {
|
override suspend fun saveToken(token: String) {
|
||||||
@ -25,9 +25,9 @@ interface DataStoreRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun token(): Flow<String> {
|
override fun token(): Flow<String?> {
|
||||||
return preferences.data.map { preferences ->
|
return preferences.data.map { preferences ->
|
||||||
preferences[TOKEN_KEY] ?: ""
|
preferences[TOKEN_KEY]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,13 +2,15 @@ package com.example.api.core.data.core
|
|||||||
|
|
||||||
import com.example.api.core.data.dto.AuthUserResponseDto
|
import com.example.api.core.data.dto.AuthUserResponseDto
|
||||||
import com.example.api.core.data.dto.RegisterUserDto
|
import com.example.api.core.data.dto.RegisterUserDto
|
||||||
|
import com.example.api.core.data.dto.SaleItemDto
|
||||||
|
import com.example.api.core.data.dto.ProjectsDto
|
||||||
import com.example.api.core.data.dto.SignInUserDto
|
import com.example.api.core.data.dto.SignInUserDto
|
||||||
import com.example.api.core.data.dto.SignInUserResponseDto
|
import com.example.api.core.data.dto.SignInUserResponseDto
|
||||||
import com.example.api.core.data.dto.UserResponseDto
|
|
||||||
import com.example.api.core.domain.AuthUserResponse
|
import com.example.api.core.domain.AuthUserResponse
|
||||||
import com.example.api.core.domain.RegisterUser
|
import com.example.api.core.domain.RegisterUser
|
||||||
|
import com.example.api.core.domain.SaleItem
|
||||||
|
import com.example.api.core.domain.Projects
|
||||||
import com.example.api.core.domain.SignInUserResponse
|
import com.example.api.core.domain.SignInUserResponse
|
||||||
import com.example.api.core.domain.UserResponse
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Автор: Манякин Дмитрий (user5)
|
* Автор: Манякин Дмитрий (user5)
|
||||||
@ -55,4 +57,32 @@ internal abstract class RemoteMapper {
|
|||||||
verified = verified
|
verified = verified
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun ProjectsDto.toDomain(): Projects {
|
||||||
|
return Projects(
|
||||||
|
page = page,
|
||||||
|
perPage = perPage,
|
||||||
|
totalPage = totalPages,
|
||||||
|
totalItems = totalItems,
|
||||||
|
items = items.map { it.toSaleItem() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun SaleItemDto.toSaleItem(): SaleItem {
|
||||||
|
return SaleItem(
|
||||||
|
id = id,
|
||||||
|
collectionId = collectionId,
|
||||||
|
collectionName = collectionName,
|
||||||
|
created = created,
|
||||||
|
updated = updated,
|
||||||
|
title = title,
|
||||||
|
dateStart = dateStart,
|
||||||
|
dateEnd = dateEnd,
|
||||||
|
gender = gender,
|
||||||
|
descriptionSource = descriptionSource,
|
||||||
|
category = category,
|
||||||
|
image = image,
|
||||||
|
userId = userId
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
@ -3,10 +3,10 @@ package com.example.api.core.data.dto
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
internal data class SalesAndActionsDto(
|
internal data class ProjectsDto(
|
||||||
val page: Int,
|
val page: Int,
|
||||||
val perPage: Int,
|
val perPage: Int,
|
||||||
val totalPage: Int,
|
val totalPages: Int,
|
||||||
val totalItems: Int,
|
val totalItems: Int,
|
||||||
val items: List<SaleItemDto>
|
val items: List<SaleItemDto>
|
||||||
)
|
)
|
@ -8,4 +8,6 @@ package com.example.api.core.domain
|
|||||||
interface ApiRepository {
|
interface ApiRepository {
|
||||||
|
|
||||||
suspend fun auth(registerUser: RegisterUser): FetchResult<AuthUserResponse>
|
suspend fun auth(registerUser: RegisterUser): FetchResult<AuthUserResponse>
|
||||||
|
|
||||||
|
suspend fun projects(): FetchResult<Projects>
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package com.example.api.core.domain
|
package com.example.api.core.domain
|
||||||
|
|
||||||
internal data class SalesAndActions(
|
data class Projects(
|
||||||
val page: Int,
|
val page: Int,
|
||||||
val perPage: Int,
|
val perPage: Int,
|
||||||
val totalPage: Int,
|
val totalPage: Int,
|
@ -126,6 +126,49 @@ class ServiceTest {
|
|||||||
assertEquals("/collections/users/auth-with-password", request.path)
|
assertEquals("/collections/users/auth-with-password", request.path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_project_list() = runBlocking {
|
||||||
|
val response = """
|
||||||
|
{
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"category": "е",
|
||||||
|
"collectionId": "pbc_3202395908",
|
||||||
|
"collectionName": "project",
|
||||||
|
"created": "2025-05-26 14:47:25.523Z",
|
||||||
|
"dateEnd": "2025-05-24 12:00:00.000Z",
|
||||||
|
"dateStart": "2025-05-15 12:00:00.000Z",
|
||||||
|
"description_source": "е",
|
||||||
|
"gender": "е",
|
||||||
|
"id": "7m35pcmbjr86kh4",
|
||||||
|
"image": "",
|
||||||
|
"title": "е",
|
||||||
|
"typeProject": "е",
|
||||||
|
"updated": "2025-05-26 14:47:25.523Z",
|
||||||
|
"user_id": ""
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"page": 1,
|
||||||
|
"perPage": 30,
|
||||||
|
"totalItems": 1,
|
||||||
|
"totalPages": 1
|
||||||
|
}
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
mockWebServer.enqueue(MockResponse().setResponseCode(200).setBody(response))
|
||||||
|
|
||||||
|
val responseResult = service.projects()
|
||||||
|
|
||||||
|
val request = mockWebServer.takeRequest()
|
||||||
|
|
||||||
|
assertEquals(1, responseResult.page)
|
||||||
|
assertEquals(1, responseResult.totalItems)
|
||||||
|
assertEquals(1, responseResult.items.size)
|
||||||
|
|
||||||
|
assertEquals("GET", request.method)
|
||||||
|
assertEquals("/collections/project/records", request.path)
|
||||||
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun teardown() {
|
fun teardown() {
|
||||||
mockWebServer.shutdown()
|
mockWebServer.shutdown()
|
||||||
|
@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope
|
|||||||
import com.example.api.core.domain.ApiRepository
|
import com.example.api.core.domain.ApiRepository
|
||||||
import com.example.api.core.domain.AuthUserResponse
|
import com.example.api.core.domain.AuthUserResponse
|
||||||
import com.example.api.core.domain.RegisterUser
|
import com.example.api.core.domain.RegisterUser
|
||||||
|
import com.example.api.core.domain.Projects
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@ -24,10 +25,15 @@ class MainViewModel @Inject constructor(
|
|||||||
private val apiRepository: ApiRepository
|
private val apiRepository: ApiRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _loadResultUiState =
|
private val _authUiState =
|
||||||
MutableStateFlow<FetchResultUiState<AuthUserResponse>>(FetchResultUiState.Initial())
|
MutableStateFlow<FetchResultUiState<AuthUserResponse>>(FetchResultUiState.Initial())
|
||||||
val loadResultUiState: StateFlow<FetchResultUiState<AuthUserResponse>>
|
val authUiState: StateFlow<FetchResultUiState<AuthUserResponse>>
|
||||||
get() = _loadResultUiState.asStateFlow()
|
get() = _authUiState.asStateFlow()
|
||||||
|
|
||||||
|
private val _ProjectsUiState =
|
||||||
|
MutableStateFlow<FetchResultUiState<Projects>>(FetchResultUiState.Initial())
|
||||||
|
val projectsUiState: StateFlow<FetchResultUiState<Projects>>
|
||||||
|
get() = _ProjectsUiState.asStateFlow()
|
||||||
|
|
||||||
fun auth(email: String, password: String) {
|
fun auth(email: String, password: String) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
@ -40,7 +46,17 @@ class MainViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
_loadResultUiState.value = result.map(FetchResultMapper())
|
_authUiState.value = result.map(FetchResultMapper())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun salesAndProjects() {
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
val result = apiRepository.projects()
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
_ProjectsUiState.value = result.map(FetchResultMapper())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,12 @@ fun TestScreen(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: MainViewModel = hiltViewModel()
|
viewModel: MainViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
val state by viewModel.loadResultUiState.collectAsState()
|
val state by viewModel.authUiState.collectAsState()
|
||||||
|
val state2 by viewModel.projectsUiState.collectAsState()
|
||||||
|
|
||||||
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
Column {
|
Column {
|
||||||
Button(onClick = { viewModel.auth("someemael@te3st1.com", "12345678") }) {
|
Button(onClick = { viewModel.auth("someemael@te3s11t1.com", "12345678") }) {
|
||||||
Text("register")
|
Text("register")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +37,16 @@ fun TestScreen(
|
|||||||
onError = { _, message -> Text(text = message) },
|
onError = { _, message -> Text(text = message) },
|
||||||
onLoading = { CircularProgressIndicator() }
|
onLoading = { CircularProgressIndicator() }
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Button(onClick = { viewModel.salesAndProjects() }) {
|
||||||
|
Text("sales and projects")
|
||||||
|
}
|
||||||
|
|
||||||
|
state2.Show(
|
||||||
|
onSuccess = { Text(text = it.items.size.toString()) },
|
||||||
|
onError = { _, message -> Text(text = message) },
|
||||||
|
onLoading = { CircularProgressIndicator() }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user