simple api model

This commit is contained in:
gowthaman.b
2023-11-10 11:06:14 +05:30
parent 203c2b97a7
commit 01e197b0f8
14 changed files with 364 additions and 232 deletions

View File

@@ -0,0 +1,177 @@
package com.restapi
import com.fasterxml.jackson.databind.JsonMappingException
import com.restapi.config.AppConfig.Companion.appConfig
import com.restapi.domain.DataModel
import com.restapi.domain.DataNotFoundException
import com.restapi.domain.Session
import com.restapi.domain.Session.creatSeq
import com.restapi.domain.Session.database
import com.restapi.domain.Session.findByEntityAndId
import com.restapi.domain.Session.nextUniqId
import io.ebean.CallableSql
import io.ebean.DuplicateKeyException
import io.ebean.RawSqlBuilder
import io.javalin.Javalin
import io.javalin.apibuilder.ApiBuilder.*
import io.javalin.http.*
import io.javalin.json.JavalinJackson
import org.slf4j.LoggerFactory
import java.time.LocalDateTime
fun main(args: Array<String>) {
val logger = LoggerFactory.getLogger("api")
Javalin
.create { cfg ->
cfg.http.generateEtags = true
if (appConfig.corsEnabled()) {
cfg.plugins.enableCors { container ->
container.add {
it.allowHost(
"http://localhost:5173",
*appConfig.corsHosts().toTypedArray()
)
}
}
}
cfg.http.defaultContentType = ContentType.JSON
cfg.compression.gzipOnly()
cfg.jsonMapper(JavalinJackson(Session.objectMapper))
}
.routes {
before("/*") { ctx ->
//validate, auth token
//allow only alpha, numeric, hypen, underscore, dot in paths
val regex = Regex("^[a-zA-Z0-9\\-_\\.]+$")
ctx.path().split("/").dropWhile { it.isEmpty() }
.forEach {
if (!it.matches(regex)) {
throw IllegalArgumentException()
}
}
}
path("/api") {
post("/execute/{name}") {
val name = it.pathParam("name")
val params = it.bodyAsClass<Map<String, Any>>()
val placeholders = (0..params.entries.size).joinToString(",") { "?" }
val sql = "{call $name($placeholders)}"
val cs: CallableSql = database.createCallableSql(sql)
params.entries.forEachIndexed { index, entry ->
cs.setParameter(index + 1, entry.value)
}
database.execute(cs)
}
get("/{entity}/{id}") {
it.json(
database.findByEntityAndId(it.pathParam("entity"), it.pathParam("id"))
)
}
post("/{entity}/query/{id}") {
val sql = it.bodyAsClass<Query>()
val query = database.findByEntityAndId(it.pathParam("entity"), it.pathParam("id"))
val querySql = query.data["sql"] as String? ?: throw NotFoundResponse()
it.json(
database.find(DataModel::class.java)
.setRawSql(
RawSqlBuilder.parse(querySql).create()
).apply {
sql.params.forEach { (t, u) ->
setParameter(t, u)
}
}
.findList()
)
}
post("/{entity}/query") {
val sql = it.bodyAsClass<Query>()
it.json(
database.find(DataModel::class.java)
.setRawSql(
RawSqlBuilder.parse(sql.sql).create()
).apply {
sql.params.forEach { (t, u) ->
setParameter(t, u)
}
}
.findList()
)
}
post("/{entity}") {
val entity = it.pathParam("entity")
val seqCreated = creatSeq(entity)
logger.debug("sequence created for $entity? = $seqCreated")
database.save(
it.bodyAsClass<DataModel>().apply {
this.entityName = entity
if (this.uniqueIdentifier.isEmpty()) {
this.uniqueIdentifier = nextUniqId(entity)
}
}
)
}
put("/{entity}/{id}") {
val e = database.findByEntityAndId(it.pathParam("entity"), it.pathParam("id"))
val newData = it.bodyAsClass<Map<String, Any>>()
e.data.putAll(newData)
e.update()
}
patch("/{entity}/{id}") {
val e = database.findByEntityAndId(it.pathParam("entity"), it.pathParam("id"))
val pv = it.bodyAsClass<PatchValue>()
e.data[pv.key] = pv.value;
e.update()
}
delete("/{entity}/{id}") {
val id = it.pathParam("id")
val e = database.findByEntityAndId(it.pathParam("entity"), it.pathParam("id"))
e.deletedBy = Session.currentUser()
e.deletedOn = LocalDateTime.now()
e.update()
e.delete()
}
}
}
.exception(DuplicateKeyException::class.java) { _, ctx ->
ctx.json(
mapOf(
"error" to "Duplicate Data"
)
).status(HttpStatus.CONFLICT)
}
.exception(DataNotFoundException::class.java) { _, ctx ->
ctx.json(
mapOf(
"error" to "Data Not Found"
)
).status(HttpStatus.NOT_FOUND)
}
.exception(IllegalArgumentException::class.java) { _, ctx ->
ctx.json(
mapOf(
"error" to "Incorrect Data"
)
).status(HttpStatus.BAD_REQUEST)
}
.exception(JsonMappingException::class.java) { _, ctx ->
ctx.json(
mapOf(
"error" to "Incorrect Data"
)
).status(HttpStatus.BAD_REQUEST)
}
.start(appConfig.portNumber())
}
data class Query(
val sql: String,
val params: Map<String, Any>
)
data class PatchValue(val key: String, val value: Any)