feat(#6): Implement full CRUD for Locations and Labels

This commit is contained in:
2025-09-02 17:03:05 +03:00
parent 8ebdc3a7b3
commit dd1a0c0c51
43 changed files with 1276 additions and 6786 deletions

View File

@@ -0,0 +1,17 @@
// [PACKAGE] com.homebox.lens.domain.model
// [FILE] LabelUpdate.kt
// [SEMANTICS] data_structure, contract, label, update
package com.homebox.lens.domain.model
// [ENTITY: DataClass('LabelUpdate')]
/**
* @summary Модель с данными, необходимыми для обновления метки.
* @param name Название метки.
* @param color Цвет метки в формате HEX.
*/
data class LabelUpdate(
val name: String?,
val color: String?
)
// [END_ENTITY: DataClass('LabelUpdate')]
// [END_FILE_LabelUpdate.kt]

View File

@@ -0,0 +1,18 @@
// [PACKAGE] com.homebox.lens.domain.model
// [FILE] LocationCreate.kt
// [SEMANTICS] data_structure, contract, location, create
package com.homebox.lens.domain.model
// [ENTITY: DataClass('LocationCreate')]
/**
* @summary Модель с данными, необходимыми для создания нового местоположения.
* @param name Название нового местоположения. Обязательное поле.
* @param color Цвет местоположения в формате HEX. Необязательное поле.
* @invariant name не может быть пустым.
*/
data class LocationCreate(
val name: String,
val color: String?
)
// [END_ENTITY: DataClass('LocationCreate')]
// [END_FILE_LocationCreate.kt]

View File

@@ -0,0 +1,17 @@
// [PACKAGE] com.homebox.lens.domain.model
// [FILE] LocationUpdate.kt
// [SEMANTICS] data_structure, contract, location, update
package com.homebox.lens.domain.model
// [ENTITY: DataClass('LocationUpdate')]
/**
* @summary Модель с данными, необходимыми для обновления местоположения.
* @param name Название местоположения.
* @param color Цвет местоположения в формате HEX.
*/
data class LocationUpdate(
val name: String?,
val color: String?
)
// [END_ENTITY: DataClass('LocationUpdate')]
// [END_FILE_LocationUpdate.kt]

View File

@@ -102,6 +102,54 @@ interface ItemRepository {
suspend fun createLabel(newLabelData: LabelCreate): LabelSummary
// [END_ENTITY: Function('createLabel')]
// [ENTITY: Function('updateLabel')]
// [RELATION: Function('updateLabel')] -> [RETURNS] -> [DataClass('LabelOut')]
/**
* @summary Обновляет метку.
* @param labelId ID метки для обновления.
* @param labelData Данные для обновления метки.
* @return Обновленная информация о метке.
*/
suspend fun updateLabel(labelId: String, labelData: LabelUpdate): LabelOut
// [END_ENTITY: Function('updateLabel')]
// [ENTITY: Function('deleteLabel')]
/**
* @summary Удаляет метку.
* @param labelId ID метки для удаления.
*/
suspend fun deleteLabel(labelId: String)
// [END_ENTITY: Function('deleteLabel')]
// [ENTITY: Function('createLocation')]
// [RELATION: Function('createLocation')] -> [RETURNS] -> [DataClass('LocationOut')]
/**
* @summary Создает новое местоположение.
* @param newLocationData Данные для создания нового местоположения.
* @return Информация о созданном местоположении.
*/
suspend fun createLocation(newLocationData: LocationCreate): LocationOut
// [END_ENTITY: Function('createLocation')]
// [ENTITY: Function('updateLocation')]
// [RELATION: Function('updateLocation')] -> [RETURNS] -> [DataClass('LocationOut')]
/**
* @summary Обновляет местоположение.
* @param locationId ID местоположения для обновления.
* @param locationData Данные для обновления местоположения.
* @return Обновленная информация о местоположении.
*/
suspend fun updateLocation(locationId: String, locationData: LocationUpdate): LocationOut
// [END_ENTITY: Function('updateLocation')]
// [ENTITY: Function('deleteLocation')]
/**
* @summary Удаляет местоположение.
* @param locationId ID местоположения для удаления.
*/
suspend fun deleteLocation(locationId: String)
// [END_ENTITY: Function('deleteLocation')]
// [ENTITY: Function('searchItems')]
// [RELATION: Function('searchItems')] -> [RETURNS] -> [DataClass('PaginationResult<ItemSummary>')]
/**

View File

@@ -0,0 +1,38 @@
// [PACKAGE] com.homebox.lens.domain.usecase
// [FILE] CreateLocationUseCase.kt
// [SEMANTICS] business_logic, use_case, location, create
package com.homebox.lens.domain.usecase
// [IMPORTS]
import com.homebox.lens.domain.model.LocationCreate
import com.homebox.lens.domain.model.LocationOut
import com.homebox.lens.domain.repository.ItemRepository
import javax.inject.Inject
// [END_IMPORTS]
// [ENTITY: UseCase('CreateLocationUseCase')]
// [RELATION: UseCase('CreateLocationUseCase')] -> [DEPENDS_ON] -> [Interface('ItemRepository')]
/**
* @summary Сценарий использования для создания нового местоположения.
* @param repository Репозиторий для доступа к данным.
*/
class CreateLocationUseCase @Inject constructor(
private val repository: ItemRepository
) {
// [ENTITY: Function('invoke')]
/**
* @summary Выполняет создание местоположения.
* @param newLocationData Данные для создания нового местоположения.
* @return Возвращает информацию о созданом местоположении [LocationOut].
* @throws Exception в случае ошибки на уровне репозитория (сеть, API).
* @precondition `newLocationData.name` не должен быть пустым.
*/
suspend operator fun invoke(newLocationData: LocationCreate): LocationOut {
require(newLocationData.name.isNotBlank()) { "Location name cannot be blank." }
return repository.createLocation(newLocationData)
}
// [END_ENTITY: Function('invoke')]
}
// [END_ENTITY: UseCase('CreateLocationUseCase')]
// [END_FILE_CreateLocationUseCase.kt]

View File

@@ -0,0 +1,32 @@
// [PACKAGE] com.homebox.lens.domain.usecase
// [FILE] DeleteLabelUseCase.kt
// [SEMANTICS] business_logic, use_case, label, delete
package com.homebox.lens.domain.usecase
// [IMPORTS]
import com.homebox.lens.domain.repository.ItemRepository
import javax.inject.Inject
// [END_IMPORTS]
// [ENTITY: UseCase('DeleteLabelUseCase')]
// [RELATION: UseCase('DeleteLabelUseCase')] -> [DEPENDS_ON] -> [Interface('ItemRepository')]
/**
* @summary Сценарий использования для удаления метки.
* @param repository Репозиторий для доступа к данным.
*/
class DeleteLabelUseCase @Inject constructor(
private val repository: ItemRepository
) {
// [ENTITY: Function('invoke')]
/**
* @summary Выполняет удаление метки.
* @param labelId ID метки для удаления.
* @throws Exception в случае ошибки на уровне репозитория (сеть, API).
*/
suspend operator fun invoke(labelId: String) {
repository.deleteLabel(labelId)
}
// [END_ENTITY: Function('invoke')]
}
// [END_ENTITY: UseCase('DeleteLabelUseCase')]
// [END_FILE_DeleteLabelUseCase.kt]

View File

@@ -0,0 +1,32 @@
// [PACKAGE] com.homebox.lens.domain.usecase
// [FILE] DeleteLocationUseCase.kt
// [SEMANTICS] business_logic, use_case, location, delete
package com.homebox.lens.domain.usecase
// [IMPORTS]
import com.homebox.lens.domain.repository.ItemRepository
import javax.inject.Inject
// [END_IMPORTS]
// [ENTITY: UseCase('DeleteLocationUseCase')]
// [RELATION: UseCase('DeleteLocationUseCase')] -> [DEPENDS_ON] -> [Interface('ItemRepository')]
/**
* @summary Сценарий использования для удаления местоположения.
* @param repository Репозиторий для доступа к данным.
*/
class DeleteLocationUseCase @Inject constructor(
private val repository: ItemRepository
) {
// [ENTITY: Function('invoke')]
/**
* @summary Выполняет удаление местоположения.
* @param locationId ID местоположения для удаления.
* @throws Exception в случае ошибки на уровне репозитория (сеть, API).
*/
suspend operator fun invoke(locationId: String) {
repository.deleteLocation(locationId)
}
// [END_ENTITY: Function('invoke')]
}
// [END_ENTITY: UseCase('DeleteLocationUseCase')]
// [END_FILE_DeleteLocationUseCase.kt]

View File

@@ -0,0 +1,36 @@
// [PACKAGE] com.homebox.lens.domain.usecase
// [FILE] UpdateLabelUseCase.kt
// [SEMANTICS] business_logic, use_case, label, update
package com.homebox.lens.domain.usecase
// [IMPORTS]
import com.homebox.lens.domain.model.LabelUpdate
import com.homebox.lens.domain.model.LabelOut
import com.homebox.lens.domain.repository.ItemRepository
import javax.inject.Inject
// [END_IMPORTS]
// [ENTITY: UseCase('UpdateLabelUseCase')]
// [RELATION: UseCase('UpdateLabelUseCase')] -> [DEPENDS_ON] -> [Interface('ItemRepository')]
/**
* @summary Сценарий использования для обновления метки.
* @param repository Репозиторий для доступа к данным.
*/
class UpdateLabelUseCase @Inject constructor(
private val repository: ItemRepository
) {
// [ENTITY: Function('invoke')]
/**
* @summary Выполняет обновление метки.
* @param labelId ID метки для обновления.
* @param labelData Данные для обновления метки.
* @return Возвращает информацию об обновленной метке [LabelOut].
* @throws Exception в случае ошибки на уровне репозитория (сеть, API).
*/
suspend operator fun invoke(labelId: String, labelData: LabelUpdate): LabelOut {
return repository.updateLabel(labelId, labelData)
}
// [END_ENTITY: Function('invoke')]
}
// [END_ENTITY: UseCase('UpdateLabelUseCase')]
// [END_FILE_UpdateLabelUseCase.kt]

View File

@@ -0,0 +1,36 @@
// [PACKAGE] com.homebox.lens.domain.usecase
// [FILE] UpdateLocationUseCase.kt
// [SEMANTICS] business_logic, use_case, location, update
package com.homebox.lens.domain.usecase
// [IMPORTS]
import com.homebox.lens.domain.model.LocationUpdate
import com.homebox.lens.domain.model.LocationOut
import com.homebox.lens.domain.repository.ItemRepository
import javax.inject.Inject
// [END_IMPORTS]
// [ENTITY: UseCase('UpdateLocationUseCase')]
// [RELATION: UseCase('UpdateLocationUseCase')] -> [DEPENDS_ON] -> [Interface('ItemRepository')]
/**
* @summary Сценарий использования для обновления местоположения.
* @param repository Репозиторий для доступа к данным.
*/
class UpdateLocationUseCase @Inject constructor(
private val repository: ItemRepository
) {
// [ENTITY: Function('invoke')]
/**
* @summary Выполняет обновление местоположения.
* @param locationId ID местоположения для обновления.
* @param locationData Данные для обновления местоположения.
* @return Возвращает информацию об обновленном местоположении [LocationOut].
* @throws Exception в случае ошибки на уровне репозитория (сеть, API).
*/
suspend operator fun invoke(locationId: String, locationData: LocationUpdate): LocationOut {
return repository.updateLocation(locationId, locationData)
}
// [END_ENTITY: Function('invoke')]
}
// [END_ENTITY: UseCase('UpdateLocationUseCase')]
// [END_FILE_UpdateLocationUseCase.kt]