remove unused stuff
This commit is contained in:
parent
ce622a118f
commit
0c9313b77c
13
api.http
13
api.http
@ -8,7 +8,7 @@ Authorization: {{auth-token}}
|
||||
"number": "KA01HD6677",
|
||||
"owner": "gowthaman"
|
||||
},
|
||||
"uniqueIdentifier": ""
|
||||
"uniqueIdentifier": "KA01HD6677"
|
||||
}
|
||||
|
||||
### create row
|
||||
@ -52,7 +52,7 @@ GET http://localhost:9001/api/log/log-0000000001
|
||||
Authorization: Bearer {{auth-token}}
|
||||
|
||||
### get row
|
||||
GET http://localhost:9001/api/vehicle/KA01HD6667
|
||||
GET http://localhost:9001/api/vehicle/KA01HD6677
|
||||
Authorization: Bearer {{auth-token}}
|
||||
|
||||
### query row
|
||||
@ -67,6 +67,15 @@ Authorization: {{set-auth-token}}
|
||||
}
|
||||
}
|
||||
|
||||
### search row
|
||||
POST http://localhost:9001/api/vehicle/search
|
||||
Content-Type: application/json
|
||||
Authorization: {{auth-token}}
|
||||
|
||||
{
|
||||
"number": "KA01HD6677"
|
||||
}
|
||||
|
||||
### update field
|
||||
PATCH http://localhost:9001/api/vehicle/KA01HD6667
|
||||
Content-Type: application/json
|
||||
|
||||
@ -300,17 +300,11 @@ fun main(args: Array<String>) {
|
||||
)
|
||||
}
|
||||
}
|
||||
post("/script/database/{name}", Entities::executeStoredProcedure, Roles(adminRole, Role.DbOps))
|
||||
post("/script/{file}/{name}", Entities::executeScript, Roles(adminRole, Role.DbOps))
|
||||
|
||||
get("/{entity}/{id}", Entities::view, Roles(adminRole, viewRole))
|
||||
post("/{entity}/query/{id}", Entities::sqlQueryById, Roles(adminRole, viewRole))
|
||||
post("/{entity}/query", Entities::sqlQueryRaw, Roles(adminRole, viewRole))
|
||||
post("/{entity}/search", Entities::search, Roles(adminRole, viewRole))
|
||||
post("/{entity}", Entities::create, Roles(adminRole, createRole))
|
||||
|
||||
put("/{entity}/approve/{id}", Entities::approve, Roles(adminRole, approveOrRejectRole))
|
||||
put("/{entity}/reject/{id}", Entities::reject, Roles(adminRole, approveOrRejectRole))
|
||||
put("/{entity}/{action}/{id}", Entities::action, Roles(adminRole, Role.Entity))
|
||||
|
||||
put("/{entity}/{id}", Entities::update, Roles(adminRole, updateRole))
|
||||
patch("/{entity}/{id}", Entities::patch, Roles(adminRole, updateRole))
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
package com.restapi
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser
|
||||
import com.fasterxml.jackson.databind.DeserializationContext
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import com.restapi.controllers.QueryParam
|
||||
import com.restapi.controllers.RawQuery
|
||||
|
||||
class FDeserializer : JsonDeserializer<F>() {
|
||||
override fun deserialize(p: JsonParser, p1: DeserializationContext?): F {
|
||||
//custom logic to do thia
|
||||
val node = p.readValueAsTree<JsonNode>()
|
||||
|
||||
if (node.isObject) {
|
||||
if (node.has("name")) {
|
||||
return F.P(name = node.get("name").textValue())
|
||||
} else if (node.has("num")) {
|
||||
return F.Q(num = node.get("num").textValue())
|
||||
} else if (node.has("tag")) {
|
||||
return F.D(tag = node.get("tag").textValue())
|
||||
}
|
||||
|
||||
} else {
|
||||
//incorrect
|
||||
}
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
|
||||
}
|
||||
val om = jacksonObjectMapper()
|
||||
|
||||
@JsonDeserialize(using = FDeserializer::class)
|
||||
sealed interface F {
|
||||
data class P(val name: String) : F
|
||||
data class Q(val num: String) : F
|
||||
data class D(val tag: String) : F
|
||||
}
|
||||
|
||||
val j = """
|
||||
{"name":"a"}
|
||||
""".trimIndent()
|
||||
|
||||
val j2 = """
|
||||
{"num":"a"}
|
||||
""".trimIndent()
|
||||
|
||||
val j3 = """
|
||||
{"tag":"a"}
|
||||
""".trimIndent()
|
||||
|
||||
val j4 = """
|
||||
{
|
||||
"sql":"aaaa",
|
||||
"params": {
|
||||
"a":"b",
|
||||
"c": {
|
||||
"type":"STRING",
|
||||
"value":"aaaa"
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
fun main() {
|
||||
println(om.readValue<F>(j))
|
||||
println(om.readValue<F>(j2))
|
||||
println(om.readValue<F>(j3))
|
||||
println(om.readValue<RawQuery>(j4))
|
||||
}
|
||||
@ -6,82 +6,81 @@ import com.fasterxml.jackson.databind.JsonDeserializer
|
||||
import com.fasterxml.jackson.databind.JsonNode
|
||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import com.restapi.domain.*
|
||||
import com.restapi.domain.Session.currentUser
|
||||
import com.restapi.domain.Session.database
|
||||
import com.restapi.domain.Session.findDataModelByEntityAndUniqId
|
||||
import com.restapi.integ.Scripting
|
||||
import io.ebean.CallableSql
|
||||
import io.ebean.RawSqlBuilder
|
||||
import io.javalin.http.*
|
||||
import io.javalin.http.BadRequestResponse
|
||||
import io.javalin.http.Context
|
||||
import io.javalin.http.bodyAsClass
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.sql.Types
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.time.format.DateTimeParseException
|
||||
|
||||
enum class QueryParamType {
|
||||
STRING, NUMBER, DATETIME, DATE
|
||||
}
|
||||
|
||||
|
||||
data class RawQuery(
|
||||
val sql: String,
|
||||
val params: Map<String, QueryParam>
|
||||
)
|
||||
|
||||
data class QueryById(
|
||||
val params: List<QueryParam>
|
||||
)
|
||||
|
||||
@JsonDeserialize(using = QueryByIdParamsDeSerializer::class)
|
||||
@JsonDeserialize(using = QueryParamDeSerializer::class)
|
||||
sealed class QueryParam {
|
||||
data class Simple(val simple: String) : QueryParam()
|
||||
data class Complex(val type: QueryParamType, val value: String) : QueryParam() {
|
||||
data class StrParam(val str: String) : QueryParam()
|
||||
data class NumberParam(val nbr: Number) : QueryParam()
|
||||
data class ComplexParam(val type: QueryParamType, val value: String) : QueryParam() {
|
||||
fun getValueComplex(): Any {
|
||||
return when (type) {
|
||||
QueryParamType.STRING -> value
|
||||
QueryParamType.NUMBER -> if (value.matches(Regex("\\d+"))) value.toLong() else value.toDouble()
|
||||
QueryParamType.DATETIME -> LocalDateTime.parse(
|
||||
value,
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||
)
|
||||
QueryParamType.NUMBER -> (if (value.matches(Regex("\\d+"))) value.toLongOrNull() else value.toDoubleOrNull())
|
||||
?: throw IllegalArgumentException("$value is not a number")
|
||||
|
||||
QueryParamType.DATE -> LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
|
||||
QueryParamType.DATETIME -> try {
|
||||
LocalDateTime.parse(
|
||||
value,
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||
)
|
||||
} catch (e: DateTimeParseException) {
|
||||
throw IllegalArgumentException("unable to parse $value as datetime")
|
||||
}
|
||||
|
||||
QueryParamType.DATE -> try {
|
||||
LocalDate.parse(value, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
|
||||
} catch (e: DateTimeParseException) {
|
||||
throw IllegalArgumentException("unable to parse $value as date")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getValue(): Any {
|
||||
return when (this) {
|
||||
is Complex -> getValueComplex()
|
||||
is Simple -> simple
|
||||
else -> {}
|
||||
is ComplexParam -> getValueComplex()
|
||||
is StrParam -> str
|
||||
is NumberParam -> nbr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class QueryByIdParamsDeSerializer : JsonDeserializer<QueryParam>() {
|
||||
class QueryParamDeSerializer : JsonDeserializer<QueryParam>() {
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): QueryParam {
|
||||
val node = p.readValueAsTree<JsonNode>()
|
||||
return if (node.isTextual) {
|
||||
QueryParam.Simple(node.asText())
|
||||
} else {
|
||||
QueryParam.Complex(
|
||||
QueryParam.StrParam(node.asText())
|
||||
} else if (node.isNumber) {
|
||||
QueryParam.NumberParam(node.numberValue())
|
||||
} else if (node.isObject) {
|
||||
QueryParam.ComplexParam(
|
||||
QueryParamType.valueOf(node.get("type").textValue()),
|
||||
node.get("value").textValue(),
|
||||
)
|
||||
} else {
|
||||
throw BadRequestResponse("unable to find valid query param for $node")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
enum class ResultType {
|
||||
INTEGER, DECIMAL, STRING, DATETIME, ARRAY, OBJECT
|
||||
}
|
||||
|
||||
data class RejectAction(val reason: String)
|
||||
data class StoredProcedure(val input: Map<String, Any>, val output: Map<String, ResultType> = hashMapOf())
|
||||
|
||||
object Entities {
|
||||
private val logger = LoggerFactory.getLogger("Entities")
|
||||
@ -116,108 +115,22 @@ object Entities {
|
||||
e.update()
|
||||
}
|
||||
|
||||
fun action(ctx: Context) {}
|
||||
fun approve(ctx: Context) {
|
||||
approveOrReject(ctx, ApprovalStatus.APPROVED)
|
||||
}
|
||||
|
||||
fun reject(ctx: Context) {
|
||||
approveOrReject(ctx, ApprovalStatus.REJECTED)
|
||||
}
|
||||
|
||||
private fun approveOrReject(ctx: Context, rejected: ApprovalStatus) {
|
||||
val e = database.findDataModelByEntityAndUniqId(ctx.pathParam("entity"), ctx.pathParam("id"))
|
||||
val reject = ctx.bodyAsClass<RejectAction>()
|
||||
e.approvalStatus = rejected
|
||||
e.comments.add(Comments(text = reject.reason, by = currentUser()))
|
||||
e.save()
|
||||
}
|
||||
|
||||
fun executeScript(ctx: Context) {
|
||||
val name = ctx.pathParam("name")
|
||||
val file = ctx.pathParam("file")
|
||||
val params = ctx.bodyAsClass<Map<String, Any>>()
|
||||
ctx.json(
|
||||
Scripting.execute(file, name, params)
|
||||
)
|
||||
}
|
||||
|
||||
fun executeStoredProcedure(ctx: Context) {
|
||||
val name = ctx.pathParam("name")
|
||||
val sp = ctx.bodyAsClass<StoredProcedure>()
|
||||
|
||||
val inputParams = sp.input.entries.toList()
|
||||
val outputParams = sp.output.entries.toList()
|
||||
|
||||
val placeholders = (0..inputParams.size + 1).joinToString(",") { "?" }
|
||||
|
||||
val sql = "{call $name($placeholders)}"
|
||||
val cs: CallableSql = database.createCallableSql(sql)
|
||||
|
||||
inputParams.forEachIndexed { index, entry ->
|
||||
cs.setParameter(index + 1, entry.value)
|
||||
}
|
||||
cs.setParameter(inputParams.size + 1, Session.currentTenant())
|
||||
|
||||
outputParams.forEachIndexed { idx, entry ->
|
||||
when (entry.value) {
|
||||
ResultType.INTEGER -> cs.registerOut(idx + 1, Types.INTEGER)
|
||||
ResultType.DECIMAL -> cs.registerOut(idx + 1, Types.DOUBLE)
|
||||
ResultType.STRING -> cs.registerOut(idx + 1, Types.VARCHAR)
|
||||
ResultType.DATETIME -> cs.registerOut(idx + 1, Types.DATE)
|
||||
ResultType.ARRAY -> cs.registerOut(idx + 1, Types.ARRAY)
|
||||
ResultType.OBJECT -> cs.registerOut(idx + 1, Types.JAVA_OBJECT)
|
||||
}
|
||||
|
||||
}
|
||||
val done = database.execute(cs)
|
||||
val output = outputParams.mapIndexed { index, entry ->
|
||||
Pair(entry.key, cs.getObject(index + 1))
|
||||
}.toMap()
|
||||
|
||||
ctx.json(
|
||||
mapOf(
|
||||
"done" to done,
|
||||
"output" to output
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun sqlQueryRaw(ctx: Context) {
|
||||
val sql = ctx.bodyAsClass<RawQuery>()
|
||||
logger.warn("running sql ${sql.sql}, with params ${sql.params}")
|
||||
ctx.json(
|
||||
database.find(DataModel::class.java)
|
||||
.setRawSql(
|
||||
RawSqlBuilder.parse(sql.sql).create()
|
||||
).apply {
|
||||
sql.params.forEach { (t, u) ->
|
||||
setParameter(t, u.getValue())
|
||||
}
|
||||
}
|
||||
.findList()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
fun sqlQueryById(ctx: Context) {
|
||||
val sql = ctx.bodyAsClass<QueryById>()
|
||||
val sqlId = ctx.pathParam("id")
|
||||
logger.warn("running sqlId $sqlId, with params ${sql.params}")
|
||||
fun search(ctx: Context) {
|
||||
val sql = ctx.bodyAsClass<SearchParams>()
|
||||
|
||||
val entity = ctx.pathParam("entity")
|
||||
val query = database.find(SqlModel::class.java)
|
||||
.where()
|
||||
.eq("entityName", entity)
|
||||
.eq("sqlId", sqlId)
|
||||
.findOne() ?: throw NotFoundResponse("sql not found for $entity, $sqlId")
|
||||
|
||||
ctx.json(
|
||||
database.find(DataModel::class.java)
|
||||
.setRawSql(RawSqlBuilder.parse(query.sql).create())
|
||||
.where()
|
||||
.eq("entityName", entity)
|
||||
.apply {
|
||||
sql.params.forEachIndexed { index, entry ->
|
||||
setParameter(index + 1, entry.getValue())
|
||||
sql.forEach { (t, u) ->
|
||||
|
||||
if (!SafeStringDeserializer.isSafe(t)) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
eq("data->>'$t'", u.getValue())
|
||||
}
|
||||
}
|
||||
.findList()
|
||||
@ -225,6 +138,7 @@ object Entities {
|
||||
|
||||
}
|
||||
|
||||
|
||||
fun view(it: Context) {
|
||||
database.save(
|
||||
AuditLog().apply {
|
||||
@ -317,18 +231,7 @@ object Entities {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!setupEntity.preSaveScript.isNullOrEmpty()) {
|
||||
val ok = Scripting.execute(setupEntity.preSaveScript!!, "preSave", this) as Boolean
|
||||
if (!ok) {
|
||||
throw BadRequestResponse("PreSave Failed")
|
||||
}
|
||||
}
|
||||
|
||||
if (setupEntity.approvalLevels > 0) {
|
||||
|
||||
this.approvalStatus = ApprovalStatus.PENDING
|
||||
this.requiredApprovalLevels = setupEntity.approvalLevels
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -371,5 +274,7 @@ object Entities {
|
||||
}
|
||||
}
|
||||
|
||||
typealias SearchParams = Map<String, QueryParam>
|
||||
|
||||
data class SequenceNumber(val number: String)
|
||||
|
||||
|
||||
@ -179,18 +179,6 @@ enum class JobType {
|
||||
SCRIPT, DB
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Index(unique = true, name = "sql_unique_id", columnNames = ["entity_name", "sql_id", "tenant_id"])
|
||||
open class SqlModel : BaseTenantModel() {
|
||||
|
||||
@JsonDeserialize(using = SafeStringDeserializer::class)
|
||||
var sqlId: String = ""
|
||||
|
||||
var entityName: String = ""
|
||||
|
||||
@Column(columnDefinition = "text")
|
||||
var sql: String = ""
|
||||
}
|
||||
|
||||
@Entity
|
||||
open class JobModel : BaseTenantModel() {
|
||||
@ -242,12 +230,17 @@ open class AnonSession : BaseTenantModel() {
|
||||
}
|
||||
|
||||
class SafeStringDeserializer : JsonDeserializer<String>() {
|
||||
private val regex = Regex("^[a-zA-Z0-9\\-_\\.]+$")
|
||||
|
||||
companion object {
|
||||
private val regex = Regex("^[a-zA-Z0-9\\-_\\.]+$")
|
||||
|
||||
fun isSafe(s: String) = regex.matches(s)
|
||||
}
|
||||
|
||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): String {
|
||||
|
||||
val text = p.text
|
||||
if (!regex.matches(text)) throw IllegalArgumentException()
|
||||
if (!isSafe(text)) throw IllegalArgumentException()
|
||||
return text
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user