Server : Alteration error management
This commit is contained in:
parent
81c6450dbe
commit
acb445c480
10 changed files with 220 additions and 80 deletions
|
|
@ -46,11 +46,21 @@ class AlterationService(
|
|||
return alterationHashFlow.value[alterationId]
|
||||
}
|
||||
|
||||
fun update(json: AlterationJson) {
|
||||
alterationStore.save(alteration = json)
|
||||
@Throws
|
||||
fun save(
|
||||
json: AlterationJson,
|
||||
create: Boolean,
|
||||
) {
|
||||
alterationStore.save(
|
||||
json = json,
|
||||
create = create,
|
||||
)
|
||||
}
|
||||
|
||||
fun delete(alterationId: String): Boolean {
|
||||
return alterationStore.delete(id = alterationId)
|
||||
@Throws
|
||||
fun delete(alterationId: String) {
|
||||
return alterationStore.delete(
|
||||
id = alterationId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,15 +28,15 @@ class AlterationStore(
|
|||
val scope = CoroutineScope(Dispatchers.IO + Job())
|
||||
// load the initial data
|
||||
scope.launch {
|
||||
updateAlterations()
|
||||
updateAlterationFlow()
|
||||
}
|
||||
}
|
||||
|
||||
fun alterationsFlow(): StateFlow<List<Alteration>> = alterationFlow
|
||||
|
||||
private fun updateAlterations() {
|
||||
private fun updateAlterationFlow() {
|
||||
alterationFlow.value = try {
|
||||
loadAlterations()
|
||||
load()
|
||||
} catch (exception: Exception) {
|
||||
println(exception) // TODO proper exception handling
|
||||
emptyList()
|
||||
|
|
@ -47,7 +47,9 @@ class AlterationStore(
|
|||
FileReadException::class,
|
||||
JsonConversionException::class,
|
||||
)
|
||||
private fun loadAlterations(): List<Alteration> {
|
||||
private fun load(
|
||||
directory: File = this.directory,
|
||||
): List<Alteration> {
|
||||
return directory
|
||||
.listFiles()
|
||||
?.mapNotNull { file ->
|
||||
|
|
@ -60,8 +62,14 @@ class AlterationStore(
|
|||
if (json.isBlank()) {
|
||||
return@mapNotNull null
|
||||
}
|
||||
// decode the file
|
||||
val data = try {
|
||||
this.json.decodeFromString<AlterationJson>(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// parse the json string.
|
||||
try {
|
||||
val data = this.json.decodeFromString<AlterationJson>(json)
|
||||
factory.convertFromJson(data)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
|
|
@ -70,67 +78,76 @@ class AlterationStore(
|
|||
?: emptyList()
|
||||
}
|
||||
|
||||
@Throws(JsonConversionException::class, FileWriteException::class)
|
||||
@Throws(
|
||||
BusinessException::class,
|
||||
JsonConversionException::class,
|
||||
JsonCodingException::class,
|
||||
FileWriteException::class,
|
||||
)
|
||||
fun save(
|
||||
alteration: Alteration,
|
||||
json: AlterationJson,
|
||||
create: Boolean,
|
||||
) {
|
||||
val json = try {
|
||||
factory.convertToJson(data = alteration)
|
||||
val file = alterationFile(id = json.id)
|
||||
// Guard case on update alteration
|
||||
if (create && file.exists()) {
|
||||
val root = Exception("Alteration already exist, creation is impossible.")
|
||||
throw BusinessException(root = root)
|
||||
}
|
||||
// Transform the json into the model.
|
||||
val alteration = try {
|
||||
factory.convertFromJson(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
}
|
||||
|
||||
save(alteration = json)
|
||||
}
|
||||
|
||||
@Throws(FileWriteException::class)
|
||||
fun save(
|
||||
alteration: AlterationJson,
|
||||
) {
|
||||
// encode the json into a string
|
||||
// Encode the json into a string.
|
||||
val data = try {
|
||||
json.encodeToString(alteration)
|
||||
this.json.encodeToString(json)
|
||||
} catch (exception: Exception) {
|
||||
throw JsonConversionException(root = exception)
|
||||
throw JsonCodingException(root = exception)
|
||||
}
|
||||
// write the alteration into a file.
|
||||
// Write the alteration into a file.
|
||||
try {
|
||||
val file = alterationFile(id = alteration.id)
|
||||
file.writeText(
|
||||
text = data,
|
||||
charset = Charsets.UTF_8,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
throw FileWriteException(
|
||||
root = exception
|
||||
)
|
||||
throw FileWriteException(root = exception)
|
||||
}
|
||||
// Update the dataflow.
|
||||
alterationFlow.update { alterations ->
|
||||
val index = alterations.indexOfFirst { it.id == alteration.id }
|
||||
val alt = factory.convertFromJson(alteration)
|
||||
|
||||
val index = alterations.indexOfFirst {
|
||||
it.id == json.id
|
||||
}
|
||||
alterations.toMutableList().also {
|
||||
if (index >= 0) {
|
||||
it[index] = alt
|
||||
} else {
|
||||
it.add(alt)
|
||||
when {
|
||||
index >= 0 -> it[index] = alteration
|
||||
else -> it.add(alteration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun delete(id: String): Boolean {
|
||||
@Throws(BusinessException::class)
|
||||
fun delete(id: String) {
|
||||
val file = alterationFile(id = id)
|
||||
val deleted = file.delete()
|
||||
if (deleted) {
|
||||
alterationFlow.update { alterations ->
|
||||
alterations.toMutableList().also { alteration ->
|
||||
alteration.removeIf { it.id == id }
|
||||
}
|
||||
// Guard case on the file existence.
|
||||
if (file.exists().not()) {
|
||||
val root = Exception("Alteration doesn't not exist, deletion is impossible.")
|
||||
throw BusinessException(root = root)
|
||||
}
|
||||
// Guard case on the file deletion
|
||||
if (file.delete().not()) {
|
||||
val root = Exception("Alteration file have not been deleted for unknown reason.")
|
||||
throw BusinessException(root = root)
|
||||
}
|
||||
// Update the data model with the deleted alteration.
|
||||
alterationFlow.update { alterations ->
|
||||
alterations.toMutableList().also { alteration ->
|
||||
alteration.removeIf { it.id == id }
|
||||
}
|
||||
}
|
||||
return deleted
|
||||
}
|
||||
|
||||
private fun alterationFile(id: String): File {
|
||||
|
|
@ -139,6 +156,8 @@ class AlterationStore(
|
|||
|
||||
sealed class AlterationStoreException(root: Exception) : Exception(root)
|
||||
class JsonConversionException(root: Exception) : AlterationStoreException(root)
|
||||
class JsonCodingException(root: Exception) : AlterationStoreException(root)
|
||||
class BusinessException(root: Exception) : AlterationStoreException(root)
|
||||
class FileWriteException(root: Exception) : AlterationStoreException(root)
|
||||
class FileReadException(root: Exception) : AlterationStoreException(root)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +1,23 @@
|
|||
package com.pixelized.server.lwa.server.rest.alteration
|
||||
|
||||
import com.pixelized.server.lwa.model.alteration.AlterationStore
|
||||
import com.pixelized.server.lwa.server.Engine
|
||||
import com.pixelized.server.lwa.utils.extentions.MissingParameterException
|
||||
import com.pixelized.server.lwa.utils.extentions.alterationId
|
||||
import com.pixelized.shared.lwa.protocol.rest.ResultJson
|
||||
import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.response.respondText
|
||||
import io.ktor.server.response.respond
|
||||
|
||||
fun Engine.deleteAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
||||
return {
|
||||
try {
|
||||
val alterationId = call.parameters.alterationId
|
||||
|
||||
val deleted = alterationService.delete(
|
||||
alterationService.delete(
|
||||
alterationId = alterationId
|
||||
)
|
||||
|
||||
if (deleted.not()) error("Unexpected error occurred")
|
||||
|
||||
call.respondText(
|
||||
text = "${HttpStatusCode.OK}",
|
||||
status = HttpStatusCode.OK,
|
||||
call.respond(
|
||||
message = ResultJson.Success(),
|
||||
)
|
||||
webSocket.emit(
|
||||
value = ApiSynchronisation.AlterationDelete(
|
||||
|
|
@ -27,10 +25,26 @@ fun Engine.deleteAlteration(): suspend io.ktor.server.routing.RoutingContext.()
|
|||
alterationId = alterationId,
|
||||
),
|
||||
)
|
||||
} catch (exception: MissingParameterException) {
|
||||
call.respond(
|
||||
message = ResultJson.Error(
|
||||
status = exception.errorCode,
|
||||
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) {
|
||||
call.respondText(
|
||||
text = "${HttpStatusCode.UnprocessableEntity}",
|
||||
status = HttpStatusCode.UnprocessableEntity,
|
||||
call.respond(
|
||||
message = ResultJson.Error(
|
||||
status = ResultJson.Error.GENERIC,
|
||||
message = exception.message ?: "?",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,8 @@ package com.pixelized.server.lwa.server.rest.alteration
|
|||
|
||||
import com.pixelized.server.lwa.server.Engine
|
||||
import com.pixelized.server.lwa.utils.extentions.alterationId
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import com.pixelized.shared.lwa.protocol.rest.ResultJson
|
||||
import io.ktor.server.response.respond
|
||||
import io.ktor.server.response.respondText
|
||||
|
||||
fun Engine.getAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
||||
return {
|
||||
|
|
@ -20,9 +19,11 @@ fun Engine.getAlteration(): suspend io.ktor.server.routing.RoutingContext.() ->
|
|||
message = alteration,
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
call.respondText(
|
||||
text = exception.localizedMessage,
|
||||
status = HttpStatusCode.UnprocessableEntity,
|
||||
call.respond(
|
||||
message = ResultJson.Error(
|
||||
status = ResultJson.Error.GENERIC,
|
||||
message = exception.message ?: "?",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,22 @@
|
|||
package com.pixelized.server.lwa.server.rest.alteration
|
||||
|
||||
import com.pixelized.server.lwa.server.Engine
|
||||
import com.pixelized.shared.lwa.protocol.rest.ResultJson
|
||||
import io.ktor.server.response.respond
|
||||
|
||||
fun Engine.getAlterationTags(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
||||
return {
|
||||
call.respond(
|
||||
message = alterationService.tags(),
|
||||
)
|
||||
try {
|
||||
call.respond(
|
||||
message = alterationService.tags(),
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
call.respond(
|
||||
message = ResultJson.Error(
|
||||
status = ResultJson.Error.GENERIC,
|
||||
message = exception.message ?: "?",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,22 @@
|
|||
package com.pixelized.server.lwa.server.rest.alteration
|
||||
|
||||
import com.pixelized.server.lwa.server.Engine
|
||||
import com.pixelized.shared.lwa.protocol.rest.ResultJson
|
||||
import io.ktor.server.response.respond
|
||||
|
||||
fun Engine.getAlterations(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
||||
return {
|
||||
call.respond(
|
||||
message = alterationService.alterations(),
|
||||
)
|
||||
try {
|
||||
call.respond(
|
||||
message = alterationService.alterations(),
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
call.respond(
|
||||
message = ResultJson.Error(
|
||||
status = ResultJson.Error.GENERIC,
|
||||
message = exception.message ?: "?",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,27 @@
|
|||
package com.pixelized.server.lwa.server.rest.alteration
|
||||
|
||||
import com.pixelized.server.lwa.model.alteration.AlterationStore
|
||||
import com.pixelized.server.lwa.server.Engine
|
||||
import com.pixelized.server.lwa.utils.extentions.MissingParameterException
|
||||
import com.pixelized.server.lwa.utils.extentions.create
|
||||
import com.pixelized.shared.lwa.model.alteration.AlterationJson
|
||||
import com.pixelized.shared.lwa.protocol.rest.ResultJson
|
||||
import com.pixelized.shared.lwa.protocol.websocket.ApiSynchronisation
|
||||
import io.ktor.http.HttpStatusCode
|
||||
import io.ktor.server.request.receive
|
||||
import io.ktor.server.response.respondText
|
||||
import io.ktor.server.response.respond
|
||||
|
||||
fun Engine.putAlteration(): suspend io.ktor.server.routing.RoutingContext.() -> Unit {
|
||||
return {
|
||||
try {
|
||||
val form = call.receive<AlterationJson>()
|
||||
val create = call.queryParameters.create
|
||||
|
||||
alterationService.update(json = form)
|
||||
|
||||
call.respondText(
|
||||
text = "${HttpStatusCode.OK}",
|
||||
status = HttpStatusCode.OK,
|
||||
alterationService.save(
|
||||
json = form,
|
||||
create = create,
|
||||
)
|
||||
call.respond(
|
||||
message = ResultJson.Success(),
|
||||
)
|
||||
webSocket.emit(
|
||||
value = ApiSynchronisation.AlterationUpdate(
|
||||
|
|
@ -24,10 +29,26 @@ fun Engine.putAlteration(): suspend io.ktor.server.routing.RoutingContext.() ->
|
|||
alterationId = form.id,
|
||||
),
|
||||
)
|
||||
} catch (exception : Exception) {
|
||||
call.respondText(
|
||||
text = "${HttpStatusCode.UnprocessableEntity}",
|
||||
status = HttpStatusCode.UnprocessableEntity,
|
||||
} catch (exception: MissingParameterException) {
|
||||
call.respond(
|
||||
message = ResultJson.Error(
|
||||
status = exception.errorCode,
|
||||
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) {
|
||||
call.respond(
|
||||
message = ResultJson.Error(
|
||||
status = ResultJson.Error.GENERIC,
|
||||
message = exception.message ?: "?",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,30 @@
|
|||
package com.pixelized.server.lwa.utils.extentions
|
||||
|
||||
import com.pixelized.shared.lwa.protocol.rest.ResultJson
|
||||
import io.ktor.http.Parameters
|
||||
|
||||
val Parameters.characterSheetId
|
||||
get() = this["characterSheetId"] ?: error("Missing characterSheetId parameter.")
|
||||
get() = this["characterSheetId"]
|
||||
?: throw MissingParameterException(
|
||||
name = "characterSheetId",
|
||||
errorCode = ResultJson.Error.MISSING_CHARACTER_SHEET_ID,
|
||||
)
|
||||
|
||||
val Parameters.alterationId
|
||||
get() = this["alterationId"] ?: error("Missing alterationId parameter.")
|
||||
get() = this["alterationId"]
|
||||
?: throw MissingParameterException(
|
||||
name = "alterationId",
|
||||
errorCode = ResultJson.Error.MISSING_ALTERATION_ID,
|
||||
)
|
||||
|
||||
val Parameters.create
|
||||
get() = this["create"]?.toBooleanStrictOrNull()
|
||||
?: throw MissingParameterException(
|
||||
name = "create",
|
||||
errorCode = ResultJson.Error.MISSING_CREATE
|
||||
)
|
||||
|
||||
class MissingParameterException(
|
||||
name: String,
|
||||
val errorCode: Int,
|
||||
) : Exception("Missing $name parameter.")
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ val toolsDependencies
|
|||
get() = module {
|
||||
factory {
|
||||
Json {
|
||||
encodeDefaults = true
|
||||
explicitNulls = false
|
||||
prettyPrint = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package com.pixelized.shared.lwa.protocol.rest
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed interface ResultJson {
|
||||
val success: Boolean
|
||||
|
||||
@Serializable
|
||||
data class Error(
|
||||
override val success: Boolean = false,
|
||||
val status: Int,
|
||||
val message: String,
|
||||
) : ResultJson {
|
||||
companion object {
|
||||
const val GENERIC = 500
|
||||
|
||||
const val FILE_ALREADY_EXIST = GENERIC + 1
|
||||
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_ALTERATION_ID = MISSING_PARAMETER + 2
|
||||
const val MISSING_CREATE = MISSING_PARAMETER + 3
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Success(
|
||||
override val success: Boolean = true,
|
||||
val status: Int = 100,
|
||||
) : ResultJson
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue