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",
|
"number": "KA01HD6677",
|
||||||
"owner": "gowthaman"
|
"owner": "gowthaman"
|
||||||
},
|
},
|
||||||
"uniqueIdentifier": ""
|
"uniqueIdentifier": "KA01HD6677"
|
||||||
}
|
}
|
||||||
|
|
||||||
### create row
|
### create row
|
||||||
@ -52,7 +52,7 @@ GET http://localhost:9001/api/log/log-0000000001
|
|||||||
Authorization: Bearer {{auth-token}}
|
Authorization: Bearer {{auth-token}}
|
||||||
|
|
||||||
### get row
|
### get row
|
||||||
GET http://localhost:9001/api/vehicle/KA01HD6667
|
GET http://localhost:9001/api/vehicle/KA01HD6677
|
||||||
Authorization: Bearer {{auth-token}}
|
Authorization: Bearer {{auth-token}}
|
||||||
|
|
||||||
### query row
|
### 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
|
### update field
|
||||||
PATCH http://localhost:9001/api/vehicle/KA01HD6667
|
PATCH http://localhost:9001/api/vehicle/KA01HD6667
|
||||||
Content-Type: application/json
|
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))
|
get("/{entity}/{id}", Entities::view, Roles(adminRole, viewRole))
|
||||||
post("/{entity}/query/{id}", Entities::sqlQueryById, Roles(adminRole, viewRole))
|
post("/{entity}/search", Entities::search, Roles(adminRole, viewRole))
|
||||||
post("/{entity}/query", Entities::sqlQueryRaw, Roles(adminRole, viewRole))
|
|
||||||
post("/{entity}", Entities::create, Roles(adminRole, createRole))
|
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))
|
put("/{entity}/{id}", Entities::update, Roles(adminRole, updateRole))
|
||||||
patch("/{entity}/{id}", Entities::patch, 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.JsonNode
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||||
import com.restapi.domain.*
|
import com.restapi.domain.*
|
||||||
import com.restapi.domain.Session.currentUser
|
|
||||||
import com.restapi.domain.Session.database
|
import com.restapi.domain.Session.database
|
||||||
import com.restapi.domain.Session.findDataModelByEntityAndUniqId
|
import com.restapi.domain.Session.findDataModelByEntityAndUniqId
|
||||||
import com.restapi.integ.Scripting
|
import com.restapi.integ.Scripting
|
||||||
import io.ebean.CallableSql
|
import io.javalin.http.BadRequestResponse
|
||||||
import io.ebean.RawSqlBuilder
|
import io.javalin.http.Context
|
||||||
import io.javalin.http.*
|
import io.javalin.http.bodyAsClass
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.sql.Types
|
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.time.LocalTime
|
import java.time.LocalTime
|
||||||
import java.time.format.DateTimeFormatter
|
import java.time.format.DateTimeFormatter
|
||||||
|
import java.time.format.DateTimeParseException
|
||||||
|
|
||||||
enum class QueryParamType {
|
enum class QueryParamType {
|
||||||
STRING, NUMBER, DATETIME, DATE
|
STRING, NUMBER, DATETIME, DATE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
data class RawQuery(
|
@JsonDeserialize(using = QueryParamDeSerializer::class)
|
||||||
val sql: String,
|
|
||||||
val params: Map<String, QueryParam>
|
|
||||||
)
|
|
||||||
|
|
||||||
data class QueryById(
|
|
||||||
val params: List<QueryParam>
|
|
||||||
)
|
|
||||||
|
|
||||||
@JsonDeserialize(using = QueryByIdParamsDeSerializer::class)
|
|
||||||
sealed class QueryParam {
|
sealed class QueryParam {
|
||||||
data class Simple(val simple: String) : QueryParam()
|
data class StrParam(val str: String) : QueryParam()
|
||||||
data class Complex(val type: QueryParamType, val value: String) : QueryParam() {
|
data class NumberParam(val nbr: Number) : QueryParam()
|
||||||
|
data class ComplexParam(val type: QueryParamType, val value: String) : QueryParam() {
|
||||||
fun getValueComplex(): Any {
|
fun getValueComplex(): Any {
|
||||||
return when (type) {
|
return when (type) {
|
||||||
QueryParamType.STRING -> value
|
QueryParamType.STRING -> value
|
||||||
QueryParamType.NUMBER -> if (value.matches(Regex("\\d+"))) value.toLong() else value.toDouble()
|
QueryParamType.NUMBER -> (if (value.matches(Regex("\\d+"))) value.toLongOrNull() else value.toDoubleOrNull())
|
||||||
QueryParamType.DATETIME -> LocalDateTime.parse(
|
?: throw IllegalArgumentException("$value is not a number")
|
||||||
value,
|
|
||||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
|
||||||
)
|
|
||||||
|
|
||||||
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 {
|
fun getValue(): Any {
|
||||||
return when (this) {
|
return when (this) {
|
||||||
is Complex -> getValueComplex()
|
is ComplexParam -> getValueComplex()
|
||||||
is Simple -> simple
|
is StrParam -> str
|
||||||
else -> {}
|
is NumberParam -> nbr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class QueryByIdParamsDeSerializer : JsonDeserializer<QueryParam>() {
|
class QueryParamDeSerializer : JsonDeserializer<QueryParam>() {
|
||||||
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): QueryParam {
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): QueryParam {
|
||||||
val node = p.readValueAsTree<JsonNode>()
|
val node = p.readValueAsTree<JsonNode>()
|
||||||
return if (node.isTextual) {
|
return if (node.isTextual) {
|
||||||
QueryParam.Simple(node.asText())
|
QueryParam.StrParam(node.asText())
|
||||||
} else {
|
} else if (node.isNumber) {
|
||||||
QueryParam.Complex(
|
QueryParam.NumberParam(node.numberValue())
|
||||||
|
} else if (node.isObject) {
|
||||||
|
QueryParam.ComplexParam(
|
||||||
QueryParamType.valueOf(node.get("type").textValue()),
|
QueryParamType.valueOf(node.get("type").textValue()),
|
||||||
node.get("value").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 {
|
object Entities {
|
||||||
private val logger = LoggerFactory.getLogger("Entities")
|
private val logger = LoggerFactory.getLogger("Entities")
|
||||||
@ -116,108 +115,22 @@ object Entities {
|
|||||||
e.update()
|
e.update()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun action(ctx: Context) {}
|
|
||||||
fun approve(ctx: Context) {
|
|
||||||
approveOrReject(ctx, ApprovalStatus.APPROVED)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun reject(ctx: Context) {
|
fun search(ctx: Context) {
|
||||||
approveOrReject(ctx, ApprovalStatus.REJECTED)
|
val sql = ctx.bodyAsClass<SearchParams>()
|
||||||
}
|
|
||||||
|
|
||||||
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}")
|
|
||||||
|
|
||||||
val entity = ctx.pathParam("entity")
|
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(
|
ctx.json(
|
||||||
database.find(DataModel::class.java)
|
database.find(DataModel::class.java)
|
||||||
.setRawSql(RawSqlBuilder.parse(query.sql).create())
|
.where()
|
||||||
|
.eq("entityName", entity)
|
||||||
.apply {
|
.apply {
|
||||||
sql.params.forEachIndexed { index, entry ->
|
sql.forEach { (t, u) ->
|
||||||
setParameter(index + 1, entry.getValue())
|
|
||||||
|
if (!SafeStringDeserializer.isSafe(t)) {
|
||||||
|
throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
eq("data->>'$t'", u.getValue())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.findList()
|
.findList()
|
||||||
@ -225,6 +138,7 @@ object Entities {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun view(it: Context) {
|
fun view(it: Context) {
|
||||||
database.save(
|
database.save(
|
||||||
AuditLog().apply {
|
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)
|
data class SequenceNumber(val number: String)
|
||||||
|
|
||||||
|
|||||||
@ -179,18 +179,6 @@ enum class JobType {
|
|||||||
SCRIPT, DB
|
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
|
@Entity
|
||||||
open class JobModel : BaseTenantModel() {
|
open class JobModel : BaseTenantModel() {
|
||||||
@ -242,12 +230,17 @@ open class AnonSession : BaseTenantModel() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SafeStringDeserializer : JsonDeserializer<String>() {
|
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 {
|
override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): String {
|
||||||
|
|
||||||
val text = p.text
|
val text = p.text
|
||||||
if (!regex.matches(text)) throw IllegalArgumentException()
|
if (!isSafe(text)) throw IllegalArgumentException()
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user