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) {
|
||||
when (message) {
|
||||
is ApiSynchronisation -> try {
|
||||
is ApiSynchronisation.CharacterSheetApiSynchronisation -> try {
|
||||
when (message) {
|
||||
is ApiSynchronisation.CharacterSheetUpdate -> {
|
||||
_detailFlow.update(
|
||||
|
|
@ -137,9 +137,6 @@ class CharacterSheetStore(
|
|||
sheets.toMutableMap().also { it.remove(message.characterSheetId) }
|
||||
}
|
||||
}
|
||||
|
||||
is ApiSynchronisation.AlterationUpdate -> Unit
|
||||
is ApiSynchronisation.AlterationDelete -> Unit
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
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.character.CharacterSheetService
|
||||
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.server.Engine
|
||||
import com.pixelized.shared.lwa.utils.PathProvider
|
||||
|
|
@ -37,6 +39,7 @@ val storeDependencies
|
|||
singleOf(::CampaignStore)
|
||||
singleOf(::AlterationStore)
|
||||
singleOf(::TagStore)
|
||||
singleOf(::ItemStore)
|
||||
}
|
||||
|
||||
val serviceDependencies
|
||||
|
|
@ -44,4 +47,5 @@ val serviceDependencies
|
|||
singleOf(::CharacterSheetService)
|
||||
singleOf(::CampaignService)
|
||||
singleOf(::AlterationService)
|
||||
singleOf(::ItemService)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.stateIn
|
|||
|
||||
class AlterationService(
|
||||
private val alterationStore: AlterationStore,
|
||||
tagStore: TagStore,
|
||||
private val tagStore: TagStore,
|
||||
factory: AlterationJsonFactory,
|
||||
) {
|
||||
private val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
|
|
@ -26,26 +26,18 @@ class AlterationService(
|
|||
initialValue = emptyMap()
|
||||
)
|
||||
|
||||
private val alterationTags = tagStore.alterationTags()
|
||||
.map { it.values.toList() }
|
||||
.stateIn(
|
||||
scope = scope,
|
||||
started = SharingStarted.Eagerly,
|
||||
initialValue = emptyList()
|
||||
)
|
||||
|
||||
fun alterations(): List<AlterationJson> {
|
||||
return alterationHashFlow.value.values.toList()
|
||||
}
|
||||
|
||||
fun tags(): List<TagJson> {
|
||||
return alterationTags.value
|
||||
}
|
||||
|
||||
fun alteration(alterationId: String): AlterationJson? {
|
||||
return alterationHashFlow.value[alterationId]
|
||||
}
|
||||
|
||||
fun tags(): List<TagJson> {
|
||||
return tagStore.alterationTags().value.values.toList()
|
||||
}
|
||||
|
||||
@Throws
|
||||
fun save(
|
||||
json: AlterationJson,
|
||||
|
|
@ -59,8 +51,6 @@ class AlterationService(
|
|||
|
||||
@Throws
|
||||
fun delete(alterationId: String) {
|
||||
return alterationStore.delete(
|
||||
id = alterationId,
|
||||
)
|
||||
alterationStore.delete(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 ALTERATION = "alteration"
|
||||
private const val ITEM = "item"
|
||||
|
||||
class TagStore(
|
||||
private val pathProvider: PathProvider,
|
||||
|
|
@ -23,6 +24,7 @@ class TagStore(
|
|||
) {
|
||||
private val alterationTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
||||
private val characterTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
||||
private val itemTagsFlow = MutableStateFlow<Map<String, TagJson>>(emptyMap())
|
||||
|
||||
init {
|
||||
// make the file path.
|
||||
|
|
@ -39,11 +41,16 @@ class TagStore(
|
|||
flow = characterTagsFlow,
|
||||
file = characterFile(),
|
||||
)
|
||||
update(
|
||||
flow = itemTagsFlow,
|
||||
file = itemFile(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun alterationTags(): StateFlow<Map<String, TagJson>> = alterationTagsFlow
|
||||
fun characterTags(): StateFlow<Map<String, TagJson>> = characterTagsFlow
|
||||
fun itemTags(): StateFlow<Map<String, TagJson>> = itemTagsFlow
|
||||
|
||||
private fun update(
|
||||
flow: MutableStateFlow<Map<String, TagJson>>,
|
||||
|
|
@ -101,6 +108,6 @@ class TagStore(
|
|||
}
|
||||
|
||||
private fun characterFile() = File("${pathProvider.tagsPath()}$CHARACTER.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.campaign.CampaignService
|
||||
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.protocol.websocket.ApiSynchronisation
|
||||
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
|
||||
|
|
@ -16,6 +17,7 @@ class Engine(
|
|||
val characterService: CharacterSheetService,
|
||||
val campaignService: CampaignService,
|
||||
val alterationService: AlterationService,
|
||||
val itemService: ItemService,
|
||||
val campaignJsonFactory: CampaignJsonFactory,
|
||||
) {
|
||||
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.putCharacterDiminished
|
||||
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.protocol.websocket.SocketMessage
|
||||
import com.pixelized.shared.lwa.sharedModuleDependencies
|
||||
|
|
@ -113,12 +118,13 @@ class LocalServer {
|
|||
}
|
||||
}.onFailure { exception ->
|
||||
// TODO
|
||||
println("WebSocket exception: ${exception.localizedMessage}")
|
||||
println("WebSocket exception: ${exception.message}")
|
||||
}.also {
|
||||
job.cancel()
|
||||
}
|
||||
}
|
||||
)
|
||||
// TODO Tags.
|
||||
route(
|
||||
path = "/alteration",
|
||||
) {
|
||||
|
|
@ -217,6 +223,28 @@ class LocalServer {
|
|||
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
|
||||
val alterationId = call.queryParameters.alterationId
|
||||
// get the alteration of the given id.
|
||||
val alteration = alterationService
|
||||
.alteration(alterationId = alterationId)
|
||||
val alteration = alterationService.alteration(alterationId = alterationId)
|
||||
?: error("Alteration with id:$alterationId not found.")
|
||||
// send it back to the user.
|
||||
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)
|
||||
}
|
||||
|
||||
val Parameters.itemId
|
||||
get() = "itemId".let { param ->
|
||||
this[param] ?: throw MissingParameterException(name = param)
|
||||
}
|
||||
|
||||
val Parameters.create
|
||||
get() = "create".let { 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.characterSheet.factory.CharacterSheetJsonFactory
|
||||
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.parser.dice.DiceParser
|
||||
import com.pixelized.shared.lwa.parser.expression.ExpressionParser
|
||||
|
|
@ -48,6 +50,8 @@ val factoryDependencies
|
|||
factoryOf(::AlteredCharacterSheetFactory)
|
||||
factoryOf(::AlterationJsonFactory)
|
||||
factoryOf(::TagJsonFactory)
|
||||
factoryOf(::ItemJsonFactory)
|
||||
factoryOf(::ItemJsonFactoryV1)
|
||||
}
|
||||
|
||||
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 {
|
||||
AlterationId,
|
||||
AlterationName,
|
||||
ItemId,
|
||||
ItemName,
|
||||
CharacterSheetId,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,27 +5,48 @@ import kotlinx.serialization.Serializable
|
|||
@Serializable
|
||||
sealed interface ApiSynchronisation : SocketMessage {
|
||||
|
||||
@Serializable
|
||||
sealed interface CharacterSheetApiSynchronisation : ApiSynchronisation, CharacterSheetIdMessage
|
||||
|
||||
@Serializable
|
||||
data class CharacterSheetDelete(
|
||||
override val timestamp: Long,
|
||||
override val characterSheetId: String,
|
||||
) : ApiSynchronisation, CharacterSheetIdMessage
|
||||
) : CharacterSheetApiSynchronisation
|
||||
|
||||
@Serializable
|
||||
data class CharacterSheetUpdate(
|
||||
override val timestamp: Long,
|
||||
override val characterSheetId: String,
|
||||
) : ApiSynchronisation, CharacterSheetIdMessage
|
||||
) : CharacterSheetApiSynchronisation
|
||||
|
||||
@Serializable
|
||||
sealed interface AlterationApiSynchronisation : ApiSynchronisation
|
||||
|
||||
@Serializable
|
||||
data class AlterationUpdate(
|
||||
override val timestamp: Long,
|
||||
val alterationId: String,
|
||||
) : ApiSynchronisation
|
||||
) : AlterationApiSynchronisation
|
||||
|
||||
@Serializable
|
||||
data class AlterationDelete(
|
||||
override val timestamp: Long,
|
||||
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(
|
||||
os: OperatingSystem = this.operatingSystem,
|
||||
app: String = this.appName,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue