Server : Campaign error management

This commit is contained in:
Thomas Andres Gomez 2025-03-30 18:25:27 +02:00
parent acb445c480
commit 0e7bc87cde
12 changed files with 222 additions and 309 deletions

View file

@ -37,223 +37,95 @@ class CampaignService(
return campaignJsonFlow.value return campaignJsonFlow.value
} }
@Throws
fun addCharacter( fun addCharacter(
characterSheetId: String, characterSheetId: String,
): Boolean { ) {
// check if the character is already in the campaign. // Check if the character is already in the campaign.
if (campaign.characters.contains(characterSheetId)) return false if (campaign.instances.contains(characterSheetId)) {
// update the corresponding instance val root = Exception("Character with id:$characterSheetId is already in the campaign.")
val characters = campaign.characters.toMutableSet().also { it.add(characterSheetId) } throw CampaignStore.BusinessException(root = root)
// save the campaign to the disk (update the flow).
return try {
store.save(
campaign = campaign.copy(characters = characters)
)
true
} catch (exception: Exception) {
false
} }
} // Update the corresponding instance
val characters = campaign.characters.toMutableSet().also {
suspend fun addNpc( it.add(characterSheetId)
characterSheetId: String,
): Boolean {
// check if the character is already in the campaign.
if (campaign.npcs.contains(characterSheetId)) return false
// update the corresponding instance
val characters = campaign.npcs.toMutableSet().also { it.add(characterSheetId) }
// save the campaign to the disk (update the flow).
return try {
store.save(
campaign = campaign.copy(npcs = characters)
)
true
} catch (exception: Exception) {
false
} }
// Save the campaign to the disk (update the flow).
store.save(
campaign = campaign.copy(characters = characters)
)
} }
suspend fun removeCharacter( @Throws
fun addNpc(
characterSheetId: String, characterSheetId: String,
): Boolean { ) {
// check if the character is in the campaign. // Check if the character is already in the campaign.
if (campaign.characters.contains(characterSheetId).not()) return false if (campaign.instances.contains(characterSheetId)) {
// update the corresponding instance val root = Exception("Character with id:$characterSheetId is already in the campaign.")
val characters = campaign.characters.toMutableSet().also { it.remove(characterSheetId) } throw CampaignStore.BusinessException(root = root)
// save the campaign to the disk + update the flow.
return try {
store.save(
campaign = campaign.copy(characters = characters)
)
true
} catch (exception: Exception) {
false
} }
} // Update the corresponding instance
val characters = campaign.npcs.toMutableSet().also {
suspend fun removeNpc( it.add(characterSheetId)
characterSheetId: String,
): Boolean {
// check if the character is in the campaign.
if (campaign.npcs.contains(characterSheetId).not()) return false
// update the corresponding instance
val characters = campaign.npcs.toMutableSet().also { it.remove(characterSheetId) }
// save the campaign to the disk + update the flow.
return try {
store.save(
campaign = campaign.copy(npcs = characters)
)
true
} catch (exception: Exception) {
false
} }
// Save the campaign to the disk (update the flow).
store.save(
campaign = campaign.copy(npcs = characters)
)
} }
suspend fun removeInstance( @Throws
fun removeCharacter(
characterSheetId: String, characterSheetId: String,
): Boolean { ) {
return removeCharacter(characterSheetId) || removeNpc(characterSheetId) // Check if the character is in the campaign.
if (campaign.characters.contains(characterSheetId).not()) {
val root = Exception("Character with id:$characterSheetId is not in the party.")
throw CampaignStore.BusinessException(root = root)
}
// Update the corresponding instance
val characters = campaign.characters.toMutableSet().also {
it.remove(characterSheetId)
}
// Save the campaign to the disk + update the flow.
store.save(
campaign = campaign.copy(characters = characters)
)
} }
suspend fun setScene( @Throws
fun removeNpc(
characterSheetId: String,
) {
// Check if the character is in the campaign.
if (campaign.npcs.contains(characterSheetId).not()) {
val root = Exception("Character with id:$characterSheetId is not in the npcs.")
throw CampaignStore.BusinessException(root = root)
}
// Update the corresponding instance
val characters = campaign.npcs.toMutableSet().also {
it.remove(characterSheetId)
}
// Save the campaign to the disk + update the flow.
store.save(
campaign = campaign.copy(npcs = characters)
)
}
@Throws
fun setScene(
scene: Campaign.Scene, scene: Campaign.Scene,
): Boolean { ) {
// save the campaign to the disk + update the flow. // save the campaign to the disk + update the flow.
return try { store.save(
store.save( campaign = campaign.copy(scene = scene)
campaign.copy(scene = scene) )
)
true
} catch (exception: Exception) {
false
}
} }
// Data manipulation through WebSocket. // Data manipulation through WebSocket.
// suspend fun updateCharacteristic( fun updateToggleParty() {
// characterInstanceId: Campaign.CharacterInstance.Id,
// characteristic: Campaign.CharacterInstance.Characteristic,
// value: Int,
// ) {
// when (characterInstanceId.prefix) {
// Campaign.CharacterInstance.Id.PLAYER -> {
// // fetch all the current campaign character
// val characters = campaign.characters.toMutableMap()
// // update the corresponding character using the use case.
// characters[characterInstanceId] = useCase.updateCharacteristic(
// instance = campaign.character(id = characterInstanceId),
// characteristic = characteristic,
// value = value,
// )
// // save the campaign to the disk + update the flow.
// store.save(
// campaign = campaign.copy(characters = characters)
// )
// }
//
// Campaign.CharacterInstance.Id.NPC -> {
// // fetch all the current campaign character
// val npcs = campaign.npcs.toMutableMap()
// // update the corresponding character using the use case.
// npcs[characterInstanceId] = useCase.updateCharacteristic(
// instance = campaign.npc(id = characterInstanceId),
// characteristic = characteristic,
// value = value,
// )
// // save the campaign to the disk + update the flow.
// store.save(
// campaign = campaign.copy(npcs = npcs)
// )
// }
// }
// }
//
// suspend fun updateDiminished(
// characterInstanceId: Campaign.CharacterInstance.Id,
// diminished: Int,
// ) {
// when (characterInstanceId.prefix) {
// Campaign.CharacterInstance.Id.PLAYER -> {
// // fetch all the current campaign character
// val characters = campaign.characters.toMutableMap()
// // update the corresponding character using the use case.
// characters[characterInstanceId] = useCase.updateDiminished(
// instance = campaign.character(id = characterInstanceId),
// diminished = diminished,
// )
// // save the campaign to the disk + update the flow.
// store.save(
// campaign = campaign.copy(characters = characters)
// )
// }
//
// Campaign.CharacterInstance.Id.NPC -> {
// // fetch all the current campaign character
// val npcs = campaign.npcs.toMutableMap()
// // update the corresponding character using the use case.
// npcs[characterInstanceId] = useCase.updateDiminished(
// instance = campaign.npc(id = characterInstanceId),
// diminished = diminished,
// )
// // save the campaign to the disk + update the flow.
// store.save(
// campaign = campaign.copy(npcs = npcs)
// )
// }
// }
// }
//
// suspend fun toggleAlteration(
// characterInstanceId: Campaign.CharacterInstance.Id,
// alterationId: String,
// ) {
// when (characterInstanceId.prefix) {
// Campaign.CharacterInstance.Id.PLAYER -> {
// // fetch all the current campaign character
// val characters = campaign.characters.toMutableMap()
// // update the corresponding character alterations
// characters[characterInstanceId]?.let { character ->
// characters[characterInstanceId] = character.copy(
// alterations = character.alterations.toMutableList().also { alterations ->
// if (alterations.contains(alterationId)) {
// alterations.remove(alterationId)
// } else {
// alterations.add(alterationId)
// }
// },
// )
// }
// // save the campaign to the disk + update the flow.
// store.save(
// campaign = campaign.copy(characters = characters)
// )
// }
//
// Campaign.CharacterInstance.Id.NPC -> {
// // fetch all the current campaign character
// val characters = campaign.npcs.toMutableMap()
// // update the corresponding character alterations
// characters[characterInstanceId]?.let { character ->
// characters[characterInstanceId] = character.copy(
// alterations = character.alterations.toMutableList().also { alterations ->
// if (alterations.contains(alterationId)) {
// alterations.remove(alterationId)
// } else {
// alterations.add(alterationId)
// }
// },
// )
// }
// // save the campaign to the disk + update the flow.
// store.save(
// campaign = campaign.copy(npcs = characters)
// )
// }
// }
// }
suspend fun updateToggleParty() {
store.save( store.save(
campaign = campaign.copy( campaign = campaign.copy(
options = campaign.options.copy( options = campaign.options.copy(
@ -263,7 +135,7 @@ class CampaignService(
) )
} }
suspend fun updateToggleNpc() { fun updateToggleNpc() {
store.save( store.save(
campaign = campaign.copy( campaign = campaign.copy(
options = campaign.options.copy( options = campaign.options.copy(

View file

@ -1,5 +1,6 @@
package com.pixelized.server.lwa.model.campaign package com.pixelized.server.lwa.model.campaign
import com.pixelized.server.lwa.model.alteration.AlterationStore.AlterationStoreException
import com.pixelized.shared.lwa.model.campaign.Campaign import com.pixelized.shared.lwa.model.campaign.Campaign
import com.pixelized.shared.lwa.model.campaign.CampaignJson import com.pixelized.shared.lwa.model.campaign.CampaignJson
import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory import com.pixelized.shared.lwa.model.campaign.factory.CampaignJsonFactory
@ -9,6 +10,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.File import java.io.File
@ -27,15 +29,15 @@ class CampaignStore(
val scope = CoroutineScope(Dispatchers.IO + Job()) val scope = CoroutineScope(Dispatchers.IO + Job())
// load the initial data // load the initial data
scope.launch { scope.launch {
updateCampaignFromDisk() updateCampaignFlow()
} }
} }
fun campaignFlow(): StateFlow<Campaign> = campaignFlow fun campaignFlow(): StateFlow<Campaign> = campaignFlow
private fun updateCampaignFromDisk() { private fun updateCampaignFlow() {
campaignFlow.value = try { campaignFlow.value = try {
loadCampaign() load()
} catch (exception: Exception) { } catch (exception: Exception) {
println(exception) // TODO proper exception handling println(exception) // TODO proper exception handling
Campaign.empty() Campaign.empty()
@ -44,60 +46,74 @@ class CampaignStore(
@Throws( @Throws(
FileReadException::class, FileReadException::class,
JsonCodingException::class,
JsonConversionException::class, JsonConversionException::class,
) )
fun loadCampaign(): Campaign { fun load(): Campaign {
val file = file() val file = campaignFile()
val json = try { // Read the campaign file.
val data = try {
file.readText(charset = Charsets.UTF_8) file.readText(charset = Charsets.UTF_8)
} catch (exception: Exception) { } catch (exception: Exception) {
throw FileReadException(root = exception) throw FileReadException(root = exception)
} }
// Guard, if the file is empty we load a default campaign. // Guard, if the file is empty we load a default campaign.
if (json.isBlank()) return Campaign.empty() if (data.isBlank()) return Campaign.empty()
// Decode the json into a string.
val json = try {
this.json.decodeFromString<CampaignJson>(data)
} catch (exception: Exception) {
throw JsonCodingException(root = exception)
}
// Convert from the Json format
val campaign = try { val campaign = try {
val data = this.json.decodeFromString<CampaignJson>(json) factory.convertFromJson(json)
factory.convertFromJson(data)
} catch (exception: Exception) { } catch (exception: Exception) {
throw JsonConversionException(root = exception) throw JsonConversionException(root = exception)
} }
return campaign return campaign
} }
@Throws( @Throws(
JsonConversionException::class, JsonConversionException::class,
JsonCodingException::class,
FileWriteException::class, FileWriteException::class,
) )
fun save(campaign: Campaign) { fun save(campaign: Campaign) {
// convert the data to json format // Transform the json into the model.
val json = try { val json = try {
factory.convertToJson(campaign = campaign).let(json::encodeToString) factory.convertToJson(campaign = campaign)
} catch (exception: Exception) { } catch (exception: Exception) {
throw JsonConversionException(root = exception) throw JsonConversionException(root = exception)
} }
// write the file // Encode the json into a string.
val data = try {
this.json.encodeToString(json)
} catch (exception: Exception) {
throw JsonCodingException(root = exception)
}
// Write the file
try { try {
val file = file() val file = campaignFile()
file.writeText( file.writeText(
text = json, text = data,
charset = Charsets.UTF_8, charset = Charsets.UTF_8,
) )
} catch (exception: Exception) { } catch (exception: Exception) {
throw FileWriteException(root = exception) throw FileWriteException(root = exception)
} }
// Update the dataflow. // Update the dataflow.
campaignFlow.value = campaign campaignFlow.update { campaign }
}
private fun campaignFile(): File {
return File("${pathProvider.campaignPath()}campaign.json")
} }
sealed class CampaignStoreException(root: Exception) : Exception(root) sealed class CampaignStoreException(root: Exception) : Exception(root)
class JsonConversionException(root: Exception) : CampaignStoreException(root) class JsonConversionException(root: Exception) : CampaignStoreException(root)
class JsonCodingException(root: Exception) : CampaignStoreException(root)
class BusinessException(root: Exception) : CampaignStoreException(root)
class FileWriteException(root: Exception) : CampaignStoreException(root) class FileWriteException(root: Exception) : CampaignStoreException(root)
class FileReadException(root: Exception) : CampaignStoreException(root) class FileReadException(root: Exception) : CampaignStoreException(root)
private fun file(): File {
return File("${pathProvider.campaignPath()}campaign.json")
}
} }

View file

@ -11,11 +11,13 @@ import io.ktor.server.response.respond
fun Engine.deleteAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { fun Engine.deleteAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
return { return {
try { try {
// get the query parameter
val alterationId = call.parameters.alterationId val alterationId = call.parameters.alterationId
// delete the alteration.
alterationService.delete( alterationService.delete(
alterationId = alterationId alterationId = alterationId
) )
// API & WebSocket responses.
call.respond( call.respond(
message = ResultJson.Success(), message = ResultJson.Success(),
) )
@ -32,13 +34,6 @@ fun Engine.deleteAlteration(): suspend io.ktor.server.routing.RoutingContext.()
message = exception.message ?: "?", message = exception.message ?: "?",
) )
) )
} catch (exception: AlterationStore.BusinessException) {
call.respond(
message = ResultJson.Error(
status = ResultJson.Error.FILE_DOES_NOT_EXIST,
message = "Alteration doesn't exist."
)
)
} catch (exception: Exception) { } catch (exception: Exception) {
call.respond( call.respond(
message = ResultJson.Error( message = ResultJson.Error(

View file

@ -36,13 +36,6 @@ fun Engine.putAlteration(): suspend io.ktor.server.routing.RoutingContext.() ->
message = exception.message ?: "?", message = exception.message ?: "?",
) )
) )
} catch (exception: AlterationStore.BusinessException) {
call.respond(
message = ResultJson.Error(
status = ResultJson.Error.FILE_ALREADY_EXIST,
message = "Alteration file already exist."
)
)
} catch (exception: Exception) { } catch (exception: Exception) {
call.respond( call.respond(
message = ResultJson.Error( message = ResultJson.Error(

View file

@ -1,10 +1,11 @@
package com.pixelized.server.lwa.server.rest.campaign package com.pixelized.server.lwa.server.rest.campaign
import com.pixelized.server.lwa.server.Engine import com.pixelized.server.lwa.server.Engine
import com.pixelized.server.lwa.utils.extentions.MissingParameterException
import com.pixelized.server.lwa.utils.extentions.characterSheetId import com.pixelized.server.lwa.utils.extentions.characterSheetId
import com.pixelized.shared.lwa.protocol.rest.ResultJson
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
import io.ktor.http.HttpStatusCode import io.ktor.server.response.respond
import io.ktor.server.response.respondText
fun Engine.removeCampaignCharacter(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { fun Engine.removeCampaignCharacter(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
return { return {
@ -12,17 +13,12 @@ fun Engine.removeCampaignCharacter(): suspend io.ktor.server.routing.RoutingCont
// get the query parameter // get the query parameter
val characterSheetId = call.queryParameters.characterSheetId val characterSheetId = call.queryParameters.characterSheetId
// remove the character form the party // remove the character form the party
val updated = campaignService.removeCharacter( campaignService.removeCharacter(
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
) )
// error case
if (updated.not()) {
error("Unexpected error when removing character (characterSheetId:$characterSheetId) from party.")
}
// API & WebSocket responses // API & WebSocket responses
call.respondText( call.respond(
text = "${HttpStatusCode.OK}", message = ResultJson.Success(),
status = HttpStatusCode.OK,
) )
webSocket.emit( webSocket.emit(
value = CampaignEvent.CharacterRemoved( value = CampaignEvent.CharacterRemoved(
@ -30,10 +26,19 @@ fun Engine.removeCampaignCharacter(): suspend io.ktor.server.routing.RoutingCont
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
) )
) )
} catch (exception: MissingParameterException) {
call.respond(
message = ResultJson.Error(
status = exception.errorCode,
message = exception.message ?: "?",
)
)
} catch (exception: Exception) { } catch (exception: Exception) {
call.respondText( call.respond(
text = exception.localizedMessage, message = ResultJson.Error(
status = HttpStatusCode.UnprocessableEntity, status = ResultJson.Error.GENERIC,
message = exception.message ?: "?",
)
) )
} }
} }

View file

@ -1,9 +1,12 @@
package com.pixelized.server.lwa.server.rest.campaign package com.pixelized.server.lwa.server.rest.campaign
import com.pixelized.server.lwa.server.Engine import com.pixelized.server.lwa.server.Engine
import com.pixelized.server.lwa.utils.extentions.MissingParameterException
import com.pixelized.server.lwa.utils.extentions.characterSheetId import com.pixelized.server.lwa.utils.extentions.characterSheetId
import com.pixelized.shared.lwa.protocol.rest.ResultJson
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.server.response.respond
import io.ktor.server.response.respondText import io.ktor.server.response.respondText
fun Engine.removeCampaignNpc(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { fun Engine.removeCampaignNpc(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
@ -12,15 +15,12 @@ fun Engine.removeCampaignNpc(): suspend io.ktor.server.routing.RoutingContext.()
// get the query parameter // get the query parameter
val characterSheetId = call.queryParameters.characterSheetId val characterSheetId = call.queryParameters.characterSheetId
// remove the character form the party // remove the character form the party
val updated = campaignService.removeNpc(characterSheetId = characterSheetId) campaignService.removeNpc(
// error case characterSheetId = characterSheetId,
if (updated.not()) { )
error("Unexpected error when removing character (characterSheetId:$characterSheetId) from npcs.")
}
// API & WebSocket responses // API & WebSocket responses
call.respondText( call.respond(
text = "${HttpStatusCode.OK}", message = ResultJson.Success(),
status = HttpStatusCode.OK,
) )
webSocket.emit( webSocket.emit(
value = CampaignEvent.NpcRemoved( value = CampaignEvent.NpcRemoved(
@ -28,10 +28,19 @@ fun Engine.removeCampaignNpc(): suspend io.ktor.server.routing.RoutingContext.()
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
) )
) )
} catch (exception: MissingParameterException) {
call.respond(
message = ResultJson.Error(
status = exception.errorCode,
message = exception.message ?: "?",
)
)
} catch (exception: Exception) { } catch (exception: Exception) {
call.respondText( call.respond(
text = exception.localizedMessage, message = ResultJson.Error(
status = HttpStatusCode.UnprocessableEntity, status = ResultJson.Error.GENERIC,
message = exception.message ?: "?",
)
) )
} }
} }

View file

@ -1,12 +1,22 @@
package com.pixelized.server.lwa.server.rest.campaign package com.pixelized.server.lwa.server.rest.campaign
import com.pixelized.server.lwa.server.Engine import com.pixelized.server.lwa.server.Engine
import com.pixelized.shared.lwa.protocol.rest.ResultJson
import io.ktor.server.response.respond import io.ktor.server.response.respond
fun Engine.getCampaign(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { fun Engine.getCampaign(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
return { return {
call.respond( try {
message = campaignService.campaignJson(), call.respond(
) message = campaignService.campaignJson(),
)
} catch (exception: Exception) {
call.respond(
message = ResultJson.Error(
status = ResultJson.Error.GENERIC,
message = exception.message ?: "?",
)
)
}
} }
} }

View file

@ -1,10 +1,11 @@
package com.pixelized.server.lwa.server.rest.campaign package com.pixelized.server.lwa.server.rest.campaign
import com.pixelized.server.lwa.server.Engine import com.pixelized.server.lwa.server.Engine
import com.pixelized.server.lwa.utils.extentions.MissingParameterException
import com.pixelized.server.lwa.utils.extentions.characterSheetId import com.pixelized.server.lwa.utils.extentions.characterSheetId
import com.pixelized.shared.lwa.protocol.rest.ResultJson
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
import io.ktor.http.HttpStatusCode import io.ktor.server.response.respond
import io.ktor.server.response.respondText
fun Engine.putCampaignCharacter(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { fun Engine.putCampaignCharacter(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
return { return {
@ -12,15 +13,12 @@ fun Engine.putCampaignCharacter(): suspend io.ktor.server.routing.RoutingContext
// get the query parameter // get the query parameter
val characterSheetId = call.queryParameters.characterSheetId val characterSheetId = call.queryParameters.characterSheetId
// add the character to the party. // add the character to the party.
val update = campaignService.addCharacter(characterSheetId = characterSheetId) campaignService.addCharacter(
// error case characterSheetId = characterSheetId,
if (update.not()) { )
error("Unexpected error occurred when the character instance was added to the party")
}
// API & WebSocket responses. // API & WebSocket responses.
call.respondText( call.respond(
text = "Character $characterSheetId successfully added to the party", message = ResultJson.Success(),
status = HttpStatusCode.OK,
) )
webSocket.emit( webSocket.emit(
value = CampaignEvent.CharacterAdded( value = CampaignEvent.CharacterAdded(
@ -28,10 +26,19 @@ fun Engine.putCampaignCharacter(): suspend io.ktor.server.routing.RoutingContext
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
) )
) )
} catch (exception: MissingParameterException) {
call.respond(
message = ResultJson.Error(
status = exception.errorCode,
message = exception.message ?: "?",
)
)
} catch (exception: Exception) { } catch (exception: Exception) {
call.respondText( call.respond(
text = "${exception.message}", message = ResultJson.Error(
status = HttpStatusCode.UnprocessableEntity, status = ResultJson.Error.GENERIC,
message = exception.message ?: "?",
)
) )
} }
} }

View file

@ -1,10 +1,11 @@
package com.pixelized.server.lwa.server.rest.campaign package com.pixelized.server.lwa.server.rest.campaign
import com.pixelized.server.lwa.server.Engine import com.pixelized.server.lwa.server.Engine
import com.pixelized.server.lwa.utils.extentions.MissingParameterException
import com.pixelized.server.lwa.utils.extentions.characterSheetId import com.pixelized.server.lwa.utils.extentions.characterSheetId
import com.pixelized.shared.lwa.protocol.rest.ResultJson
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
import io.ktor.http.HttpStatusCode import io.ktor.server.response.respond
import io.ktor.server.response.respondText
fun Engine.putCampaignNpc(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { fun Engine.putCampaignNpc(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
return { return {
@ -12,15 +13,12 @@ fun Engine.putCampaignNpc(): suspend io.ktor.server.routing.RoutingContext.() ->
// get the query parameter // get the query parameter
val characterSheetId = call.queryParameters.characterSheetId val characterSheetId = call.queryParameters.characterSheetId
// add the character to the npcs. // add the character to the npcs.
val update = campaignService.addNpc(characterSheetId = characterSheetId) campaignService.addNpc(
// error case characterSheetId = characterSheetId,
if (update.not()) { )
error("Unexpected error occurred when the character instance was added to the npcs")
}
// API & WebSocket responses. // API & WebSocket responses.
call.respondText( call.respond(
text = "Character $characterSheetId successfully added to the npcs", message = ResultJson.Success(),
status = HttpStatusCode.OK,
) )
webSocket.emit( webSocket.emit(
value = CampaignEvent.NpcAdded( value = CampaignEvent.NpcAdded(
@ -28,10 +26,19 @@ fun Engine.putCampaignNpc(): suspend io.ktor.server.routing.RoutingContext.() ->
characterSheetId = characterSheetId, characterSheetId = characterSheetId,
) )
) )
} catch (exception: MissingParameterException) {
call.respond(
message = ResultJson.Error(
status = exception.errorCode,
message = exception.message ?: "?",
)
)
} catch (exception: Exception) { } catch (exception: Exception) {
call.respondText( call.respond(
text = "${exception.message}", message = ResultJson.Error(
status = HttpStatusCode.UnprocessableEntity, status = ResultJson.Error.GENERIC,
message = exception.message ?: "?",
)
) )
} }
} }

View file

@ -1,10 +1,13 @@
package com.pixelized.server.lwa.server.rest.campaign package com.pixelized.server.lwa.server.rest.campaign
import com.pixelized.server.lwa.server.Engine import com.pixelized.server.lwa.server.Engine
import com.pixelized.server.lwa.utils.extentions.MissingParameterException
import com.pixelized.shared.lwa.model.campaign.CampaignJsonV2 import com.pixelized.shared.lwa.model.campaign.CampaignJsonV2
import com.pixelized.shared.lwa.protocol.rest.ResultJson
import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent import com.pixelized.shared.lwa.protocol.websocket.CampaignEvent
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.server.request.receive import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.response.respondText import io.ktor.server.response.respondText
fun Engine.putCampaignScene(): suspend io.ktor.server.routing.RoutingContext.() -> Unit { fun Engine.putCampaignScene(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
@ -15,15 +18,10 @@ fun Engine.putCampaignScene(): suspend io.ktor.server.routing.RoutingContext.()
// convert the scene into the a usable data model. // convert the scene into the a usable data model.
val scene = campaignJsonFactory.convertFromJson(json = form) val scene = campaignJsonFactory.convertFromJson(json = form)
// update the campaign. // update the campaign.
val updated = campaignService.setScene(scene = scene) campaignService.setScene(scene = scene)
// error case
if (updated.not()) {
error("Unexpected error when updating the scene.")
}
// API & WebSocket responses // API & WebSocket responses
call.respondText( call.respond(
text = "${HttpStatusCode.OK}", message = ResultJson.Success(),
status = HttpStatusCode.OK,
) )
webSocket.emit( webSocket.emit(
value = CampaignEvent.UpdateScene( value = CampaignEvent.UpdateScene(
@ -31,10 +29,19 @@ fun Engine.putCampaignScene(): suspend io.ktor.server.routing.RoutingContext.()
name = scene.name, name = scene.name,
) )
) )
} catch (exception: MissingParameterException) {
call.respond(
message = ResultJson.Error(
status = exception.errorCode,
message = exception.message ?: "?",
)
)
} catch (exception: Exception) { } catch (exception: Exception) {
call.respondText( call.respond(
text = exception.localizedMessage, message = ResultJson.Error(
status = HttpStatusCode.UnprocessableEntity, status = ResultJson.Error.GENERIC,
message = exception.message ?: "?",
)
) )
} }
} }

View file

@ -13,11 +13,6 @@ fun Engine.deleteCharacter(): suspend io.ktor.server.routing.RoutingContext.() -
val deleted = characterService.deleteCharacterSheet( val deleted = characterService.deleteCharacterSheet(
characterSheetId = characterSheetId characterSheetId = characterSheetId
) )
// Remove the character fom the campaign if needed.
// TODO probably useless because all data will not be cleaned up (all campaign / screnes)
campaignService.removeInstance(
characterSheetId = characterSheetId,
)
if (deleted) { if (deleted) {
call.respondText( call.respondText(

View file

@ -13,12 +13,9 @@ sealed interface ResultJson {
val message: String, val message: String,
) : ResultJson { ) : ResultJson {
companion object { companion object {
const val GENERIC = 500 const val GENERIC = 600
const val FILE_ALREADY_EXIST = GENERIC + 1 const val MISSING_PARAMETER = 700
const val FILE_DOES_NOT_EXIST = GENERIC + 2
const val MISSING_PARAMETER = 1000
const val MISSING_CHARACTER_SHEET_ID = MISSING_PARAMETER + 1 const val MISSING_CHARACTER_SHEET_ID = MISSING_PARAMETER + 1
const val MISSING_ALTERATION_ID = MISSING_PARAMETER + 2 const val MISSING_ALTERATION_ID = MISSING_PARAMETER + 2
const val MISSING_CREATE = MISSING_PARAMETER + 3 const val MISSING_CREATE = MISSING_PARAMETER + 3