interface Repo<E> {
fun create(element: E): Boolean
fun read(): List<Item<E>>
fun read(id: String): Item<E>?
fun read(ids: List<String>): List<Item<E>>
fun update(id: String, value: E): Boolean
fun delete(id: String): Boolean
}
class ListRepo<E> : Repo<E> {
private val list = ConcurrentHashMap<String, E>()
override fun create(element: E): Boolean {
list[UUID.randomUUID().toString()] = element
return true
}
override fun read(): List<Item<E>> =
list.map {
Item(it.value, it.key)
}
override fun read(id: String): Item<E>? =
list[id]?.let {
Item(it, id)
}
override fun read(ids: List<String>): List<Item<E>> =
ids.mapNotNull { id ->
list[id]?.let {
Item(it, id)
}
}
override fun update(id: String, value: E): Boolean =
if (list.containsKey(id)) {
list[id] = value
true
} else false
override fun delete(id: String): Boolean =
list.remove(id) != null
val studentsRepo = ListRepo<Student>()
fun createTestData() {
listOf(
Student("Sheldon", "Cooper"),
Student("Leonard", "Hofstadter"),
Student("Howard", "Wolowitz"),
Student("Penny", "Hofstadter"),
).map {
studentsRepo.create(it)
}
}
fun Application.config(isTest: Boolean) {
install(ContentNegotiation) {
json()
}
if (isTest) {
createTestData()
install(createApplicationPlugin("DelayEmulator") {
onCall {
delay(1000L)
}
})
}
}
fun Route.studentRoutes() {
route(Config.studentsPath) {
get {
val students = studentsRepo.read()
if (students.isEmpty()) {
call.respondText(
"No students found",
status = HttpStatusCode.NotFound
)
} else {
call.respond(students)
}
}
get("{id}") {
val id =
call.parameters["id"]
?: return@get call.respondText(
"Missing or malformed id",
status = HttpStatusCode.BadRequest)
val studentItem =
studentsRepo.read(id)
?: return@get call.respondText(
"No student with id $id",
status = HttpStatusCode.NotFound)
call.respond(studentItem)
}
post {
val student = call.receive<Student>()
studentsRepo.create(student)
call.respondText(
"Student stored correctly",
status = HttpStatusCode.Created
)
}
delete("{id}") {
val id = call.parameters["id"]
?: return@delete call.respond(
HttpStatusCode.BadRequest)
if (studentsRepo.delete(id)) {
call.respondText(
"Student removed correctly",
status = HttpStatusCode.Accepted)
} else {
call.respondText(
"Not Found",
status = HttpStatusCode.NotFound)
}
}
put("{id}") {
val id = call.parameters["id"]
?: return@put call.respondText(
"Missing or malformed id",
status = HttpStatusCode.BadRequest)
studentsRepo.read(id)
?: return@put call.respondText(
"No student with id $id",
status = HttpStatusCode.NotFound)
val newStudent = call.receive<Student>()
studentsRepo.update(id, newStudent)
call.respondText(
"Student updates correctly",
status = HttpStatusCode.Created)}
class ApplicationTest : StringSpec({
"Students routes" {
testApplication {
application {
main()
}
// this: TestApplicationBuilder
val students = withClue("read") {
val response = client.get("/students/")
response.status shouldBe HttpStatusCode.OK
Json.decodeFromString<List<Item<Student>>>(
response.bodyAsText()
).apply { size shouldBe 4 }
}
val newStudents = withClue("create") {
val response = client.post("/students/") {
contentType(ContentType.Application.Json)
setBody(Student("Raj", "Koothrappali").json)
}
response.status shouldBe HttpStatusCode.Created
Json.decodeFromString<List<Item<Student>>>(
client.get("/students/").bodyAsText()
).apply { size shouldBe 5 }
}