initial commit
This commit is contained in:
73
data/build.gradle.kts
Normal file
73
data/build.gradle.kts
Normal file
@@ -0,0 +1,73 @@
|
||||
// [FILE] data/build.gradle.kts
|
||||
// [PURPOSE] Build script for the data module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.data"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// [MODULE_DEPENDENCY] Domain module
|
||||
implementation(project(":domain"))
|
||||
|
||||
// [DEPENDENCY] AndroidX
|
||||
implementation(Libs.coreKtx)
|
||||
|
||||
// [DEPENDENCY] Coroutines
|
||||
implementation(Libs.coroutinesCore)
|
||||
|
||||
// [DEPENDENCY] Networking (Retrofit, Gson)
|
||||
implementation(Libs.retrofit)
|
||||
implementation(Libs.converterGson)
|
||||
implementation(Libs.okhttp)
|
||||
implementation(Libs.okhttpLoggingInterceptor)
|
||||
|
||||
// [DEPENDENCY] Database (Room)
|
||||
implementation(Libs.roomRuntime)
|
||||
implementation(Libs.roomKtx)
|
||||
kapt(Libs.roomCompiler)
|
||||
|
||||
// [DEPENDENCY] DI (Hilt)
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
// [DEPENDENCY] Testing
|
||||
testImplementation(Libs.junit)
|
||||
androidTestImplementation(Libs.extJunit)
|
||||
androidTestImplementation(Libs.espressoCore)
|
||||
}
|
||||
|
||||
kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
|
||||
// [END_FILE_data/build.gradle.kts]
|
||||
@@ -0,0 +1,63 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api
|
||||
// [FILE] HomeboxApiService.kt
|
||||
|
||||
package com.homebox.lens.data.api
|
||||
|
||||
import com.homebox.lens.data.api.dto.GroupStatisticsDto
|
||||
import com.homebox.lens.data.api.dto.ItemCreateDto
|
||||
import com.homebox.lens.data.api.dto.ItemOutDto
|
||||
import com.homebox.lens.data.api.dto.ItemSummaryDto
|
||||
import com.homebox.lens.data.api.dto.ItemUpdateDto
|
||||
import com.homebox.lens.data.api.dto.LabelOutDto
|
||||
import com.homebox.lens.data.api.dto.LocationOutCountDto
|
||||
import com.homebox.lens.data.api.dto.PaginationResultDto
|
||||
import retrofit2.Response
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.DELETE
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.PUT
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: Interface('HomeboxApiService')]
|
||||
* [PURPOSE] Определяет эндпоинты для взаимодействия с Homebox API, используя DTO.
|
||||
*/
|
||||
interface HomeboxApiService {
|
||||
|
||||
// [ENDPOINT] Items
|
||||
@GET("v1/items")
|
||||
suspend fun getItems(
|
||||
@Query("q") query: String? = null,
|
||||
@Query("page") page: Int? = null,
|
||||
@Query("pageSize") pageSize: Int? = null
|
||||
): PaginationResultDto<ItemSummaryDto>
|
||||
|
||||
@POST("v1/items")
|
||||
suspend fun createItem(@Body item: ItemCreateDto): ItemSummaryDto
|
||||
|
||||
@GET("v1/items/{id}")
|
||||
suspend fun getItem(@Path("id") itemId: String): ItemOutDto
|
||||
|
||||
@PUT("v1/items/{id}")
|
||||
suspend fun updateItem(@Path("id") itemId: String, @Body item: ItemUpdateDto): ItemOutDto
|
||||
|
||||
@DELETE("v1/items/{id}")
|
||||
suspend fun deleteItem(@Path("id") itemId: String): Response<Unit>
|
||||
|
||||
// [ENDPOINT] Locations
|
||||
@GET("v1/locations")
|
||||
suspend fun getLocations(): List<LocationOutCountDto>
|
||||
|
||||
// [ENDPOINT] Labels
|
||||
@GET("v1/labels")
|
||||
suspend fun getLabels(): List<LabelOutDto>
|
||||
|
||||
// [ENDPOINT] Statistics
|
||||
@GET("v1/groups/statistics")
|
||||
suspend fun getStatistics(): GroupStatisticsDto
|
||||
}
|
||||
|
||||
// [END_FILE_HomeboxApiService.kt]
|
||||
@@ -0,0 +1,30 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] CustomFieldDto.kt
|
||||
// [SEMANTICS] data_transfer_object, custom_field
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.CustomField
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для кастомного поля.
|
||||
*/
|
||||
data class CustomFieldDto(
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("value") val value: String,
|
||||
@SerializedName("type") val type: String
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из CustomFieldDto в доменную модель CustomField.
|
||||
*/
|
||||
fun CustomFieldDto.toDomain(): CustomField {
|
||||
return CustomField(
|
||||
name = this.name,
|
||||
value = this.value,
|
||||
type = this.type
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] GroupStatisticsDto.kt
|
||||
// [SEMANTICS] data_transfer_object, statistics
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.GroupStatistics
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для статистики.
|
||||
*/
|
||||
data class GroupStatisticsDto(
|
||||
@SerializedName("items") val items: Int,
|
||||
@SerializedName("labels") val labels: Int,
|
||||
@SerializedName("locations") val locations: Int,
|
||||
@SerializedName("totalValue") val totalValue: Double
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из GroupStatisticsDto в доменную модель GroupStatistics.
|
||||
*/
|
||||
fun GroupStatisticsDto.toDomain(): GroupStatistics {
|
||||
return GroupStatistics(
|
||||
items = this.items,
|
||||
labels = this.labels,
|
||||
locations = this.locations,
|
||||
totalValue = this.totalValue
|
||||
)
|
||||
}
|
||||
33
data/src/main/java/com/homebox/lens/data/api/dto/ImageDto.kt
Normal file
33
data/src/main/java/com/homebox/lens/data/api/dto/ImageDto.kt
Normal file
@@ -0,0 +1,33 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] ImageDto.kt
|
||||
// [SEMANTICS] data_transfer_object, image
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.Image
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для изображения.
|
||||
* @property id Уникальный идентификатор.
|
||||
* @property path Путь к файлу.
|
||||
* @property isPrimary Является ли основным.
|
||||
*/
|
||||
data class ImageDto(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("path") val path: String,
|
||||
@SerializedName("isPrimary") val isPrimary: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из ImageDto в доменную модель Image.
|
||||
*/
|
||||
fun ImageDto.toDomain(): Image {
|
||||
return Image(
|
||||
id = this.id,
|
||||
path = this.path,
|
||||
isPrimary = this.isPrimary
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] ItemAttachmentDto.kt
|
||||
// [SEMANTICS] data_transfer_object, attachment
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.ItemAttachment
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для вложения.
|
||||
*/
|
||||
data class ItemAttachmentDto(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("path") val path: String,
|
||||
@SerializedName("type") val type: String,
|
||||
@SerializedName("createdAt") val createdAt: String,
|
||||
@SerializedName("updatedAt") val updatedAt: String
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из ItemAttachmentDto в доменную модель ItemAttachment.
|
||||
*/
|
||||
fun ItemAttachmentDto.toDomain(): ItemAttachment {
|
||||
return ItemAttachment(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
path = this.path,
|
||||
type = this.type,
|
||||
createdAt = this.createdAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] ItemCreateDto.kt
|
||||
// [SEMANTICS] data_transfer_object, item_creation
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.ItemCreate
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для создания вещи.
|
||||
*/
|
||||
data class ItemCreateDto(
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("assetId") val assetId: String?,
|
||||
@SerializedName("description") val description: String?,
|
||||
@SerializedName("notes") val notes: String?,
|
||||
@SerializedName("serialNumber") val serialNumber: String?,
|
||||
@SerializedName("quantity") val quantity: Int?,
|
||||
@SerializedName("value") val value: Double?,
|
||||
@SerializedName("purchasePrice") val purchasePrice: Double?,
|
||||
@SerializedName("purchaseDate") val purchaseDate: String?,
|
||||
@SerializedName("warrantyUntil") val warrantyUntil: String?,
|
||||
@SerializedName("locationId") val locationId: String?,
|
||||
@SerializedName("parentId") val parentId: String?,
|
||||
@SerializedName("labelIds") val labelIds: List<String>?
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из доменной модели ItemCreate в ItemCreateDto.
|
||||
*/
|
||||
fun ItemCreate.toDto(): ItemCreateDto {
|
||||
return ItemCreateDto(
|
||||
name = this.name,
|
||||
assetId = this.assetId,
|
||||
description = this.description,
|
||||
notes = this.notes,
|
||||
serialNumber = this.serialNumber,
|
||||
quantity = this.quantity,
|
||||
value = this.value,
|
||||
purchasePrice = this.purchasePrice,
|
||||
purchaseDate = this.purchaseDate,
|
||||
warrantyUntil = this.warrantyUntil,
|
||||
locationId = this.locationId,
|
||||
parentId = this.parentId,
|
||||
labelIds = this.labelIds
|
||||
)
|
||||
}
|
||||
66
data/src/main/java/com/homebox/lens/data/api/dto/ItemDto.kt
Normal file
66
data/src/main/java/com/homebox/lens/data/api/dto/ItemDto.kt
Normal file
@@ -0,0 +1,66 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] ItemDto.kt
|
||||
|
||||
package com.homebox.lens.data.api.dto
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.math.BigDecimal
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: DataClass('ItemOut')]
|
||||
* [PURPOSE] DTO для полной информации о вещи (GET /v1/items/{id}).
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ItemOut(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "description") val description: String?,
|
||||
@Json(name = "image") val image: String?,
|
||||
@Json(name = "location") val location: LocationOut?,
|
||||
@Json(name = "labels") val labels: List<LabelOut>,
|
||||
@Json(name = "value") val value: BigDecimal?,
|
||||
@Json(name = "createdAt") val createdAt: String?
|
||||
)
|
||||
|
||||
/**
|
||||
* [ENTITY: DataClass('ItemSummary')]
|
||||
* [PURPOSE] DTO для краткой информации о вещи в списках (GET /v1/items).
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ItemSummary(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "image") val image: String?,
|
||||
@Json(name = "location") val location: LocationOut?,
|
||||
@Json(name = "createdAt") val createdAt: String?
|
||||
)
|
||||
|
||||
/**
|
||||
* [ENTITY: DataClass('ItemCreate')]
|
||||
* [PURPOSE] DTO для создания новой вещи (POST /v1/items).
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ItemCreate(
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "description") val description: String?,
|
||||
@Json(name = "locationId") val locationId: String?,
|
||||
@Json(name = "labelIds") val labelIds: List<String>?,
|
||||
@Json(name = "value") val value: BigDecimal?
|
||||
)
|
||||
|
||||
/**
|
||||
* [ENTITY: DataClass('ItemUpdate')]
|
||||
* [PURPOSE] DTO для обновления вещи (PUT /v1/items/{id}).
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ItemUpdate(
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "description") val description: String?,
|
||||
@Json(name = "locationId") val locationId: String?,
|
||||
@Json(name = "labelIds") val labelIds: List<String>?,
|
||||
@Json(name = "value") val value: BigDecimal?
|
||||
)
|
||||
|
||||
// [END_FILE_ItemDto.kt]
|
||||
@@ -0,0 +1,68 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] ItemOutDto.kt
|
||||
// [SEMANTICS] data_transfer_object, item_detailed
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.ItemOut
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для полной модели вещи.
|
||||
*/
|
||||
data class ItemOutDto(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("assetId") val assetId: String?,
|
||||
@SerializedName("description") val description: String?,
|
||||
@SerializedName("notes") val notes: String?,
|
||||
@SerializedName("serialNumber") val serialNumber: String?,
|
||||
@SerializedName("quantity") val quantity: Int,
|
||||
@SerializedName("isArchived") val isArchived: Boolean,
|
||||
@SerializedName("value") val value: Double,
|
||||
@SerializedName("purchasePrice") val purchasePrice: Double?,
|
||||
@SerializedName("purchaseDate") val purchaseDate: String?,
|
||||
@SerializedName("warrantyUntil") val warrantyUntil: String?,
|
||||
@SerializedName("location") val location: LocationOutDto?,
|
||||
@SerializedName("parent") val parent: ItemSummaryDto?,
|
||||
@SerializedName("children") val children: List<ItemSummaryDto>,
|
||||
@SerializedName("labels") val labels: List<LabelOutDto>,
|
||||
@SerializedName("attachments") val attachments: List<ItemAttachmentDto>,
|
||||
@SerializedName("images") val images: List<ImageDto>,
|
||||
@SerializedName("fields") val fields: List<CustomFieldDto>,
|
||||
@SerializedName("maintenance") val maintenance: List<MaintenanceEntryDto>,
|
||||
@SerializedName("createdAt") val createdAt: String,
|
||||
@SerializedName("updatedAt") val updatedAt: String
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из ItemOutDto в доменную модель ItemOut.
|
||||
*/
|
||||
fun ItemOutDto.toDomain(): ItemOut {
|
||||
return ItemOut(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
assetId = this.assetId,
|
||||
description = this.description,
|
||||
notes = this.notes,
|
||||
serialNumber = this.serialNumber,
|
||||
quantity = this.quantity,
|
||||
isArchived = this.isArchived,
|
||||
value = this.value,
|
||||
purchasePrice = this.purchasePrice,
|
||||
purchaseDate = this.purchaseDate,
|
||||
warrantyUntil = this.warrantyUntil,
|
||||
location = this.location?.toDomain(),
|
||||
parent = this.parent?.toDomain(),
|
||||
children = this.children.map { it.toDomain() },
|
||||
labels = this.labels.map { it.toDomain() },
|
||||
attachments = this.attachments.map { it.toDomain() },
|
||||
images = this.images.map { it.toDomain() },
|
||||
fields = this.fields.map { it.toDomain() },
|
||||
maintenance = this.maintenance.map { it.toDomain() },
|
||||
createdAt = this.createdAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] ItemSummaryDto.kt
|
||||
// [SEMANTICS] data_transfer_object, item_summary
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.ItemSummary
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для сокращенной модели вещи.
|
||||
*/
|
||||
data class ItemSummaryDto(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("assetId") val assetId: String?,
|
||||
@SerializedName("image") val image: ImageDto?,
|
||||
@SerializedName("isArchived") val isArchived: Boolean,
|
||||
@SerializedName("labels") val labels: List<LabelOutDto>,
|
||||
@SerializedName("location") val location: LocationOutDto?,
|
||||
@SerializedName("value") val value: Double,
|
||||
@SerializedName("createdAt") val createdAt: String,
|
||||
@SerializedName("updatedAt") val updatedAt: String
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из ItemSummaryDto в доменную модель ItemSummary.
|
||||
*/
|
||||
fun ItemSummaryDto.toDomain(): ItemSummary {
|
||||
return ItemSummary(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
assetId = this.assetId,
|
||||
image = this.image?.toDomain(),
|
||||
isArchived = this.isArchived,
|
||||
labels = this.labels.map { it.toDomain() },
|
||||
location = this.location?.toDomain(),
|
||||
value = this.value,
|
||||
createdAt = this.createdAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] ItemUpdateDto.kt
|
||||
// [SEMANTICS] data_transfer_object, item_update
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.ItemUpdate
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для обновления вещи.
|
||||
*/
|
||||
data class ItemUpdateDto(
|
||||
@SerializedName("name") val name: String?,
|
||||
@SerializedName("assetId") val assetId: String?,
|
||||
@SerializedName("description") val description: String?,
|
||||
@SerializedName("notes") val notes: String?,
|
||||
@SerializedName("serialNumber") val serialNumber: String?,
|
||||
@SerializedName("quantity") val quantity: Int?,
|
||||
@SerializedName("isArchived") val isArchived: Boolean?,
|
||||
@SerializedName("value") val value: Double?,
|
||||
@SerializedName("purchasePrice") val purchasePrice: Double?,
|
||||
@SerializedName("purchaseDate") val purchaseDate: String?,
|
||||
@SerializedName("warrantyUntil") val warrantyUntil: String?,
|
||||
@SerializedName("locationId") val locationId: String?,
|
||||
@SerializedName("parentId") val parentId: String?,
|
||||
@SerializedName("labelIds") val labelIds: List<String>?
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из доменной модели ItemUpdate в ItemUpdateDto.
|
||||
*/
|
||||
fun ItemUpdate.toDto(): ItemUpdateDto {
|
||||
return ItemUpdateDto(
|
||||
name = this.name,
|
||||
assetId = this.assetId,
|
||||
description = this.description,
|
||||
notes = this.notes,
|
||||
serialNumber = this.serialNumber,
|
||||
quantity = this.quantity,
|
||||
isArchived = this.isArchived,
|
||||
value = this.value,
|
||||
purchasePrice = this.purchasePrice,
|
||||
purchaseDate = this.purchaseDate,
|
||||
warrantyUntil = this.warrantyUntil,
|
||||
locationId = this.locationId,
|
||||
parentId = this.parentId,
|
||||
labelIds = this.labelIds
|
||||
)
|
||||
}
|
||||
20
data/src/main/java/com/homebox/lens/data/api/dto/LabelDto.kt
Normal file
20
data/src/main/java/com/homebox/lens/data/api/dto/LabelDto.kt
Normal file
@@ -0,0 +1,20 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] LabelDto.kt
|
||||
|
||||
package com.homebox.lens.data.api.dto
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: DataClass('LabelOut')]
|
||||
* [PURPOSE] DTO для информации о метке.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class LabelOut(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "name") val name: String
|
||||
)
|
||||
|
||||
// [END_FILE_LabelDto.kt]
|
||||
@@ -0,0 +1,36 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] LabelOutDto.kt
|
||||
// [SEMANTICS] data_transfer_object, label
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.LabelOut
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для метки.
|
||||
*/
|
||||
data class LabelOutDto(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("color") val color: String,
|
||||
@SerializedName("isArchived") val isArchived: Boolean,
|
||||
@SerializedName("createdAt") val createdAt: String,
|
||||
@SerializedName("updatedAt") val updatedAt: String
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из LabelOutDto в доменную модель LabelOut.
|
||||
*/
|
||||
fun LabelOutDto.toDomain(): LabelOut {
|
||||
return LabelOut(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
color = this.color,
|
||||
isArchived = this.isArchived,
|
||||
createdAt = this.createdAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] LocationDto.kt
|
||||
|
||||
package com.homebox.lens.data.api.dto
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: DataClass('LocationOut')]
|
||||
* [PURPOSE] DTO для информации о местоположении.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class LocationOut(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "name") val name: String
|
||||
)
|
||||
|
||||
/**
|
||||
* [ENTITY: DataClass('LocationOutCount')]
|
||||
* [PURPOSE] DTO для информации о местоположении со счетчиком вещей.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class LocationOutCount(
|
||||
@Json(name = "id") val id: String,
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "itemCount") val itemCount: Int
|
||||
)
|
||||
|
||||
// [END_FILE_LocationDto.kt]
|
||||
@@ -0,0 +1,38 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] LocationOutCountDto.kt
|
||||
// [SEMANTICS] data_transfer_object, location, count
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.LocationOutCount
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для местоположения со счетчиком.
|
||||
*/
|
||||
data class LocationOutCountDto(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("color") val color: String,
|
||||
@SerializedName("isArchived") val isArchived: Boolean,
|
||||
@SerializedName("itemCount") val itemCount: Int,
|
||||
@SerializedName("createdAt") val createdAt: String,
|
||||
@SerializedName("updatedAt") val updatedAt: String
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из LocationOutCountDto в доменную модель LocationOutCount.
|
||||
*/
|
||||
fun LocationOutCountDto.toDomain(): LocationOutCount {
|
||||
return LocationOutCount(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
color = this.color,
|
||||
isArchived = this.isArchived,
|
||||
itemCount = this.itemCount,
|
||||
createdAt = this.createdAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] LocationOutDto.kt
|
||||
// [SEMANTICS] data_transfer_object, location
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.LocationOut
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для местоположения.
|
||||
*/
|
||||
data class LocationOutDto(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("name") val name: String,
|
||||
@SerializedName("color") val color: String,
|
||||
@SerializedName("isArchived") val isArchived: Boolean,
|
||||
@SerializedName("createdAt") val createdAt: String,
|
||||
@SerializedName("updatedAt") val updatedAt: String
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из LocationOutDto в доменную модель LocationOut.
|
||||
*/
|
||||
fun LocationOutDto.toDomain(): LocationOut {
|
||||
return LocationOut(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
color = this.color,
|
||||
isArchived = this.isArchived,
|
||||
createdAt = this.createdAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] MaintenanceEntryDto.kt
|
||||
// [SEMANTICS] data_transfer_object, maintenance
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.MaintenanceEntry
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для записи об обслуживании.
|
||||
*/
|
||||
data class MaintenanceEntryDto(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("itemId") val itemId: String,
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("details") val details: String?,
|
||||
@SerializedName("dueAt") val dueAt: String?,
|
||||
@SerializedName("completedAt") val completedAt: String?,
|
||||
@SerializedName("createdAt") val createdAt: String,
|
||||
@SerializedName("updatedAt") val updatedAt: String
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из MaintenanceEntryDto в доменную модель MaintenanceEntry.
|
||||
*/
|
||||
fun MaintenanceEntryDto.toDomain(): MaintenanceEntry {
|
||||
return MaintenanceEntry(
|
||||
id = this.id,
|
||||
itemId = this.itemId,
|
||||
title = this.title,
|
||||
details = this.details,
|
||||
dueAt = this.dueAt,
|
||||
completedAt = this.completedAt,
|
||||
createdAt = this.createdAt,
|
||||
updatedAt = this.updatedAt
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] PaginationDto.kt
|
||||
|
||||
package com.homebox.lens.data.api.dto
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: DataClass('PaginationResult')]
|
||||
* [PURPOSE] DTO для пагинированных результатов от API.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class PaginationResult<T>(
|
||||
@Json(name = "items") val items: List<T>,
|
||||
@Json(name = "page") val page: Int,
|
||||
@Json(name = "pages") val pages: Int,
|
||||
@Json(name = "total") val total: Int,
|
||||
@Json(name = "pageSize") val pageSize: Int
|
||||
)
|
||||
|
||||
// [END_FILE_PaginationDto.kt]
|
||||
@@ -0,0 +1,33 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] PaginationResultDto.kt
|
||||
// [SEMANTICS] data_transfer_object, pagination
|
||||
|
||||
// [IMPORTS]
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.homebox.lens.domain.model.PaginationResult
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* DTO для постраничных результатов.
|
||||
*/
|
||||
data class PaginationResultDto<T>(
|
||||
@SerializedName("items") val items: List<T>,
|
||||
@SerializedName("page") val page: Int,
|
||||
@SerializedName("pageSize") val pageSize: Int,
|
||||
@SerializedName("total") val total: Int
|
||||
)
|
||||
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Маппер из PaginationResultDto в доменную модель PaginationResult.
|
||||
* @param transform Функция для преобразования каждого элемента из DTO в доменную модель.
|
||||
*/
|
||||
fun <T, R> PaginationResultDto<T>.toDomain(transform: (T) -> R): PaginationResult<R> {
|
||||
return PaginationResult(
|
||||
items = this.items.map(transform),
|
||||
page = this.page,
|
||||
pageSize = this.pageSize,
|
||||
total = this.total
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// [PACKAGE] com.homebox.lens.data.api.dto
|
||||
// [FILE] StatisticsDto.kt
|
||||
|
||||
package com.homebox.lens.data.api.dto
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import java.math.BigDecimal
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: DataClass('GroupStatistics')]
|
||||
* [PURPOSE] DTO для статистической информации.
|
||||
*/
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class GroupStatistics(
|
||||
@Json(name = "totalValue") val totalValue: BigDecimal,
|
||||
@Json(name = "totalItems") val totalItems: Int,
|
||||
@Json(name = "locations") val locations: Int,
|
||||
@Json(name = "labels") val labels: Int
|
||||
)
|
||||
|
||||
// [END_FILE_StatisticsDto.kt]
|
||||
26
data/src/main/java/com/homebox/lens/data/db/Converters.kt
Normal file
26
data/src/main/java/com/homebox/lens/data/db/Converters.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db
|
||||
// [FILE] Converters.kt
|
||||
|
||||
package com.homebox.lens.data.db
|
||||
|
||||
import androidx.room.TypeConverter
|
||||
import java.math.BigDecimal
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: Class('Converters')]
|
||||
* [PURPOSE] Предоставляет TypeConverters для Room для типов, не поддерживаемых по умолчанию.
|
||||
*/
|
||||
class Converters {
|
||||
@TypeConverter
|
||||
fun fromString(value: String?): BigDecimal? {
|
||||
return value?.let { BigDecimal(it) }
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
fun bigDecimalToString(bigDecimal: BigDecimal?): String? {
|
||||
return bigDecimal?.toPlainString()
|
||||
}
|
||||
}
|
||||
|
||||
// [END_FILE_Converters.kt]
|
||||
@@ -0,0 +1,41 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db
|
||||
// [FILE] HomeboxDatabase.kt
|
||||
|
||||
package com.homebox.lens.data.db
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import com.homebox.lens.data.db.dao.ItemDao
|
||||
import com.homebox.lens.data.db.dao.LabelDao
|
||||
import com.homebox.lens.data.db.dao.LocationDao
|
||||
import com.homebox.lens.data.db.entity.*
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: RoomDatabase('HomeboxDatabase')]
|
||||
* [PURPOSE] Основной класс для работы с локальной базой данных Room.
|
||||
*/
|
||||
@Database(
|
||||
entities = [
|
||||
ItemEntity::class,
|
||||
LabelEntity::class,
|
||||
LocationEntity::class,
|
||||
ItemLabelCrossRef::class
|
||||
],
|
||||
version = 1,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(Converters::class)
|
||||
abstract class HomeboxDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun itemDao(): ItemDao
|
||||
abstract fun labelDao(): LabelDao
|
||||
abstract fun locationDao(): LocationDao
|
||||
|
||||
companion object {
|
||||
const val DATABASE_NAME = "homebox_lens_db"
|
||||
}
|
||||
}
|
||||
|
||||
// [END_FILE_HomeboxDatabase.kt]
|
||||
40
data/src/main/java/com/homebox/lens/data/db/dao/ItemDao.kt
Normal file
40
data/src/main/java/com/homebox/lens/data/db/dao/ItemDao.kt
Normal file
@@ -0,0 +1,40 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db.dao
|
||||
// [FILE] ItemDao.kt
|
||||
|
||||
package com.homebox.lens.data.db.dao
|
||||
|
||||
import androidx.room.*
|
||||
import com.homebox.lens.data.db.entity.ItemEntity
|
||||
import com.homebox.lens.data.db.entity.ItemLabelCrossRef
|
||||
import com.homebox.lens.data.db.entity.ItemWithLabels
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: RoomDao('ItemDao')]
|
||||
* [PURPOSE] Предоставляет методы для работы с 'items' в локальной БД.
|
||||
*/
|
||||
@Dao
|
||||
interface ItemDao {
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM items")
|
||||
suspend fun getItems(): List<ItemWithLabels>
|
||||
|
||||
@Transaction
|
||||
@Query("SELECT * FROM items WHERE id = :itemId")
|
||||
suspend fun getItem(itemId: String): ItemWithLabels?
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertItems(items: List<ItemEntity>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertItem(item: ItemEntity)
|
||||
|
||||
@Query("DELETE FROM items WHERE id = :itemId")
|
||||
suspend fun deleteItem(itemId: String)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertItemLabelCrossRefs(crossRefs: List<ItemLabelCrossRef>)
|
||||
}
|
||||
|
||||
// [END_FILE_ItemDao.kt]
|
||||
27
data/src/main/java/com/homebox/lens/data/db/dao/LabelDao.kt
Normal file
27
data/src/main/java/com/homebox/lens/data/db/dao/LabelDao.kt
Normal file
@@ -0,0 +1,27 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db.dao
|
||||
// [FILE] LabelDao.kt
|
||||
|
||||
package com.homebox.lens.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.homebox.lens.data.db.entity.LabelEntity
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: RoomDao('LabelDao')]
|
||||
* [PURPOSE] Предоставляет методы для работы с 'labels' в локальной БД.
|
||||
*/
|
||||
@Dao
|
||||
interface LabelDao {
|
||||
|
||||
@Query("SELECT * FROM labels")
|
||||
suspend fun getLabels(): List<LabelEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertLabels(labels: List<LabelEntity>)
|
||||
}
|
||||
|
||||
// [END_FILE_LabelDao.kt]
|
||||
@@ -0,0 +1,27 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db.dao
|
||||
// [FILE] LocationDao.kt
|
||||
|
||||
package com.homebox.lens.data.db.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import com.homebox.lens.data.db.entity.LocationEntity
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: RoomDao('LocationDao')]
|
||||
* [PURPOSE] Предоставляет методы для работы с 'locations' в локальной БД.
|
||||
*/
|
||||
@Dao
|
||||
interface LocationDao {
|
||||
|
||||
@Query("SELECT * FROM locations")
|
||||
suspend fun getLocations(): List<LocationEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertLocations(locations: List<LocationEntity>)
|
||||
}
|
||||
|
||||
// [END_FILE_LocationDao.kt]
|
||||
@@ -0,0 +1,26 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db.entity
|
||||
// [FILE] ItemEntity.kt
|
||||
|
||||
package com.homebox.lens.data.db.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import java.math.BigDecimal
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: RoomEntity('ItemEntity')]
|
||||
* [PURPOSE] Представляет собой строку в таблице 'items' в локальной БД.
|
||||
*/
|
||||
@Entity(tableName = "items")
|
||||
data class ItemEntity(
|
||||
@PrimaryKey val id: String,
|
||||
val name: String,
|
||||
val description: String?,
|
||||
val image: String?,
|
||||
val locationId: String?,
|
||||
val value: BigDecimal?,
|
||||
val createdAt: String?
|
||||
)
|
||||
|
||||
// [END_FILE_ItemEntity.kt]
|
||||
@@ -0,0 +1,19 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db.entity
|
||||
// [FILE] ItemLabelCrossRef.kt
|
||||
|
||||
package com.homebox.lens.data.db.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: RoomEntity('ItemLabelCrossRef')]
|
||||
* [PURPOSE] Таблица для связи "многие-ко-многим" между ItemEntity и LabelEntity.
|
||||
*/
|
||||
@Entity(primaryKeys = ["itemId", "labelId"])
|
||||
data class ItemLabelCrossRef(
|
||||
val itemId: String,
|
||||
val labelId: String
|
||||
)
|
||||
|
||||
// [END_FILE_ItemLabelCrossRef.kt]
|
||||
@@ -0,0 +1,29 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db.entity
|
||||
// [FILE] ItemWithLabels.kt
|
||||
|
||||
package com.homebox.lens.data.db.entity
|
||||
|
||||
import androidx.room.Embedded
|
||||
import androidx.room.Junction
|
||||
import androidx.room.Relation
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: Pojo('ItemWithLabels')]
|
||||
* [PURPOSE] POJO для получения ItemEntity вместе со связанными LabelEntity.
|
||||
*/
|
||||
data class ItemWithLabels(
|
||||
@Embedded val item: ItemEntity,
|
||||
@Relation(
|
||||
parentColumn = "id",
|
||||
entityColumn = "id",
|
||||
associateBy = Junction(
|
||||
value = ItemLabelCrossRef::class,
|
||||
parentColumn = "itemId",
|
||||
entityColumn = "labelId"
|
||||
)
|
||||
)
|
||||
val labels: List<LabelEntity>
|
||||
)
|
||||
|
||||
// [END_FILE_ItemWithLabels.kt]
|
||||
@@ -0,0 +1,20 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db.entity
|
||||
// [FILE] LabelEntity.kt
|
||||
|
||||
package com.homebox.lens.data.db.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: RoomEntity('LabelEntity')]
|
||||
* [PURPOSE] Представляет собой строку в таблице 'labels' в локальной БД.
|
||||
*/
|
||||
@Entity(tableName = "labels")
|
||||
data class LabelEntity(
|
||||
@PrimaryKey val id: String,
|
||||
val name: String
|
||||
)
|
||||
|
||||
// [END_FILE_LabelEntity.kt]
|
||||
@@ -0,0 +1,20 @@
|
||||
// [PACKAGE] com.homebox.lens.data.db.entity
|
||||
// [FILE] LocationEntity.kt
|
||||
|
||||
package com.homebox.lens.data.db.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [ENTITY: RoomEntity('LocationEntity')]
|
||||
* [PURPOSE] Представляет собой строку в таблице 'locations' в локальной БД.
|
||||
*/
|
||||
@Entity(tableName = "locations")
|
||||
data class LocationEntity(
|
||||
@PrimaryKey val id: String,
|
||||
val name: String
|
||||
)
|
||||
|
||||
// [END_FILE_LocationEntity.kt]
|
||||
77
data/src/main/java/com/homebox/lens/data/di/ApiModule.kt
Normal file
77
data/src/main/java/com/homebox/lens/data/di/ApiModule.kt
Normal file
@@ -0,0 +1,77 @@
|
||||
// [PACKAGE] com.homebox.lens.data.di
|
||||
// [FILE] ApiModule.kt
|
||||
|
||||
package com.homebox.lens.data.di
|
||||
|
||||
import com.homebox.lens.data.api.HomeboxApiService
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
import javax.inject.Singleton
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [MODULE: DaggerHilt('ApiModule')]
|
||||
* [PURPOSE] Предоставляет зависимости для работы с сетью (Retrofit, OkHttp, Moshi).
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object ApiModule {
|
||||
|
||||
// [HELPER]
|
||||
private const val BASE_URL = "https://api.homebox.app/"
|
||||
|
||||
// [PROVIDER]
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideOkHttpClient(): OkHttpClient {
|
||||
// [ACTION] Create logging interceptor
|
||||
val logging = HttpLoggingInterceptor().apply {
|
||||
level = HttpLoggingInterceptor.Level.BODY
|
||||
}
|
||||
// [ACTION] Build OkHttpClient
|
||||
return OkHttpClient.Builder()
|
||||
.addInterceptor(logging)
|
||||
// [TODO] Add AuthInterceptor for Bearer token
|
||||
.build()
|
||||
}
|
||||
|
||||
// [PROVIDER]
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideMoshi(): Moshi {
|
||||
// [ACTION] Build Moshi with Kotlin adapter
|
||||
return Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
}
|
||||
|
||||
// [PROVIDER]
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideRetrofit(okHttpClient: OkHttpClient, moshi: Moshi): Retrofit {
|
||||
// [ACTION] Build Retrofit instance
|
||||
return Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.build()
|
||||
}
|
||||
|
||||
// [PROVIDER]
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideHomeboxApiService(retrofit: Retrofit): HomeboxApiService {
|
||||
// [ACTION] Create ApiService from Retrofit instance
|
||||
return retrofit.create(HomeboxApiService::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
// [END_FILE_ApiModule.kt]
|
||||
@@ -0,0 +1,50 @@
|
||||
// [PACKAGE] com.homebox.lens.data.di
|
||||
// [FILE] DatabaseModule.kt
|
||||
|
||||
package com.homebox.lens.data.di
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import com.homebox.lens.data.db.HomeboxDatabase
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [MODULE: DaggerHilt('DatabaseModule')]
|
||||
* [PURPOSE] Предоставляет зависимости для работы с базой данных Room.
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
object DatabaseModule {
|
||||
|
||||
// [PROVIDER]
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideHomeboxDatabase(@ApplicationContext context: Context): HomeboxDatabase {
|
||||
// [ACTION] Build Room database instance
|
||||
return Room.databaseBuilder(
|
||||
context,
|
||||
HomeboxDatabase::class.java,
|
||||
HomeboxDatabase.DATABASE_NAME
|
||||
).build()
|
||||
}
|
||||
|
||||
// [PROVIDER]
|
||||
@Provides
|
||||
fun provideItemDao(database: HomeboxDatabase) = database.itemDao()
|
||||
|
||||
// [PROVIDER]
|
||||
@Provides
|
||||
fun provideLabelDao(database: HomeboxDatabase) = database.labelDao()
|
||||
|
||||
// [PROVIDER]
|
||||
@Provides
|
||||
fun provideLocationDao(database: HomeboxDatabase) = database.locationDao()
|
||||
}
|
||||
|
||||
// [END_FILE_DatabaseModule.kt]
|
||||
@@ -0,0 +1,31 @@
|
||||
// [PACKAGE] com.homebox.lens.data.di
|
||||
// [FILE] RepositoryModule.kt
|
||||
|
||||
package com.homebox.lens.data.di
|
||||
|
||||
import com.homebox.lens.data.repository.ItemRepositoryImpl
|
||||
import com.homebox.lens.domain.repository.ItemRepository
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [MODULE: DaggerHilt('RepositoryModule')]
|
||||
* [PURPOSE] Предоставляет реализацию для интерфейса ItemRepository.
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
abstract class RepositoryModule {
|
||||
|
||||
// [PROVIDER]
|
||||
@Binds
|
||||
@Singleton
|
||||
abstract fun bindItemRepository(
|
||||
itemRepositoryImpl: ItemRepositoryImpl
|
||||
): ItemRepository
|
||||
}
|
||||
|
||||
// [END_FILE_RepositoryModule.kt]
|
||||
@@ -0,0 +1,105 @@
|
||||
// [PACKAGE] com.homebox.lens.data.repository
|
||||
// [FILE] ItemRepositoryImpl.kt
|
||||
// [SEMANTICS] data_repository, implementation, network
|
||||
|
||||
// [IMPORTS]
|
||||
import com.homebox.lens.data.api.HomeboxApiService
|
||||
import com.homebox.lens.data.api.dto.toDomain
|
||||
import com.homebox.lens.data.api.dto.toDto
|
||||
import com.homebox.lens.domain.model.*
|
||||
import com.homebox.lens.domain.repository.ItemRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
// [CORE-LOGIC]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Реализация репозитория для работы с данными о вещах.
|
||||
* @param apiService Сервис для взаимодействия с Homebox API.
|
||||
*/
|
||||
class ItemRepositoryImpl @Inject constructor(
|
||||
private val apiService: HomeboxApiService
|
||||
) : ItemRepository {
|
||||
|
||||
/**
|
||||
* [CONTRACT] @see ItemRepository.createItem
|
||||
*/
|
||||
override suspend fun createItem(newItemData: ItemCreate): ItemSummary {
|
||||
// [ACTION]
|
||||
val itemDto = newItemData.toDto()
|
||||
val resultDto = apiService.createItem(itemDto)
|
||||
return resultDto.toDomain()
|
||||
}
|
||||
|
||||
/**
|
||||
* [CONTRACT] @see ItemRepository.getItemDetails
|
||||
*/
|
||||
override suspend fun getItemDetails(itemId: String): ItemOut {
|
||||
// [ACTION]
|
||||
val resultDto = apiService.getItem(itemId)
|
||||
return resultDto.toDomain()
|
||||
}
|
||||
|
||||
/**
|
||||
* [CONTRACT] @see ItemRepository.updateItem
|
||||
*/
|
||||
override suspend fun updateItem(itemId: String, item: ItemUpdate): ItemOut {
|
||||
// [ACTION]
|
||||
val itemDto = item.toDto()
|
||||
val resultDto = apiService.updateItem(itemId, itemDto)
|
||||
return resultDto.toDomain()
|
||||
}
|
||||
|
||||
/**
|
||||
* [CONTRACT] @see ItemRepository.deleteItem
|
||||
*/
|
||||
override suspend fun deleteItem(itemId: String) {
|
||||
// [ACTION]
|
||||
apiService.deleteItem(itemId)
|
||||
}
|
||||
|
||||
/**
|
||||
* [CONTRACT] @see ItemRepository.syncInventory
|
||||
*/
|
||||
override suspend fun syncInventory(page: Int, pageSize: Int): PaginationResult<ItemSummary> {
|
||||
// [ACTION]
|
||||
val resultDto = apiService.getItems(page = page, pageSize = pageSize)
|
||||
return resultDto.toDomain { it.toDomain() }
|
||||
}
|
||||
|
||||
/**
|
||||
* [CONTRACT] @see ItemRepository.getStatistics
|
||||
*/
|
||||
override suspend fun getStatistics(): GroupStatistics {
|
||||
// [ACTION]
|
||||
val resultDto = apiService.getStatistics()
|
||||
return resultDto.toDomain()
|
||||
}
|
||||
|
||||
/**
|
||||
* [CONTRACT] @see ItemRepository.getAllLocations
|
||||
*/
|
||||
override suspend fun getAllLocations(): List<LocationOutCount> {
|
||||
// [ACTION]
|
||||
val resultDto = apiService.getLocations()
|
||||
return resultDto.map { it.toDomain() }
|
||||
}
|
||||
|
||||
/**
|
||||
* [CONTRACT] @see ItemRepository.getAllLabels
|
||||
*/
|
||||
override suspend fun getAllLabels(): List<LabelOut> {
|
||||
// [ACTION]
|
||||
val resultDto = apiService.getLabels()
|
||||
return resultDto.map { it.toDomain() }
|
||||
}
|
||||
|
||||
/**
|
||||
* [CONTRACT] @see ItemRepository.searchItems
|
||||
*/
|
||||
override suspend fun searchItems(query: String): PaginationResult<ItemSummary> {
|
||||
// [ACTION]
|
||||
val resultDto = apiService.getItems(query = query)
|
||||
return resultDto.toDomain { it.toDomain() }
|
||||
}
|
||||
}
|
||||
// [END_FILE_ItemRepositoryImpl.kt]
|
||||
Reference in New Issue
Block a user