Server : Add item service.
This commit is contained in:
parent
b09a6d5184
commit
0aaa56a4aa
24 changed files with 607 additions and 28 deletions
|
|
@ -114,7 +114,7 @@ class CharacterSheetStore(
|
||||||
|
|
||||||
private suspend fun handleMessage(message: SocketMessage) {
|
private suspend fun handleMessage(message: SocketMessage) {
|
||||||
when (message) {
|
when (message) {
|
||||||
is ApiSynchronisation -> try {
|
is ApiSynchronisation.CharacterSheetApiSynchronisation -> try {
|
||||||
when (message) {
|
when (message) {
|
||||||
is ApiSynchronisation.CharacterSheetUpdate -> {
|
is ApiSynchronisation.CharacterSheetUpdate -> {
|
||||||
_detailFlow.update(
|
_detailFlow.update(
|
||||||
|
|
@ -137,9 +137,6 @@ class CharacterSheetStore(
|
||||||
sheets.toMutableMap().also { it.remove(message.characterSheetId) }
|
sheets.toMutableMap().also { it.remove(message.characterSheetId) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ApiSynchronisation.AlterationUpdate -> Unit
|
|
||||||
is ApiSynchronisation.AlterationDelete -> Unit
|
|
||||||
}
|
}
|
||||||
} catch (exception: Exception) {
|
} catch (exception: Exception) {
|
||||||
println(exception.message) // TODO proper exception handling
|
println(exception.message) // TODO proper exception handling
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ import com.pixelized.server.lwa.model.campaign.CampaignService
|
||||||
import com.pixelized.server.lwa.model.campaign.CampaignStore
|
import com.pixelized.server.lwa.model.campaign.CampaignStore
|
||||||
import com.pixelized.server.lwa.model.character.CharacterSheetService
|
import com.pixelized.server.lwa.model.character.CharacterSheetService
|
||||||
import com.pixelized.server.lwa.model.character.CharacterSheetStore
|
import com.pixelized.server.lwa.model.character.CharacterSheetStore
|
||||||
|
import com.pixelized.server.lwa.model.item.ItemService
|
||||||
|
import com.pixelized.server.lwa.model.item.ItemStore
|
||||||
import com.pixelized.server.lwa.model.tag.TagStore
|
import com.pixelized.server.lwa.model.tag.TagStore
|
||||||
import com.pixelized.server.lwa.server.Engine
|
import com.pixelized.server.lwa.server.Engine
|
||||||
import com.pixelized.shared.lwa.utils.PathProvider
|
import com.pixelized.shared.lwa.utils.PathProvider
|
||||||
|
|
@ -37,6 +39,7 @@ val storeDependencies
|
||||||
singleOf(::CampaignStore)
|
singleOf(::CampaignStore)
|
||||||
singleOf(::AlterationStore)
|
singleOf(::AlterationStore)
|
||||||
singleOf(::TagStore)
|
singleOf(::TagStore)
|
||||||
|
singleOf(::ItemStore)
|
||||||
}
|
}
|
||||||
|
|
||||||
val serviceDependencies
|
val serviceDependencies
|
||||||
|
|
@ -44,4 +47,5 @@ val serviceDependencies
|
||||||
singleOf(::CharacterSheetService)
|
singleOf(::CharacterSheetService)
|
||||||
singleOf(::CampaignService)
|
singleOf(::CampaignService)
|
||||||
singleOf(::AlterationService)
|
singleOf(::AlterationService)
|
||||||
|
singleOf(::ItemService)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
class AlterationService(
|
class AlterationService(
|
||||||
private val alterationStore: AlterationStore,
|
private val alterationStore: AlterationStore,
|
||||||
tagStore: TagStore,
|
private val tagStore: TagStore,
|
||||||
factory: AlterationJsonFactory,
|
factory: AlterationJsonFactory,
|
||||||
) {
|
) {
|
||||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
|
|
@ -26,26 +26,18 @@ class AlterationService(
|
||||||
initialValue = emptyMap()
|
initialValue = emptyMap()
|
||||||
)
|
)
|
||||||
|
|
||||||
private val alterationTags = tagStore.alterationTags()
|
|
||||||
.map { it.values.toList() }
|
|
||||||
.stateIn(
|
|
||||||
scope = scope,
|
|
||||||
started = SharingStarted.Eagerly,
|
|
||||||
initialValue = emptyList()
|
|
||||||
)
|
|
||||||
|
|
||||||
fun alterations(): List<AlterationJson> {
|
fun alterations(): List<AlterationJson> {
|
||||||
return alterationHashFlow.value.values.toList()
|
return alterationHashFlow.value.values.toList()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tags(): List<TagJson> {
|
|
||||||
return alterationTags.value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun alteration(alterationId: String): AlterationJson? {
|
fun alteration(alterationId: String): AlterationJson? {
|
||||||
return alterationHashFlow.value[alterationId]
|
return alterationHashFlow.value[alterationId]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun tags(): List<TagJson> {
|
||||||
|
return tagStore.alterationTags().value.values.toList()
|
||||||
|
}
|
||||||
|
|
||||||
@Throws
|
@Throws
|
||||||
fun save(
|
fun save(
|
||||||
json: AlterationJson,
|
json: AlterationJson,
|
||||||
|
|
@ -59,8 +51,6 @@ class AlterationService(
|
||||||
|
|
||||||
@Throws
|
@Throws
|
||||||
fun delete(alterationId: String) {
|
fun delete(alterationId: String) {
|
||||||
return alterationStore.delete(
|
alterationStore.delete(id = alterationId)
|
||||||
id = alterationId,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,56 @@
|
||||||
|
package com.pixelized.server.lwa.model.item
|
||||||
|
|
||||||
|
import com.pixelized.server.lwa.model.tag.TagStore
|
||||||
|
import com.pixelized.shared.lwa.model.item.ItemJson
|
||||||
|
import com.pixelized.shared.lwa.model.item.factory.ItemJsonFactory
|
||||||
|
import com.pixelized.shared.lwa.model.tag.TagJson
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
|
class ItemService(
|
||||||
|
private val itemStore: ItemStore,
|
||||||
|
private val tagStore: TagStore,
|
||||||
|
factory: ItemJsonFactory,
|
||||||
|
) {
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
|
|
||||||
|
private val itemHashFlow = itemStore.itemsFlow()
|
||||||
|
.map { items -> items.associate { it.id to factory.convertToJson(it) } }
|
||||||
|
.stateIn(
|
||||||
|
scope = scope,
|
||||||
|
started = SharingStarted.Eagerly,
|
||||||
|
initialValue = emptyMap()
|
||||||
|
)
|
||||||
|
|
||||||
|
fun items(): List<ItemJson> {
|
||||||
|
return itemHashFlow.value.values.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun item(itemId: String): ItemJson? {
|
||||||
|
return itemHashFlow.value[itemId]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun tags(): List<TagJson> {
|
||||||
|
return tagStore.itemTags().value.values.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws
|
||||||
|
fun save(
|
||||||
|
json: ItemJson,
|
||||||
|
create: Boolean,
|
||||||
|
) {
|
||||||
|
itemStore.save(
|
||||||
|
json = json,
|
||||||
|
create = create,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws
|
||||||
|
fun delete(itemId: String) {
|
||||||
|
itemStore.delete(id = itemId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
package com.pixelized.server.lwa.model.item
|
||||||
|
|
||||||
|
import com.pixelized.server.lwa.server.exception.BusinessException
|
||||||
|
import com.pixelized.server.lwa.server.exception.FileReadException
|
||||||
|
import com.pixelized.server.lwa.server.exception.FileWriteException
|
||||||
|
import com.pixelized.server.lwa.server.exception.JsonCodingException
|
||||||
|
import com.pixelized.server.lwa.server.exception.JsonConversionException
|
||||||
|
import com.pixelized.shared.lwa.model.item.Item
|
||||||
|
import com.pixelized.shared.lwa.model.item.ItemJson
|
||||||
|
import com.pixelized.shared.lwa.model.item.factory.ItemJsonFactory
|
||||||
|
import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
||||||
|
import com.pixelized.shared.lwa.utils.PathProvider
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import java.io.File
|
||||||
|
import java.text.Collator
|
||||||
|
|
||||||
|
class ItemStore(
|
||||||
|
private val pathProvider: PathProvider,
|
||||||
|
private val factory: ItemJsonFactory,
|
||||||
|
private val json: Json,
|
||||||
|
) {
|
||||||
|
private val directory = File(pathProvider.itemsPath()).also { it.mkdirs() }
|
||||||
|
|
||||||
|
private val itemFlow = MutableStateFlow<List<Item>>(emptyList())
|
||||||
|
|
||||||
|
init {
|
||||||
|
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||||
|
|
||||||
|
scope.launch {
|
||||||
|
updateItemsFlow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun itemsFlow(): StateFlow<List<Item>> = itemFlow
|
||||||
|
|
||||||
|
private fun updateItemsFlow() {
|
||||||
|
itemFlow.value = try {
|
||||||
|
load()
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
println(exception.message) // TODO proper exception handling
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(
|
||||||
|
FileReadException::class,
|
||||||
|
JsonConversionException::class,
|
||||||
|
)
|
||||||
|
private fun load(
|
||||||
|
directory: File = this.directory,
|
||||||
|
): List<Item> {
|
||||||
|
return directory
|
||||||
|
.listFiles()
|
||||||
|
?.mapNotNull { file ->
|
||||||
|
val json = try {
|
||||||
|
file.readText(charset = Charsets.UTF_8)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
throw FileReadException(root = exception)
|
||||||
|
}
|
||||||
|
// Guard, if the json is blank no item have been save, ignore this file.
|
||||||
|
if (json.isBlank()) {
|
||||||
|
return@mapNotNull null
|
||||||
|
}
|
||||||
|
// decode the file
|
||||||
|
val data = try {
|
||||||
|
this.json.decodeFromString<ItemJson>(json)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
throw JsonCodingException(root = exception)
|
||||||
|
}
|
||||||
|
// parse the json string.
|
||||||
|
try {
|
||||||
|
factory.convertFromJson(data)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
throw JsonConversionException(root = exception)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?.sortedWith(compareBy(Collator.getInstance()) { it.metadata.name })
|
||||||
|
?: emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(
|
||||||
|
BusinessException::class,
|
||||||
|
JsonConversionException::class,
|
||||||
|
JsonCodingException::class,
|
||||||
|
FileWriteException::class,
|
||||||
|
)
|
||||||
|
fun save(
|
||||||
|
json: ItemJson,
|
||||||
|
create: Boolean,
|
||||||
|
) {
|
||||||
|
val file = itemFile(id = json.id)
|
||||||
|
// Guard case on update alteration
|
||||||
|
if (create && file.exists()) {
|
||||||
|
throw BusinessException(
|
||||||
|
message = "Item already exist, creation is impossible.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Transform the json into the model.
|
||||||
|
val item = try {
|
||||||
|
factory.convertFromJson(json)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
throw JsonConversionException(root = exception)
|
||||||
|
}
|
||||||
|
if (item.id.isEmpty()) {
|
||||||
|
throw BusinessException(
|
||||||
|
message = "Item 'id' is a mandatory field.",
|
||||||
|
code = APIResponse.ErrorCode.ItemId,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (item.metadata.name.isEmpty()) {
|
||||||
|
throw BusinessException(
|
||||||
|
message = "Item 'name' is a mandatory field.",
|
||||||
|
code = APIResponse.ErrorCode.ItemName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Encode the json into a string.
|
||||||
|
val data = try {
|
||||||
|
this.json.encodeToString(json)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
throw JsonCodingException(root = exception)
|
||||||
|
}
|
||||||
|
// Write the alteration into a file.
|
||||||
|
try {
|
||||||
|
file.writeText(
|
||||||
|
text = data,
|
||||||
|
charset = Charsets.UTF_8,
|
||||||
|
)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
throw FileWriteException(root = exception)
|
||||||
|
}
|
||||||
|
// Update the dataflow.
|
||||||
|
itemFlow.update { items ->
|
||||||
|
val index = items.indexOfFirst { it.id == json.id }
|
||||||
|
items.toMutableList()
|
||||||
|
.also {
|
||||||
|
if (index >= 0) {
|
||||||
|
it[index] = item
|
||||||
|
} else {
|
||||||
|
it.add(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.sortedWith(compareBy(Collator.getInstance()) {
|
||||||
|
it.metadata.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(BusinessException::class)
|
||||||
|
fun delete(id: String) {
|
||||||
|
val file = itemFile(id = id)
|
||||||
|
// Guard case on the file existence.
|
||||||
|
if (file.exists().not()) {
|
||||||
|
throw BusinessException(
|
||||||
|
message = "Item doesn't not exist, deletion is impossible.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Guard case on the file deletion
|
||||||
|
if (file.delete().not()) {
|
||||||
|
throw BusinessException(
|
||||||
|
message = "Item file have not been deleted for unknown reason.",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Update the data model with the deleted alteration.
|
||||||
|
itemFlow.update { items ->
|
||||||
|
items.toMutableList()
|
||||||
|
.also { item ->
|
||||||
|
item.removeIf { it.id == id }
|
||||||
|
}
|
||||||
|
.sortedWith(compareBy(Collator.getInstance()) {
|
||||||
|
it.metadata.name
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun itemFile(id: String): File {
|
||||||
|
return File("${pathProvider.itemsPath()}${id}.json")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,6 +16,7 @@ import java.io.File
|
||||||
|
|
||||||
private const val CHARACTER = "character"
|
private const val CHARACTER = "character"
|
||||||
private const val ALTERATION = "alteration"
|
private const val ALTERATION = "alteration"
|
||||||
|
private const val ITEM = "item"
|
||||||
|
|
||||||
class TagStore(
|
class TagStore(
|
||||||
private val pathProvider: PathProvider,
|
private val pathProvider: PathProvider,
|
||||||
|
|
@ -23,6 +24,7 @@ class TagStore(
|
||||||
) {
|
) {
|
||||||
private val alterationTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
private val alterationTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
||||||
private val characterTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
private val characterTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
||||||
|
private val itemTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
// make the file path.
|
// make the file path.
|
||||||
|
|
@ -39,11 +41,16 @@ class TagStore(
|
||||||
flow = characterTagsFlow,
|
flow = characterTagsFlow,
|
||||||
file = characterFile(),
|
file = characterFile(),
|
||||||
)
|
)
|
||||||
|
update(
|
||||||
|
flow = itemTagsFlow,
|
||||||
|
file = itemFile(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun alterationTags(): StateFlow<Map<String, TagJson>> = alterationTagsFlow
|
fun alterationTags(): StateFlow<Map<String, TagJson>> = alterationTagsFlow
|
||||||
fun characterTags(): StateFlow<Map<String, TagJson>> = characterTagsFlow
|
fun characterTags(): StateFlow<Map<String, TagJson>> = characterTagsFlow
|
||||||
|
fun itemTags(): StateFlow<Map<String, TagJson>> = itemTagsFlow
|
||||||
|
|
||||||
private fun update(
|
private fun update(
|
||||||
flow: MutableStateFlow<Map<String, TagJson>>,
|
flow: MutableStateFlow<Map<String, TagJson>>,
|
||||||
|
|
@ -101,6 +108,6 @@ class TagStore(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun characterFile() = File("${pathProvider.tagsPath()}$CHARACTER.json")
|
private fun characterFile() = File("${pathProvider.tagsPath()}$CHARACTER.json")
|
||||||
|
|
||||||
private fun alterationFile() = File("${pathProvider.tagsPath()}$ALTERATION.json")
|
private fun alterationFile() = File("${pathProvider.tagsPath()}$ALTERATION.json")
|
||||||
|
private fun itemFile() = File("${pathProvider.tagsPath()}$ITEM.json")
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package com.pixelized.server.lwa.server
|
||||||
import com.pixelized.server.lwa.model.alteration.AlterationService
|
import com.pixelized.server.lwa.model.alteration.AlterationService
|
||||||
import com.pixelized.server.lwa.model.campaign.CampaignService
|
import com.pixelized.server.lwa.model.campaign.CampaignService
|
||||||
import com.pixelized.server.lwa.model.character.CharacterSheetService
|
import com.pixelized.server.lwa.model.character.CharacterSheetService
|
||||||
|
import com.pixelized.server.lwa.model.item.ItemService
|
||||||
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
|
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation
|
import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
|
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
|
||||||
|
|
@ -16,6 +17,7 @@ class Engine(
|
||||||
val characterService: CharacterSheetService,
|
val characterService: CharacterSheetService,
|
||||||
val campaignService: CampaignService,
|
val campaignService: CampaignService,
|
||||||
val alterationService: AlterationService,
|
val alterationService: AlterationService,
|
||||||
|
val itemService: ItemService,
|
||||||
val campaignJsonFactory: CampaignJsonFactory,
|
val campaignJsonFactory: CampaignJsonFactory,
|
||||||
) {
|
) {
|
||||||
val webSocket = MutableSharedFlow<SocketMessage>()
|
val webSocket = MutableSharedFlow<SocketMessage>()
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,11 @@ import com.pixelized.server.lwa.server.rest.character.putCharacterAlteration
|
||||||
import com.pixelized.server.lwa.server.rest.character.putCharacterDamage
|
import com.pixelized.server.lwa.server.rest.character.putCharacterDamage
|
||||||
import com.pixelized.server.lwa.server.rest.character.putCharacterDiminished
|
import com.pixelized.server.lwa.server.rest.character.putCharacterDiminished
|
||||||
import com.pixelized.server.lwa.server.rest.character.putCharacterFatigue
|
import com.pixelized.server.lwa.server.rest.character.putCharacterFatigue
|
||||||
|
import com.pixelized.server.lwa.server.rest.item.deleteItem
|
||||||
|
import com.pixelized.server.lwa.server.rest.item.getItem
|
||||||
|
import com.pixelized.server.lwa.server.rest.item.getItemTags
|
||||||
|
import com.pixelized.server.lwa.server.rest.item.getItems
|
||||||
|
import com.pixelized.server.lwa.server.rest.item.putItem
|
||||||
import com.pixelized.shared.lwa.SERVER_PORT
|
import com.pixelized.shared.lwa.SERVER_PORT
|
||||||
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
|
import com.pixelized.shared.lwa.protocol.websocket.SocketMessage
|
||||||
import com.pixelized.shared.lwa.sharedModuleDependencies
|
import com.pixelized.shared.lwa.sharedModuleDependencies
|
||||||
|
|
@ -113,12 +118,13 @@ class LocalServer {
|
||||||
}
|
}
|
||||||
}.onFailure { exception ->
|
}.onFailure { exception ->
|
||||||
// TODO
|
// TODO
|
||||||
println("WebSocket exception: ${exception.localizedMessage}")
|
println("WebSocket exception: ${exception.message}")
|
||||||
}.also {
|
}.also {
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
// TODO Tags.
|
||||||
route(
|
route(
|
||||||
path = "/alteration",
|
path = "/alteration",
|
||||||
) {
|
) {
|
||||||
|
|
@ -217,6 +223,28 @@ class LocalServer {
|
||||||
body = engine.putCampaignScene(),
|
body = engine.putCampaignScene(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
route(path = "item") {
|
||||||
|
get(
|
||||||
|
path = "/all",
|
||||||
|
body = engine.getItems(),
|
||||||
|
)
|
||||||
|
get(
|
||||||
|
path = "/detail",
|
||||||
|
body = engine.getItem(),
|
||||||
|
)
|
||||||
|
get(
|
||||||
|
path = "/tags",
|
||||||
|
body = engine.getItemTags(),
|
||||||
|
)
|
||||||
|
put(
|
||||||
|
path = "/update",
|
||||||
|
body = engine.putItem(),
|
||||||
|
)
|
||||||
|
delete(
|
||||||
|
path = "/delete",
|
||||||
|
body = engine.deleteItem(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,7 @@ fun Engine.getAlteration(): suspend RoutingContext.() -> Unit {
|
||||||
// get the query parameter
|
// get the query parameter
|
||||||
val alterationId = call.queryParameters.alterationId
|
val alterationId = call.queryParameters.alterationId
|
||||||
// get the alteration of the given id.
|
// get the alteration of the given id.
|
||||||
val alteration = alterationService
|
val alteration = alterationService.alteration(alterationId = alterationId)
|
||||||
.alteration(alterationId = alterationId)
|
|
||||||
?: error("Alteration with id:$alterationId not found.")
|
?: error("Alteration with id:$alterationId not found.")
|
||||||
// send it back to the user.
|
// send it back to the user.
|
||||||
call.respond(
|
call.respond(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.pixelized.server.lwa.server.rest.item
|
||||||
|
|
||||||
|
import com.pixelized.server.lwa.server.Engine
|
||||||
|
import com.pixelized.server.lwa.utils.extentions.exception
|
||||||
|
import com.pixelized.server.lwa.utils.extentions.itemId
|
||||||
|
import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.RoutingContext
|
||||||
|
|
||||||
|
fun Engine.deleteItem(): suspend RoutingContext.() -> Unit {
|
||||||
|
return {
|
||||||
|
try {
|
||||||
|
// get the query parameter
|
||||||
|
val itemId = call.parameters.itemId
|
||||||
|
// delete the alteration.
|
||||||
|
itemService.delete(
|
||||||
|
itemId = itemId
|
||||||
|
)
|
||||||
|
// API & WebSocket responses.
|
||||||
|
call.respond(
|
||||||
|
message = APIResponse.success(),
|
||||||
|
)
|
||||||
|
webSocket.emit(
|
||||||
|
value = ApiSynchronisation.ItemDelete(
|
||||||
|
timestamp = System.currentTimeMillis(),
|
||||||
|
itemId = itemId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
call.exception(
|
||||||
|
exception = exception,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.pixelized.server.lwa.server.rest.item
|
||||||
|
|
||||||
|
import com.pixelized.server.lwa.server.Engine
|
||||||
|
import com.pixelized.server.lwa.utils.extentions.exception
|
||||||
|
import com.pixelized.server.lwa.utils.extentions.itemId
|
||||||
|
import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.RoutingContext
|
||||||
|
|
||||||
|
fun Engine.getItem(): suspend RoutingContext.() -> Unit {
|
||||||
|
return {
|
||||||
|
try {
|
||||||
|
// get the query parameter
|
||||||
|
val itemId = call.queryParameters.itemId
|
||||||
|
// get the alteration of the given id.
|
||||||
|
val item = itemService.item(itemId = itemId)
|
||||||
|
?: error("Item with id:$itemId not found.")
|
||||||
|
// send it back to the user.
|
||||||
|
call.respond(
|
||||||
|
message = APIResponse.success(
|
||||||
|
data = item
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
call.exception(
|
||||||
|
exception = exception,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.pixelized.server.lwa.server.rest.item
|
||||||
|
|
||||||
|
import com.pixelized.server.lwa.server.Engine
|
||||||
|
import com.pixelized.server.lwa.utils.extentions.exception
|
||||||
|
import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.RoutingContext
|
||||||
|
|
||||||
|
fun Engine.getItemTags(): suspend RoutingContext.() -> Unit {
|
||||||
|
return {
|
||||||
|
try {
|
||||||
|
call.respond(
|
||||||
|
message = APIResponse.success(
|
||||||
|
data = itemService.tags(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
call.exception(
|
||||||
|
exception = exception,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.pixelized.server.lwa.server.rest.item
|
||||||
|
|
||||||
|
import com.pixelized.server.lwa.server.Engine
|
||||||
|
import com.pixelized.server.lwa.utils.extentions.exception
|
||||||
|
import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.RoutingContext
|
||||||
|
|
||||||
|
fun Engine.getItems(): suspend RoutingContext.() -> Unit {
|
||||||
|
return {
|
||||||
|
try {
|
||||||
|
call.respond(
|
||||||
|
message = APIResponse.success(
|
||||||
|
data = itemService.items(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
call.exception(
|
||||||
|
exception = exception,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.pixelized.server.lwa.server.rest.item
|
||||||
|
|
||||||
|
import com.pixelized.server.lwa.server.Engine
|
||||||
|
import com.pixelized.server.lwa.utils.extentions.create
|
||||||
|
import com.pixelized.server.lwa.utils.extentions.exception
|
||||||
|
import com.pixelized.shared.lwa.model.alteration.AlterationJson
|
||||||
|
import com.pixelized.shared.lwa.model.item.ItemJson
|
||||||
|
import com.pixelized.shared.lwa.protocol.rest.APIResponse
|
||||||
|
import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation
|
||||||
|
import io.ktor.server.request.receive
|
||||||
|
import io.ktor.server.response.respond
|
||||||
|
import io.ktor.server.routing.RoutingContext
|
||||||
|
|
||||||
|
fun Engine.putItem(): suspend RoutingContext.() -> Unit {
|
||||||
|
return {
|
||||||
|
try {
|
||||||
|
val form = call.receive<ItemJson>()
|
||||||
|
val create = call.queryParameters.create
|
||||||
|
|
||||||
|
itemService.save(
|
||||||
|
json = form,
|
||||||
|
create = create,
|
||||||
|
)
|
||||||
|
call.respond(
|
||||||
|
message = APIResponse.success(),
|
||||||
|
)
|
||||||
|
webSocket.emit(
|
||||||
|
value = ApiSynchronisation.ItemUpdate(
|
||||||
|
timestamp = System.currentTimeMillis(),
|
||||||
|
itemId = form.id,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
call.exception(
|
||||||
|
exception = exception,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,11 @@ val Parameters.alterationId
|
||||||
this[param] ?: throw MissingParameterException(name = param)
|
this[param] ?: throw MissingParameterException(name = param)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val Parameters.itemId
|
||||||
|
get() = "itemId".let { param ->
|
||||||
|
this[param] ?: throw MissingParameterException(name = param)
|
||||||
|
}
|
||||||
|
|
||||||
val Parameters.create
|
val Parameters.create
|
||||||
get() = "create".let { param ->
|
get() = "create".let { param ->
|
||||||
this[param]?.toBooleanStrictOrNull() ?: throw MissingParameterException(name = param)
|
this[param]?.toBooleanStrictOrNull() ?: throw MissingParameterException(name = param)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonV1Factory
|
||||||
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonV2Factory
|
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonV2Factory
|
||||||
import com.pixelized.shared.lwa.model.characterSheet.factory.CharacterSheetJsonFactory
|
import com.pixelized.shared.lwa.model.characterSheet.factory.CharacterSheetJsonFactory
|
||||||
import com.pixelized.shared.lwa.model.characterSheet.factory.CharacterSheetJsonV1Factory
|
import com.pixelized.shared.lwa.model.characterSheet.factory.CharacterSheetJsonV1Factory
|
||||||
|
import com.pixelized.shared.lwa.model.item.factory.ItemJsonFactory
|
||||||
|
import com.pixelized.shared.lwa.model.item.factory.ItemJsonFactoryV1
|
||||||
import com.pixelized.shared.lwa.model.tag.TagJsonFactory
|
import com.pixelized.shared.lwa.model.tag.TagJsonFactory
|
||||||
import com.pixelized.shared.lwa.parser.dice.DiceParser
|
import com.pixelized.shared.lwa.parser.dice.DiceParser
|
||||||
import com.pixelized.shared.lwa.parser.expression.ExpressionParser
|
import com.pixelized.shared.lwa.parser.expression.ExpressionParser
|
||||||
|
|
@ -48,6 +50,8 @@ val factoryDependencies
|
||||||
factoryOf(::AlteredCharacterSheetFactory)
|
factoryOf(::AlteredCharacterSheetFactory)
|
||||||
factoryOf(::AlterationJsonFactory)
|
factoryOf(::AlterationJsonFactory)
|
||||||
factoryOf(::TagJsonFactory)
|
factoryOf(::TagJsonFactory)
|
||||||
|
factoryOf(::ItemJsonFactory)
|
||||||
|
factoryOf(::ItemJsonFactoryV1)
|
||||||
}
|
}
|
||||||
|
|
||||||
val parserDependencies
|
val parserDependencies
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package com.pixelized.shared.lwa.model.item
|
||||||
|
|
||||||
|
data class Item(
|
||||||
|
val id: String,
|
||||||
|
val metadata: MetaData,
|
||||||
|
val options: Options,
|
||||||
|
val tags: List<String>,
|
||||||
|
val alterations: List<String>,
|
||||||
|
) {
|
||||||
|
data class MetaData(
|
||||||
|
val name: String,
|
||||||
|
val description: String,
|
||||||
|
val thumbnail: String?,
|
||||||
|
val image: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Options(
|
||||||
|
val stackable: Boolean,
|
||||||
|
val equipable: Boolean,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package com.pixelized.shared.lwa.model.item
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed interface ItemJson {
|
||||||
|
val id: String
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package com.pixelized.shared.lwa.model.item
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ItemJsonV1(
|
||||||
|
override val id: String,
|
||||||
|
val metadata: ItemMetadataJsonV1,
|
||||||
|
val options: ItemOptionJsonV1,
|
||||||
|
val tags: List<String>,
|
||||||
|
val alterations: List<String>,
|
||||||
|
) : ItemJson {
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ItemMetadataJsonV1(
|
||||||
|
val name: String,
|
||||||
|
val description: String,
|
||||||
|
val thumbnail: String?,
|
||||||
|
val image: String?,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ItemOptionJsonV1(
|
||||||
|
val stackable: Boolean,
|
||||||
|
val equipable: Boolean,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package com.pixelized.shared.lwa.model.item.factory
|
||||||
|
|
||||||
|
import com.pixelized.shared.lwa.model.item.Item
|
||||||
|
import com.pixelized.shared.lwa.model.item.ItemJson
|
||||||
|
import com.pixelized.shared.lwa.model.item.ItemJsonV1
|
||||||
|
|
||||||
|
class ItemJsonFactory(
|
||||||
|
private val v1: ItemJsonFactoryV1,
|
||||||
|
) {
|
||||||
|
fun convertFromJson(json: ItemJson): Item {
|
||||||
|
return when (json) {
|
||||||
|
is ItemJsonV1 -> v1.convertFromJson(json = json)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertToJson(item: Item): ItemJson {
|
||||||
|
return v1.convertToJson(item = item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
package com.pixelized.shared.lwa.model.item.factory
|
||||||
|
|
||||||
|
import com.pixelized.shared.lwa.model.item.Item
|
||||||
|
import com.pixelized.shared.lwa.model.item.ItemJsonV1
|
||||||
|
|
||||||
|
class ItemJsonFactoryV1 {
|
||||||
|
|
||||||
|
fun convertFromJson(json: ItemJsonV1): Item {
|
||||||
|
return Item(
|
||||||
|
id = json.id,
|
||||||
|
metadata = Item.MetaData(
|
||||||
|
name = json.metadata.name,
|
||||||
|
description = json.metadata.description,
|
||||||
|
image = json.metadata.image,
|
||||||
|
thumbnail = json.metadata.thumbnail,
|
||||||
|
),
|
||||||
|
options = Item.Options(
|
||||||
|
stackable = json.options.stackable,
|
||||||
|
equipable = json.options.equipable,
|
||||||
|
),
|
||||||
|
tags = json.tags,
|
||||||
|
alterations = json.alterations,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun convertToJson(item: Item): ItemJsonV1 {
|
||||||
|
return ItemJsonV1(
|
||||||
|
id = item.id,
|
||||||
|
metadata = ItemJsonV1.ItemMetadataJsonV1(
|
||||||
|
name = item.metadata.name,
|
||||||
|
description = item.metadata.description,
|
||||||
|
image = item.metadata.image,
|
||||||
|
thumbnail = item.metadata.thumbnail,
|
||||||
|
),
|
||||||
|
options = ItemJsonV1.ItemOptionJsonV1(
|
||||||
|
stackable = item.options.stackable,
|
||||||
|
equipable = item.options.equipable,
|
||||||
|
),
|
||||||
|
tags = item.tags,
|
||||||
|
alterations = item.alterations,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,8 @@ data class APIResponse<T>(
|
||||||
enum class ErrorCode {
|
enum class ErrorCode {
|
||||||
AlterationId,
|
AlterationId,
|
||||||
AlterationName,
|
AlterationName,
|
||||||
|
ItemId,
|
||||||
|
ItemName,
|
||||||
CharacterSheetId,
|
CharacterSheetId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,27 +5,48 @@ import kotlinx.serialization.Serializable
|
||||||
@Serializable
|
@Serializable
|
||||||
sealed interface ApiSynchronisation : SocketMessage {
|
sealed interface ApiSynchronisation : SocketMessage {
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed interface CharacterSheetApiSynchronisation : ApiSynchronisation, CharacterSheetIdMessage
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class CharacterSheetDelete(
|
data class CharacterSheetDelete(
|
||||||
override val timestamp: Long,
|
override val timestamp: Long,
|
||||||
override val characterSheetId: String,
|
override val characterSheetId: String,
|
||||||
) : ApiSynchronisation, CharacterSheetIdMessage
|
) : CharacterSheetApiSynchronisation
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class CharacterSheetUpdate(
|
data class CharacterSheetUpdate(
|
||||||
override val timestamp: Long,
|
override val timestamp: Long,
|
||||||
override val characterSheetId: String,
|
override val characterSheetId: String,
|
||||||
) : ApiSynchronisation, CharacterSheetIdMessage
|
) : CharacterSheetApiSynchronisation
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed interface AlterationApiSynchronisation : ApiSynchronisation
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class AlterationUpdate(
|
data class AlterationUpdate(
|
||||||
override val timestamp: Long,
|
override val timestamp: Long,
|
||||||
val alterationId: String,
|
val alterationId: String,
|
||||||
) : ApiSynchronisation
|
) : AlterationApiSynchronisation
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class AlterationDelete(
|
data class AlterationDelete(
|
||||||
override val timestamp: Long,
|
override val timestamp: Long,
|
||||||
val alterationId: String,
|
val alterationId: String,
|
||||||
) : ApiSynchronisation
|
) : AlterationApiSynchronisation
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
sealed interface ItemApiSynchronisation : ApiSynchronisation
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ItemUpdate(
|
||||||
|
override val timestamp: Long,
|
||||||
|
val itemId: String,
|
||||||
|
) : ItemApiSynchronisation
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class ItemDelete(
|
||||||
|
override val timestamp: Long,
|
||||||
|
val itemId: String,
|
||||||
|
) : ItemApiSynchronisation
|
||||||
}
|
}
|
||||||
|
|
@ -54,6 +54,16 @@ class PathProvider(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun itemsPath(
|
||||||
|
os: OperatingSystem = this.operatingSystem,
|
||||||
|
app: String = this.appName,
|
||||||
|
): String {
|
||||||
|
return when (os) {
|
||||||
|
OperatingSystem.Windows -> "${storePath(os = os, app = app)}items\\"
|
||||||
|
OperatingSystem.Macintosh -> "${storePath(os = os, app = app)}items/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun tagsPath(
|
fun tagsPath(
|
||||||
os: OperatingSystem = this.operatingSystem,
|
os: OperatingSystem = this.operatingSystem,
|
||||||
app: String = this.appName,
|
app: String = this.appName,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue