more stuff
This commit is contained in:
parent
1e3d9aa1f1
commit
dc0a59fcf2
4
api.http
4
api.http
@ -5,9 +5,9 @@ Authorization: set-auth-token
|
|||||||
|
|
||||||
{
|
{
|
||||||
"data": {
|
"data": {
|
||||||
"number": "KA01MU0556"
|
"number": "TN36BA5009"
|
||||||
},
|
},
|
||||||
"uniqueIdentifier": "KA01MU0556"
|
"uniqueIdentifier": "TN36BA5009"
|
||||||
}
|
}
|
||||||
|
|
||||||
### create row, with autogenerated identifier
|
### create row, with autogenerated identifier
|
||||||
|
|||||||
@ -6,7 +6,7 @@ plugins {
|
|||||||
application
|
application
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.readymixerp"
|
group = "com.basuvaraj"
|
||||||
version = "1.0-SNAPSHOT"
|
version = "1.0-SNAPSHOT"
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@ -29,6 +29,7 @@ dependencies {
|
|||||||
implementation("org.bitbucket.b_c:jose4j:0.9.3")
|
implementation("org.bitbucket.b_c:jose4j:0.9.3")
|
||||||
implementation("org.slf4j:slf4j-simple:2.0.7")
|
implementation("org.slf4j:slf4j-simple:2.0.7")
|
||||||
implementation("redis.clients:jedis:5.0.2")
|
implementation("redis.clients:jedis:5.0.2")
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-scripting-jsr223:1.9.0")
|
||||||
api ("net.cactusthorn.config:config-core:0.81")
|
api ("net.cactusthorn.config:config-core:0.81")
|
||||||
kapt("net.cactusthorn.config:config-compiler:0.81")
|
kapt("net.cactusthorn.config:config-compiler:0.81")
|
||||||
kapt("io.ebean:kotlin-querybean-generator:13.23.2")
|
kapt("io.ebean:kotlin-querybean-generator:13.23.2")
|
||||||
|
|||||||
@ -9,4 +9,4 @@ plugins {
|
|||||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
|
id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
|
||||||
}
|
}
|
||||||
|
|
||||||
rootProject.name = "rmc_modules_api"
|
rootProject.name = "rest_api"
|
||||||
15
src/main/kotlin/com/restapi/AppAccessManager.kt
Normal file
15
src/main/kotlin/com/restapi/AppAccessManager.kt
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.restapi
|
||||||
|
|
||||||
|
import io.javalin.http.Context
|
||||||
|
import io.javalin.http.Handler
|
||||||
|
import io.javalin.security.AccessManager
|
||||||
|
import io.javalin.security.RouteRole
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
class AppAccessManager : AccessManager {
|
||||||
|
private val logger = LoggerFactory.getLogger("Access")
|
||||||
|
override fun manage(handler: Handler, ctx: Context, routeRoles: Set<RouteRole>) {
|
||||||
|
logger.warn("access {}, {}", ctx.pathParamMap(), routeRoles)
|
||||||
|
handler.handle(ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -4,26 +4,24 @@ import AuthTokenResponse
|
|||||||
import com.fasterxml.jackson.databind.JsonMappingException
|
import com.fasterxml.jackson.databind.JsonMappingException
|
||||||
import com.fasterxml.jackson.module.kotlin.readValue
|
import com.fasterxml.jackson.module.kotlin.readValue
|
||||||
import com.restapi.config.AppConfig.Companion.appConfig
|
import com.restapi.config.AppConfig.Companion.appConfig
|
||||||
import com.restapi.config.Auth
|
|
||||||
import com.restapi.config.Auth.getAuthEndpoint
|
import com.restapi.config.Auth.getAuthEndpoint
|
||||||
import com.restapi.config.AuthEndpoint
|
import com.restapi.config.Auth.parseAuthToken
|
||||||
import com.restapi.domain.DataModel
|
import com.restapi.controllers.Entities
|
||||||
import com.restapi.domain.DataNotFoundException
|
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.database
|
||||||
import com.restapi.domain.Session.findByEntityAndId
|
|
||||||
import com.restapi.domain.Session.nextUniqId
|
|
||||||
import com.restapi.domain.Session.objectMapper
|
import com.restapi.domain.Session.objectMapper
|
||||||
import com.restapi.domain.Session.redis
|
import com.restapi.domain.Session.redis
|
||||||
import com.restapi.domain.Session.setAuthorizedUser
|
import com.restapi.domain.Session.setAuthorizedUser
|
||||||
import io.ebean.CallableSql
|
import io.ebean.CallableSql
|
||||||
import io.ebean.DuplicateKeyException
|
import io.ebean.DuplicateKeyException
|
||||||
import io.ebean.RawSqlBuilder
|
|
||||||
import io.javalin.Javalin
|
import io.javalin.Javalin
|
||||||
import io.javalin.apibuilder.ApiBuilder.*
|
import io.javalin.apibuilder.ApiBuilder.*
|
||||||
import io.javalin.http.*
|
import io.javalin.http.*
|
||||||
|
import io.javalin.http.util.NaiveRateLimit
|
||||||
|
import io.javalin.http.util.RateLimitUtil
|
||||||
import io.javalin.json.JavalinJackson
|
import io.javalin.json.JavalinJackson
|
||||||
|
import io.javalin.security.AccessManager
|
||||||
|
import io.javalin.security.RouteRole
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URLEncoder
|
import java.net.URLEncoder
|
||||||
@ -32,11 +30,14 @@ import java.net.http.HttpRequest
|
|||||||
import java.net.http.HttpRequest.BodyPublishers
|
import java.net.http.HttpRequest.BodyPublishers
|
||||||
import java.net.http.HttpResponse.BodyHandlers
|
import java.net.http.HttpResponse.BodyHandlers
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.time.LocalDateTime
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.jvm.optionals.getOrDefault
|
||||||
|
|
||||||
fun main(args: Array<String>) {
|
fun main(args: Array<String>) {
|
||||||
val logger = LoggerFactory.getLogger("api")
|
val logger = LoggerFactory.getLogger("api")
|
||||||
|
|
||||||
|
//ratelimit based on IP Only
|
||||||
|
RateLimitUtil.keyFunction = { ctx -> ctx.header("X-Forwarded-For")?.split(",")?.get(0) ?: ctx.ip() }
|
||||||
Javalin
|
Javalin
|
||||||
.create { cfg ->
|
.create { cfg ->
|
||||||
cfg.http.generateEtags = true
|
cfg.http.generateEtags = true
|
||||||
@ -52,7 +53,8 @@ fun main(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
cfg.http.defaultContentType = ContentType.JSON
|
cfg.http.defaultContentType = ContentType.JSON
|
||||||
cfg.compression.gzipOnly()
|
cfg.compression.gzipOnly()
|
||||||
cfg.jsonMapper(JavalinJackson(Session.objectMapper))
|
cfg.jsonMapper(JavalinJackson(objectMapper))
|
||||||
|
cfg.accessManager(AppAccessManager())
|
||||||
}
|
}
|
||||||
.routes {
|
.routes {
|
||||||
|
|
||||||
@ -95,110 +97,49 @@ fun main(args: Array<String>) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
before("/api/*") { ctx ->
|
before("/api/*") { ctx ->
|
||||||
//validate, auth token
|
//validate, auth token
|
||||||
|
|
||||||
|
NaiveRateLimit.requestPerTimeUnit(ctx, appConfig.rateLimit().getOrDefault(30), TimeUnit.MINUTES) // throws if rate limit is exceeded
|
||||||
|
|
||||||
//allow only alpha, numeric, hypen, underscore, dot in paths
|
//allow only alpha, numeric, hypen, underscore, dot in paths
|
||||||
val regex = Regex("^[a-zA-Z0-9\\-_\\.]+$")
|
val regex = Regex("^[a-zA-Z0-9\\-_\\.]+$")
|
||||||
|
|
||||||
ctx.path().split("/").dropWhile { it.isEmpty() }
|
ctx.path().split("/")
|
||||||
|
.dropWhile { it.isEmpty() }
|
||||||
.forEach {
|
.forEach {
|
||||||
if (!it.matches(regex)) {
|
if (!it.matches(regex)) {
|
||||||
throw IllegalArgumentException()
|
throw IllegalArgumentException()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val at = ctx.header("Authorization")?.replace("Bearer ", "")?.replace("Bearer: ", "")?.trim()
|
val authToken = ctx.header("Authorization")?.replace("Bearer ", "")
|
||||||
?: throw UnauthorizedResponse()
|
?.replace("Bearer: ", "")
|
||||||
val pt = Auth.parseAuthToken(authToken = at)
|
?.trim() ?: throw UnauthorizedResponse()
|
||||||
|
|
||||||
setAuthorizedUser(pt)
|
logger.warn("authToken = $authToken")
|
||||||
|
|
||||||
|
setAuthorizedUser(parseAuthToken(authToken = authToken))
|
||||||
}
|
}
|
||||||
path("/api") {
|
path("/api") {
|
||||||
post("/execute/{name}") {
|
post("/execute/{name}", Entities::executeStoredProcedure, Roles(Role.DbOps))
|
||||||
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()
|
get("/{entity}/{id}", Entities::view, Roles(Role.Standard(Action.VIEW)))
|
||||||
|
post("/{entity}/query/{id}", Entities::sqlQueryId, Roles(Role.Standard(Action.VIEW)))
|
||||||
|
post("/{entity}/query", Entities::sqlQueryRaw, Roles(Role.Standard(Action.VIEW)))
|
||||||
|
post("/{entity}", Entities::create, Roles(Role.Standard(Action.CREATE)))
|
||||||
|
|
||||||
it.json(
|
put("/{entity}/approve/{id}", Entities::approve, Roles(Role.Standard(Action.APPROVE)))
|
||||||
database.find(DataModel::class.java)
|
put("/{entity}/reject/{id}", Entities::reject, Roles(Role.Standard(Action.APPROVE)))
|
||||||
.setRawSql(
|
put("/{entity}/{action}/{id}", Entities::action, Roles(Role.Entity))
|
||||||
RawSqlBuilder.parse(querySql).create()
|
|
||||||
).apply {
|
|
||||||
sql.params.forEach { (t, u) ->
|
|
||||||
setParameter(t, u)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.findList()
|
|
||||||
)
|
|
||||||
|
|
||||||
}
|
put("/{entity}/{id}", Entities::update, Roles(Role.Standard(Action.UPDATE)))
|
||||||
post("/{entity}/query") {
|
patch("/{entity}/{id}", Entities::patch, Roles(Role.Standard(Action.UPDATE)))
|
||||||
val sql = it.bodyAsClass<Query>()
|
delete("/{entity}/{id}", Entities::delete, Roles(Role.Standard(Action.DELETE)))
|
||||||
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 ->
|
.exception(DuplicateKeyException::class.java) { _, ctx ->
|
||||||
ctx.json(
|
ctx.json(
|
||||||
@ -231,10 +172,19 @@ fun main(args: Array<String>) {
|
|||||||
.start(appConfig.portNumber())
|
.start(appConfig.portNumber())
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Query(
|
|
||||||
val sql: String,
|
enum class Action {
|
||||||
val params: Map<String, Any>
|
CREATE, VIEW, UPDATE, DELETE, APPROVE
|
||||||
)
|
}
|
||||||
|
|
||||||
|
sealed class Role {
|
||||||
|
open class Standard(vararg val action: Action) : Role()
|
||||||
|
data object Entity : Role()
|
||||||
|
data object DbOps : Role()
|
||||||
|
}
|
||||||
|
|
||||||
|
open class Roles(vararg val roles: Role) : RouteRole
|
||||||
|
|
||||||
|
|
||||||
private fun getFormDataAsString(formData: Map<String, String>): String {
|
private fun getFormDataAsString(formData: Map<String, String>): String {
|
||||||
val formBodyBuilder = StringBuilder()
|
val formBodyBuilder = StringBuilder()
|
||||||
@ -249,4 +199,3 @@ private fun getFormDataAsString(formData: Map<String, String>): String {
|
|||||||
return formBodyBuilder.toString()
|
return formBodyBuilder.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PatchValue(val key: String, val value: Any)
|
|
||||||
@ -63,6 +63,9 @@ interface AppConfig {
|
|||||||
@Key("app.cache.redis_uri")
|
@Key("app.cache.redis_uri")
|
||||||
fun redisUri(): Optional<String>
|
fun redisUri(): Optional<String>
|
||||||
|
|
||||||
|
@Key("app.network.rate_limit")
|
||||||
|
fun rateLimit(): Optional<Int>
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val appConfig: AppConfig = ConfigFactory.builder().build().create(AppConfig::class.java)
|
val appConfig: AppConfig = ConfigFactory.builder().build().create(AppConfig::class.java)
|
||||||
}
|
}
|
||||||
|
|||||||
119
src/main/kotlin/com/restapi/controllers/Entities.kt
Normal file
119
src/main/kotlin/com/restapi/controllers/Entities.kt
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
package com.restapi.controllers
|
||||||
|
|
||||||
|
import com.restapi.domain.DataModel
|
||||||
|
import com.restapi.domain.Session
|
||||||
|
import com.restapi.domain.Session.database
|
||||||
|
import com.restapi.domain.Session.findByEntityAndId
|
||||||
|
import io.ebean.CallableSql
|
||||||
|
import io.ebean.RawSqlBuilder
|
||||||
|
import io.javalin.http.Context
|
||||||
|
import io.javalin.http.NotFoundResponse
|
||||||
|
import io.javalin.http.bodyAsClass
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.time.LocalDateTime
|
||||||
|
|
||||||
|
data class PatchValue(val key: String, val value: Any)
|
||||||
|
data class Query(
|
||||||
|
val sql: String,
|
||||||
|
val params: Map<String, Any>
|
||||||
|
)
|
||||||
|
|
||||||
|
object Entities {
|
||||||
|
private val logger = LoggerFactory.getLogger("Entities")
|
||||||
|
fun delete(ctx: Context) {
|
||||||
|
val e = database.findByEntityAndId(ctx.pathParam("entity"), ctx.pathParam("id"))
|
||||||
|
e.deletedBy = Session.currentUser()
|
||||||
|
e.deletedOn = LocalDateTime.now()
|
||||||
|
e.update()
|
||||||
|
e.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun patch(ctx: Context) {
|
||||||
|
val e = database.findByEntityAndId(ctx.pathParam("entity"), ctx.pathParam("id"))
|
||||||
|
val pv = ctx.bodyAsClass<PatchValue>()
|
||||||
|
e.data[pv.key] = pv.value;
|
||||||
|
e.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun update(ctx: Context) {
|
||||||
|
val e = database.findByEntityAndId(ctx.pathParam("entity"), ctx.pathParam("id"))
|
||||||
|
val newData = ctx.bodyAsClass<Map<String, Any>>()
|
||||||
|
e.data.putAll(newData)
|
||||||
|
e.update()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun action(ctx: Context) {}
|
||||||
|
fun approve(ctx: Context) {}
|
||||||
|
fun reject(ctx: Context) {}
|
||||||
|
|
||||||
|
fun executeStoredProcedure(ctx: Context) {
|
||||||
|
val name = ctx.pathParam("name")
|
||||||
|
val params = ctx.bodyAsClass<Map<String, Any>>()
|
||||||
|
val placeholders = (0..params.entries.size + 1).joinToString(",") { "?" }
|
||||||
|
|
||||||
|
val sql = "{call $name($placeholders)}"
|
||||||
|
val cs: CallableSql = database.createCallableSql(sql)
|
||||||
|
|
||||||
|
params.entries.forEachIndexed { index, entry ->
|
||||||
|
cs.setParameter(index + 1, entry.value)
|
||||||
|
}
|
||||||
|
cs.setParameter(params.entries.size + 1, Session.currentTenant())
|
||||||
|
database.execute(cs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sqlQueryRaw(ctx: Context) {
|
||||||
|
val sql = ctx.bodyAsClass<Query>()
|
||||||
|
ctx.json(
|
||||||
|
database.find(DataModel::class.java)
|
||||||
|
.setRawSql(
|
||||||
|
RawSqlBuilder.parse(sql.sql).create()
|
||||||
|
).apply {
|
||||||
|
sql.params.forEach { (t, u) ->
|
||||||
|
setParameter(t, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.findList()
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun sqlQueryId(ctx: Context) {
|
||||||
|
val sql = ctx.bodyAsClass<Query>()
|
||||||
|
val query = database.findByEntityAndId(ctx.pathParam("entity"), ctx.pathParam("id"))
|
||||||
|
|
||||||
|
val querySql = query.data["sql"] as String? ?: throw NotFoundResponse()
|
||||||
|
|
||||||
|
ctx.json(
|
||||||
|
database.find(DataModel::class.java)
|
||||||
|
.setRawSql(
|
||||||
|
RawSqlBuilder.parse(querySql).create()
|
||||||
|
).apply {
|
||||||
|
sql.params.forEach { (t, u) ->
|
||||||
|
setParameter(t, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.findList()
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun view(it: Context) {
|
||||||
|
it.json(
|
||||||
|
database.findByEntityAndId(it.pathParam("entity"), it.pathParam("id"))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun create(ctx: Context) {
|
||||||
|
val entity = ctx.pathParam("entity")
|
||||||
|
val seqCreated = Session.creatSeq(entity)
|
||||||
|
logger.debug("sequence created for $entity? = $seqCreated")
|
||||||
|
database.save(
|
||||||
|
ctx.bodyAsClass<DataModel>().apply {
|
||||||
|
this.entityName = entity
|
||||||
|
if (this.uniqueIdentifier.isEmpty()) {
|
||||||
|
this.uniqueIdentifier = Session.nextUniqId(entity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -46,6 +46,7 @@ object Session {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun currentUser() = currentUser.get().userName
|
fun currentUser() = currentUser.get().userName
|
||||||
|
fun currentTenant() = currentUser.get().tenant
|
||||||
|
|
||||||
fun Database.findByEntityAndId(entity: String, id: String): DataModel {
|
fun Database.findByEntityAndId(entity: String, id: String): DataModel {
|
||||||
return find(DataModel::class.java)
|
return find(DataModel::class.java)
|
||||||
|
|||||||
@ -16,11 +16,7 @@ import io.ebean.annotation.WhenModified
|
|||||||
import io.ebean.annotation.WhoCreated
|
import io.ebean.annotation.WhoCreated
|
||||||
import io.ebean.annotation.WhoModified
|
import io.ebean.annotation.WhoModified
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import javax.persistence.Entity
|
import javax.persistence.*
|
||||||
import javax.persistence.GeneratedValue
|
|
||||||
import javax.persistence.Id
|
|
||||||
import javax.persistence.MappedSuperclass
|
|
||||||
import javax.persistence.Version
|
|
||||||
|
|
||||||
data class Comments(val text: String = "", val by: String = "", val at: LocalDateTime = LocalDateTime.now())
|
data class Comments(val text: String = "", val by: String = "", val at: LocalDateTime = LocalDateTime.now())
|
||||||
|
|
||||||
@ -62,6 +58,95 @@ abstract class BaseModel : Model() {
|
|||||||
var comments: MutableList<Comments> = arrayListOf()
|
var comments: MutableList<Comments> = arrayListOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
open class TenantModel : BaseModel() {
|
||||||
|
var name: String = ""
|
||||||
|
var domain: String = ""
|
||||||
|
var mobile: List<String> = emptyList()
|
||||||
|
var emails: List<String> = emptyList()
|
||||||
|
|
||||||
|
@DbJsonB
|
||||||
|
var preferences: MutableMap<String, Any> = hashMapOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class AuditType {
|
||||||
|
CREATE, UPDATE, DELETE, VIEW
|
||||||
|
}
|
||||||
|
|
||||||
|
open class AuditLog : BaseModel() {
|
||||||
|
var auditType: AuditType = AuditType.CREATE
|
||||||
|
var entity: String = ""
|
||||||
|
var uniqueIdentifier: String = ""
|
||||||
|
|
||||||
|
@DbJsonB
|
||||||
|
@Index(definition = "create index audit_log_values_idx on audit_log using GIN (data) ", platforms = [Platform.POSTGRES])
|
||||||
|
var data: Map<String, Any> = hashMapOf()
|
||||||
|
|
||||||
|
@DbJsonB
|
||||||
|
@Index(definition = "create index audit_log_changes_idx on audit_log using GIN (changes) ", platforms = [Platform.POSTGRES])
|
||||||
|
var changes: Map<String, Any> = hashMapOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
open class EntityModel : BaseModel() {
|
||||||
|
@Index(unique = true)
|
||||||
|
@JsonDeserialize(using = SafeStringDeserializer::class)
|
||||||
|
var name: String = ""
|
||||||
|
|
||||||
|
//a kts script that will return true/false along with errors before saving
|
||||||
|
var preSaveScript: String = ""
|
||||||
|
|
||||||
|
//a kts script that will do something ... returns void
|
||||||
|
var postSaveScript: String = ""
|
||||||
|
|
||||||
|
//this will create extra actions/roles in keycloak
|
||||||
|
//the default actions are create, update, view, delete
|
||||||
|
@DbArray
|
||||||
|
var actions: List<String> = emptyList()
|
||||||
|
|
||||||
|
//allow only these fields, if this is empty, then all fields are allowed
|
||||||
|
@DbArray
|
||||||
|
var allowedFields: List<String> = emptyList()
|
||||||
|
|
||||||
|
//when an entity is saved/updated audit logs will be populated, when this is empty, all fields are logged
|
||||||
|
@DbArray
|
||||||
|
var auditLogFields: List<String> = emptyList()
|
||||||
|
|
||||||
|
@DbJsonB
|
||||||
|
var preferences: MutableMap<String, Any> = hashMapOf()
|
||||||
|
|
||||||
|
//if '0' then its auto saved, no approval steps are required, for further steps,
|
||||||
|
//a user needs to have ROLE_ENTITY_APPROVE_LEVEL1, ROLE_ENTITY_APPROVE_LEVEL2 roles
|
||||||
|
var approvalLevels: Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class JobFrequencyType {
|
||||||
|
SPECIFIC, EVERY, CRON
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class JobType {
|
||||||
|
SCRIPT, DB
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
open class JobModel : BaseModel() {
|
||||||
|
@Index(unique = true)
|
||||||
|
var jobName: String = ""
|
||||||
|
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
var jobType: JobType = JobType.SCRIPT
|
||||||
|
var jobPath: String = ""
|
||||||
|
|
||||||
|
@DbArray
|
||||||
|
var tenants: List<String> = emptyList()
|
||||||
|
|
||||||
|
@Enumerated(EnumType.STRING)
|
||||||
|
var jobFrequencyType = JobFrequencyType.EVERY
|
||||||
|
var frequency: String = "1h"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Index(unique = true, name = "entity_unique_id", columnNames = ["entity_name", "unique_identifier", "tenant_id"])
|
@Index(unique = true, name = "entity_unique_id", columnNames = ["entity_name", "unique_identifier", "tenant_id"])
|
||||||
open class DataModel : BaseModel() {
|
open class DataModel : BaseModel() {
|
||||||
|
|||||||
10
src/main/kotlin/com/restapi/integ/Jobs.kt
Normal file
10
src/main/kotlin/com/restapi/integ/Jobs.kt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package com.restapi.integ
|
||||||
|
|
||||||
|
object Jobs {
|
||||||
|
fun runJobs(){
|
||||||
|
//wake up every minute
|
||||||
|
//see jobs that are to be run and run them
|
||||||
|
//not very accurate so use for simple jobs,
|
||||||
|
// sometimes they might fail and will not be attempted to re-run
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/main/kotlin/com/restapi/integ/Scripting.kt
Normal file
43
src/main/kotlin/com/restapi/integ/Scripting.kt
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package com.restapi.integ
|
||||||
|
|
||||||
|
import javax.script.Invocable
|
||||||
|
import javax.script.ScriptEngineManager
|
||||||
|
|
||||||
|
|
||||||
|
object Scripting {
|
||||||
|
const val a = "1"
|
||||||
|
@JvmStatic
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
k()
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun k(){
|
||||||
|
val engine = ScriptEngineManager().getEngineByExtension("kts")!!
|
||||||
|
val res1 = engine.eval("""
|
||||||
|
fun fn(x: Int) = x + 2
|
||||||
|
val obj = object {
|
||||||
|
fun fn1(x: Int) = x + 3
|
||||||
|
}
|
||||||
|
obj""".trimIndent())
|
||||||
|
println(res1)
|
||||||
|
|
||||||
|
val invocator = engine as? Invocable
|
||||||
|
println(invocator)
|
||||||
|
|
||||||
|
try {
|
||||||
|
println(invocator!!.invokeFunction("fn1", 3))
|
||||||
|
} catch (e: NoSuchMethodException) {
|
||||||
|
println(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
val res2 = invocator!!.invokeFunction("fn", 3)
|
||||||
|
println(res2)
|
||||||
|
val res3 = invocator.invokeMethod(res1, "fn1", 3)
|
||||||
|
println(res3)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user