Merge pull request 'LMS-18 Library API' (#1) from FEAT-Library-API into main
Reviewed-on: https://gitea.com/NickKalar/LMS-APIs/pulls/1
This commit is contained in:
@@ -7,6 +7,7 @@ data class Library(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
val address: String,
|
||||
val isArchived: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@@ -14,4 +15,5 @@ data class NewLibrary(
|
||||
// ID to be inserted by Database
|
||||
val name: String,
|
||||
val address: String,
|
||||
val isArchived: Boolean = false,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package codes.kalar.routes
|
||||
|
||||
import codes.kalar.exception.DbElementInsertionException
|
||||
import codes.kalar.exception.DbElementNotFoundException
|
||||
import codes.kalar.model.Library
|
||||
import codes.kalar.model.NewLibrary
|
||||
import codes.kalar.service.LibraryService
|
||||
import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.request.*
|
||||
import io.ktor.server.response.*
|
||||
@@ -8,28 +13,83 @@ import io.ktor.server.routing.*
|
||||
import java.sql.Connection
|
||||
|
||||
fun Application.configureLibraryRoutes(dbConnection: Connection) {
|
||||
val libraryService = LibraryService(dbConnection)
|
||||
|
||||
routing {
|
||||
get("/libraries") {
|
||||
call.respondText("Libraries are neat!")
|
||||
try {
|
||||
val id = call.parameters["id"]?.toLong()
|
||||
if (id != null) {
|
||||
val library = libraryService.readLibraryById(id)
|
||||
call.respond(HttpStatusCode.OK, library)
|
||||
} else {
|
||||
val libraries = libraryService.readAllLibraries()
|
||||
call.respond(HttpStatusCode.OK, libraries)
|
||||
}
|
||||
} catch (cause: DbElementNotFoundException) {
|
||||
call.respond(HttpStatusCode.BadRequest, cause.message ?: "Unable to find Library.")
|
||||
} catch (cause: IllegalArgumentException) {
|
||||
call.respond(HttpStatusCode.BadRequest, cause.message ?: "Missing 'id' parameter.")
|
||||
}
|
||||
}
|
||||
|
||||
get("/libraries/{id}") {
|
||||
try {
|
||||
val id = call.pathParameters["id"]!!.toLong()
|
||||
val library = libraryService.readLibraryById(id)
|
||||
call.respond(HttpStatusCode.OK, library)
|
||||
} catch (cause: DbElementNotFoundException) {
|
||||
call.respond(HttpStatusCode.BadRequest, cause.message ?: "Unable to find Library.")
|
||||
} catch (cause: NumberFormatException) {
|
||||
call.respond(HttpStatusCode.BadRequest, "Unable to parse number format. \"${call.pathParameters["id"]}\" is not a number.")
|
||||
}
|
||||
}
|
||||
|
||||
get("/libraries/{libraryId}/items/{itemId}") {
|
||||
call.respondText("You asked for ${call.parameters["itemId"]} from ${call.parameters["libraryId"]}")
|
||||
// TODO Add search for collection_it where itemID && libraryID
|
||||
}
|
||||
|
||||
post("/libraries") {
|
||||
val library = call.receive<Library>()
|
||||
call.respondText("${library.name} is posted")
|
||||
val library = call.receive<NewLibrary>()
|
||||
try {
|
||||
val id = libraryService.create(library)
|
||||
call.respondText("${library.name} is posted with the ID: $id")
|
||||
} catch (cause: DbElementInsertionException) {
|
||||
call.respond(HttpStatusCode.BadRequest, cause.message ?: "Unable to insert Library.")
|
||||
}
|
||||
}
|
||||
|
||||
patch("/libraries") {
|
||||
val library = call.receive<Library>()
|
||||
call.respondText("${library.name} is patched")
|
||||
try {
|
||||
val library = call.receive<Library>()
|
||||
val patchedLibrary = libraryService.update(library)
|
||||
call.respond(HttpStatusCode.OK, patchedLibrary)
|
||||
} catch (cause: DbElementInsertionException) {
|
||||
log.error(cause.message)
|
||||
call.respond(HttpStatusCode.BadRequest, cause.message ?: "Unable to update Library.")
|
||||
} catch (cause: DbElementInsertionException) {
|
||||
log.error(cause.message)
|
||||
call.respond(HttpStatusCode.BadRequest, cause.message ?: "Bad Arguments")
|
||||
} catch (cause: ContentTransformationException) {
|
||||
log.error(cause.message)
|
||||
call.respond(HttpStatusCode.BadRequest, cause.message ?: "Bad Arguments")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
delete("/libraries") {
|
||||
call.respondText("We hate to see you go!")
|
||||
try {
|
||||
val id = call.parameters["id"]!!.toLong()
|
||||
log.info("Deleting item with id=$id")
|
||||
libraryService.delete(id)
|
||||
call.respondText(":(", status = HttpStatusCode.OK)
|
||||
} catch (cause: DbElementNotFoundException) {
|
||||
log.error(cause.message, cause)
|
||||
call.respond(HttpStatusCode.BadRequest, cause.message ?: "Bad Arguments")
|
||||
} catch (cause: NumberFormatException) {
|
||||
log.error(cause.message, cause)
|
||||
call.respond(HttpStatusCode.BadRequest, cause.message ?: "Invalid ID format")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,25 +2,101 @@ package codes.kalar.service
|
||||
|
||||
import codes.kalar.exception.DbElementInsertionException
|
||||
import codes.kalar.exception.DbElementNotFoundException
|
||||
import kotlinx.serialization.json.Json
|
||||
import codes.kalar.model.Library
|
||||
import codes.kalar.model.NewLibrary
|
||||
import java.sql.*
|
||||
|
||||
class LibraryService(private val connection: Connection) {
|
||||
|
||||
companion object {
|
||||
private const val SELECT_LIBRARY_BY_ID = ""
|
||||
private const val INSERT_LIBRARY = ""
|
||||
private const val UPDATE_LIBRARY_BY_ID = ""
|
||||
private const val SELECT_LIBRARY_BY_ID = "SELECT * FROM library WHERE id = ?"
|
||||
private const val SELECT_ALL_LIBRARIES = "SELECT * FROM library"
|
||||
private const val INSERT_LIBRARY = "INSERT INTO library (name, address, is_archived) VALUES (?, ?, ?)"
|
||||
private const val UPDATE_LIBRARY_BY_ID = "UPDATE library SET name = ?, address = ? WHERE id = ?"
|
||||
// In the event are "deleted" erroneously, having a flag set instead of actually removing the entry allows
|
||||
// for quick reversal.
|
||||
private const val ARCHIVE_LIBRARY_BY_ID = ""
|
||||
private const val ARCHIVE_LIBRARY_BY_ID = "UPDATE library SET is_archived = true WHERE id = ?"
|
||||
}
|
||||
|
||||
suspend fun create() {}
|
||||
suspend fun create(library: NewLibrary): Long {
|
||||
val statement = connection.prepareStatement(INSERT_LIBRARY, Statement.RETURN_GENERATED_KEYS)
|
||||
statement.setString(1, library.name)
|
||||
statement.setString(2, library.address)
|
||||
statement.setBoolean(3, library.isArchived)
|
||||
try {
|
||||
statement.execute()
|
||||
val key = statement.generatedKeys
|
||||
if (key.next()) {
|
||||
return key.getLong(1)
|
||||
}
|
||||
return -1
|
||||
} catch (cause: SQLException) {
|
||||
throw DbElementInsertionException("Could not insert library: ${cause.message}")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun read() {}
|
||||
suspend fun readLibraryById(id: Long): Library {
|
||||
val statement = connection.prepareStatement(SELECT_LIBRARY_BY_ID)
|
||||
statement.setLong(1, id)
|
||||
try {
|
||||
val resultSet = statement.executeQuery()
|
||||
if (resultSet.next() && !resultSet.getBoolean("is_archived")) {
|
||||
return createLibraryFromResult(resultSet)
|
||||
} else {
|
||||
throw DbElementNotFoundException("Could not find collection item. resultSet: $resultSet")
|
||||
}
|
||||
} catch(cause: SQLException) {
|
||||
throw DbElementNotFoundException("Could not find collection item with id $id")
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun update() {}
|
||||
fun readAllLibraries(): List<Library> {
|
||||
val libraries = ArrayList<Library>()
|
||||
val statement = connection.prepareStatement(SELECT_ALL_LIBRARIES)
|
||||
val resultSet = statement.executeQuery()
|
||||
while (resultSet.next()) {
|
||||
if (!resultSet.getBoolean("is_archived")) {
|
||||
val library = createLibraryFromResult(resultSet)
|
||||
libraries.add(library)
|
||||
}
|
||||
}
|
||||
return libraries
|
||||
}
|
||||
|
||||
suspend fun delete() {}
|
||||
suspend fun update(library: Library): Library {
|
||||
val statement = connection.prepareStatement(UPDATE_LIBRARY_BY_ID)
|
||||
try {
|
||||
statement.setString(1, library.name)
|
||||
statement.setString(2, library.address)
|
||||
statement.setLong(3, library.id)
|
||||
statement.execute()
|
||||
return readLibraryById(library.id)
|
||||
} catch (e: SQLException) {
|
||||
throw DbElementInsertionException("${e.message}\ncollectionItem: $library\n statement: $statement\n ", e)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
throw DbElementInsertionException("${e.message}\ncollectionItem: $library\n statement: $statement\n ", e)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun delete(id: Long) {
|
||||
val statement = connection.prepareStatement(ARCHIVE_LIBRARY_BY_ID)
|
||||
try {
|
||||
statement.setLong(1, id)
|
||||
statement.execute()
|
||||
} catch (e: SQLException) {
|
||||
throw DbElementNotFoundException("Could not find Library with id $id")
|
||||
}
|
||||
}
|
||||
|
||||
private fun createLibraryFromResult(resultSet: ResultSet): Library {
|
||||
try {
|
||||
val id = resultSet.getLong("id")
|
||||
val name = resultSet.getString("name")
|
||||
val address = resultSet.getString("address")
|
||||
val isArchived = resultSet.getBoolean("is_archived")
|
||||
return Library(id, name, address, isArchived)
|
||||
} catch (cause: NullPointerException) {
|
||||
throw DbElementInsertionException("${cause.message}\nresultSet = ${resultSet.metaData}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,17 +81,31 @@ paths:
|
||||
|
||||
/libraries:
|
||||
get:
|
||||
description: ""
|
||||
description: "Search for libraries based on ID or get all libraries"
|
||||
parameters:
|
||||
- name: "id"
|
||||
in: "path"
|
||||
required: true
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: id=27
|
||||
example: id=1
|
||||
responses:
|
||||
"200":
|
||||
description: "OK"
|
||||
description: "Either a single library, or a list of all libraries."
|
||||
"400":
|
||||
description: "Bad Request. Likely, the ID you are looking for doesn't exist."
|
||||
post:
|
||||
description: "The method to add a new library."
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/NewLibrary"
|
||||
responses:
|
||||
"200":
|
||||
description: "The ID of the newly created library will be returned."
|
||||
"400":
|
||||
description: "Bad Request. Unable to insert the new library."
|
||||
|
||||
components:
|
||||
schemas:
|
||||
|
||||
@@ -113,7 +113,7 @@ components:
|
||||
example: ""
|
||||
sortTitle:
|
||||
type: "string"
|
||||
description: "The tite of the book with any articles moved to the end for sorting purposes."
|
||||
description: "The title of the book with any articles moved to the end for sorting purposes."
|
||||
example: "Fellowship of the Ring, The"
|
||||
format:
|
||||
type: "string"
|
||||
@@ -194,7 +194,7 @@ components:
|
||||
example: ""
|
||||
sortTitle:
|
||||
type: "string"
|
||||
description: "The tite of the book with any articles moved to the end for sorting purposes."
|
||||
description: "The title of the book with any articles moved to the end for sorting purposes."
|
||||
example: "Fellowship of the Ring, The"
|
||||
format:
|
||||
type: "string"
|
||||
|
||||
Reference in New Issue
Block a user