more optional validations
This commit is contained in:
parent
ea14212337
commit
31388bae59
@ -1,22 +1,55 @@
|
||||
package com.restapi
|
||||
|
||||
import com.restapi.config.AppConfig.Companion.appConfig
|
||||
import com.restapi.domain.EntityModel
|
||||
import io.javalin.http.Context
|
||||
import io.javalin.http.Handler
|
||||
import io.javalin.http.HttpStatus
|
||||
import io.javalin.security.AccessManager
|
||||
import io.javalin.security.RouteRole
|
||||
import org.slf4j.LoggerFactory
|
||||
import com.restapi.domain.Session.currentRoles
|
||||
import com.restapi.domain.Session.database
|
||||
|
||||
class AppAccessManager : AccessManager {
|
||||
private val logger = LoggerFactory.getLogger("Access")
|
||||
private fun loadEntityActionRole(entity: String?, action: String?): List<String> {
|
||||
if (entity == null || action == null) return emptyList()
|
||||
|
||||
return database.find(EntityModel::class.java)
|
||||
.where()
|
||||
.eq("name", entity)
|
||||
.findOne()?.actions
|
||||
?.filter { it.equals(action, ignoreCase = true) }
|
||||
?.map { "role_${entity}_$it" } ?: emptyList()
|
||||
}
|
||||
|
||||
override fun manage(handler: Handler, ctx: Context, routeRoles: Set<RouteRole>) {
|
||||
logger.warn("access {}, {}", ctx.pathParamMap(), routeRoles)
|
||||
val pathParamMap = ctx.pathParamMap()
|
||||
logger.warn("access {}, {}", pathParamMap, routeRoles)
|
||||
val regex = Regex("^[a-zA-Z0-9\\-_\\.]+$")
|
||||
|
||||
if(ctx.pathParamMap().values.count { !regex.matches(it) } > 0){
|
||||
ctx.status(HttpStatus.FORBIDDEN).result("invalid request")
|
||||
if (pathParamMap.values.count { !regex.matches(it) } > 0) {
|
||||
ctx.status(HttpStatus.FORBIDDEN).result("invalid request")
|
||||
} else {
|
||||
handler.handle(ctx)
|
||||
val entity = pathParamMap["entity"]
|
||||
val action = pathParamMap["action"]
|
||||
|
||||
val allowedRoles = routeRoles.map { it as Role }.flatMap {
|
||||
when (it) {
|
||||
Role.DbOps -> listOf("ROLE_DB_OPS")
|
||||
Role.Entity -> loadEntityActionRole(entity, action)
|
||||
is Role.Standard -> listOf("ROLE_${entity}_${it.action}")
|
||||
}.map(String::uppercase)
|
||||
}
|
||||
|
||||
val isAllowed = currentRoles().count { allowedRoles.contains(it) } > 0
|
||||
if (isAllowed || !appConfig.enforceRoleRestriction() || allowedRoles.isEmpty()) {
|
||||
//if role is allowed, or enforcement is turned off or no roles are explicitly allowed
|
||||
handler.handle(ctx)
|
||||
} else {
|
||||
ctx.status(HttpStatus.UNAUTHORIZED).result("unauthorized request")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,11 +8,9 @@ import com.restapi.config.Auth.getAuthEndpoint
|
||||
import com.restapi.config.Auth.parseAuthToken
|
||||
import com.restapi.controllers.Entities
|
||||
import com.restapi.domain.DataNotFoundException
|
||||
import com.restapi.domain.Session.database
|
||||
import com.restapi.domain.Session.objectMapper
|
||||
import com.restapi.domain.Session.redis
|
||||
import com.restapi.domain.Session.setAuthorizedUser
|
||||
import io.ebean.CallableSql
|
||||
import io.ebean.DuplicateKeyException
|
||||
import io.javalin.Javalin
|
||||
import io.javalin.apibuilder.ApiBuilder.*
|
||||
@ -20,7 +18,6 @@ import io.javalin.http.*
|
||||
import io.javalin.http.util.NaiveRateLimit
|
||||
import io.javalin.http.util.RateLimitUtil
|
||||
import io.javalin.json.JavalinJackson
|
||||
import io.javalin.security.AccessManager
|
||||
import io.javalin.security.RouteRole
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.net.URI
|
||||
@ -99,11 +96,15 @@ fun main(args: Array<String>) {
|
||||
}
|
||||
|
||||
before("/api/*") { ctx ->
|
||||
//validate, auth token
|
||||
|
||||
NaiveRateLimit.requestPerTimeUnit(ctx, appConfig.rateLimit().getOrDefault(30), TimeUnit.MINUTES) // throws if rate limit is exceeded
|
||||
NaiveRateLimit.requestPerTimeUnit(
|
||||
ctx,
|
||||
appConfig.rateLimit().getOrDefault(30),
|
||||
TimeUnit.MINUTES
|
||||
)
|
||||
|
||||
val authToken = ctx.header("Authorization")?.replace("Bearer ", "")
|
||||
val authToken = ctx.header("Authorization")
|
||||
?.replace("Bearer ", "")
|
||||
?.replace("Bearer: ", "")
|
||||
?.trim() ?: throw UnauthorizedResponse()
|
||||
|
||||
@ -111,21 +112,29 @@ fun main(args: Array<String>) {
|
||||
|
||||
setAuthorizedUser(parseAuthToken(authToken = authToken))
|
||||
}
|
||||
|
||||
val adminRole = Role.Standard(Action.ADMIN)
|
||||
val viewRole = Role.Standard(Action.VIEW)
|
||||
val createRole = Role.Standard(Action.CREATE)
|
||||
val updateRole = Role.Standard(Action.UPDATE)
|
||||
val approveOrRejectRole = Role.Standard(Action.APPROVE)
|
||||
|
||||
path("/api") {
|
||||
post("/execute/{name}", Entities::executeStoredProcedure, Roles(Role.DbOps))
|
||||
post("/execute/{name}", Entities::executeStoredProcedure, Roles(adminRole, Role.DbOps))
|
||||
post("/script/{name}", Entities::executeScript, Roles(adminRole, Role.DbOps))
|
||||
|
||||
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)))
|
||||
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}", Entities::create, Roles(adminRole, createRole))
|
||||
|
||||
put("/{entity}/approve/{id}", Entities::approve, Roles(Role.Standard(Action.APPROVE)))
|
||||
put("/{entity}/reject/{id}", Entities::reject, Roles(Role.Standard(Action.APPROVE)))
|
||||
put("/{entity}/{action}/{id}", Entities::action, Roles(Role.Entity))
|
||||
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(Role.Standard(Action.UPDATE)))
|
||||
patch("/{entity}/{id}", Entities::patch, Roles(Role.Standard(Action.UPDATE)))
|
||||
delete("/{entity}/{id}", Entities::delete, Roles(Role.Standard(Action.DELETE)))
|
||||
put("/{entity}/{id}", Entities::update, Roles(adminRole, updateRole))
|
||||
patch("/{entity}/{id}", Entities::patch, Roles(adminRole, updateRole))
|
||||
delete("/{entity}/{id}", Entities::delete, Roles(adminRole, Role.Standard(Action.DELETE)))
|
||||
}
|
||||
|
||||
|
||||
@ -163,7 +172,7 @@ fun main(args: Array<String>) {
|
||||
|
||||
|
||||
enum class Action {
|
||||
CREATE, VIEW, UPDATE, DELETE, APPROVE
|
||||
CREATE, VIEW, UPDATE, DELETE, APPROVE, ADMIN
|
||||
}
|
||||
|
||||
sealed class Role {
|
||||
|
||||
@ -66,6 +66,13 @@ interface AppConfig {
|
||||
@Key("app.network.rate_limit")
|
||||
fun rateLimit(): Optional<Int>
|
||||
|
||||
@Key("app.security.enforce_role_restriction")
|
||||
@Default("true")
|
||||
fun enforceRoleRestriction(): Boolean
|
||||
|
||||
@Key("app.scripts.path")
|
||||
fun scriptsPath(): String
|
||||
|
||||
companion object {
|
||||
val appConfig: AppConfig = ConfigFactory.builder().build().create(AppConfig::class.java)
|
||||
}
|
||||
|
||||
@ -1,16 +1,23 @@
|
||||
package com.restapi.controllers
|
||||
|
||||
import com.restapi.domain.ApprovalStatus
|
||||
import com.restapi.domain.DataModel
|
||||
import com.restapi.domain.EntityModel
|
||||
import com.restapi.domain.Session
|
||||
import com.restapi.domain.Session.database
|
||||
import com.restapi.domain.Session.findByEntityAndId
|
||||
import com.restapi.integ.Scripting
|
||||
import io.ebean.CallableSql
|
||||
import io.ebean.RawSqlBuilder
|
||||
import io.javalin.http.BadRequestResponse
|
||||
import io.javalin.http.Context
|
||||
import io.javalin.http.NotFoundResponse
|
||||
import io.javalin.http.bodyAsClass
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalDateTime
|
||||
import java.time.LocalTime
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
data class PatchValue(val key: String, val value: Any)
|
||||
data class Query(
|
||||
@ -46,6 +53,14 @@ object Entities {
|
||||
fun approve(ctx: Context) {}
|
||||
fun reject(ctx: Context) {}
|
||||
|
||||
fun executeScript(ctx: Context) {
|
||||
val name = ctx.pathParam("name")
|
||||
val params = ctx.bodyAsClass<Map<String, Any>>()
|
||||
ctx.json(
|
||||
Scripting.execute(name, params)
|
||||
)
|
||||
}
|
||||
|
||||
fun executeStoredProcedure(ctx: Context) {
|
||||
val name = ctx.pathParam("name")
|
||||
val params = ctx.bodyAsClass<Map<String, Any>>()
|
||||
@ -58,7 +73,12 @@ object Entities {
|
||||
cs.setParameter(index + 1, entry.value)
|
||||
}
|
||||
cs.setParameter(params.entries.size + 1, Session.currentTenant())
|
||||
database.execute(cs)
|
||||
val done = database.execute(cs)
|
||||
ctx.json(
|
||||
mapOf(
|
||||
"done" to done
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun sqlQueryRaw(ctx: Context) {
|
||||
@ -77,7 +97,7 @@ object Entities {
|
||||
|
||||
}
|
||||
|
||||
fun sqlQueryId(ctx: Context) {
|
||||
fun sqlQueryById(ctx: Context) {
|
||||
val sql = ctx.bodyAsClass<Query>()
|
||||
val query = database.findByEntityAndId(ctx.pathParam("entity"), ctx.pathParam("id"))
|
||||
|
||||
@ -104,16 +124,112 @@ object Entities {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
//may be approval flow is configured?
|
||||
val setupEntity = database.find(EntityModel::class.java)
|
||||
.where()
|
||||
.eq("name", entity)
|
||||
.findOne()
|
||||
|
||||
Session.creatSeq(entity)
|
||||
val dataModel = ctx.bodyAsClass<DataModel>().apply {
|
||||
this.entityName = entity
|
||||
if (this.uniqueIdentifier.isEmpty()) {
|
||||
this.uniqueIdentifier = Session.nextUniqId(entity)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
database.save(
|
||||
dataModel.apply {
|
||||
if (setupEntity != null) {
|
||||
|
||||
val allowedFields = setupEntity.allowedFields.map { it.lowercase() }
|
||||
if (allowedFields.isNotEmpty()) {
|
||||
|
||||
val moreFields =
|
||||
dataModel.data.keys.map { it.lowercase() }.filter { !allowedFields.contains(it) }
|
||||
|
||||
if (moreFields.isNotEmpty()) {
|
||||
logger.warn("Data Keys = ${dataModel.data.keys} is more than $allowedFields, extra fields = $moreFields")
|
||||
throw BadRequestResponse("data contains more fields than allowed")
|
||||
}
|
||||
|
||||
setupEntity.allowedFieldTypes.forEach { (key, expectedType) ->
|
||||
|
||||
val valueFromUser = dataModel.data[key] ?: return@forEach
|
||||
val isDate = expectedType.equals("date", ignoreCase = true)
|
||||
val isDateTime = expectedType.equals("datetime", ignoreCase = true)
|
||||
val isTime = expectedType.equals("time", ignoreCase = true)
|
||||
if (isDate || isDateTime || isTime) {
|
||||
//this should be a string of a particular format
|
||||
if (valueFromUser !is String) {
|
||||
throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType")
|
||||
} else {
|
||||
val dtPattern = Regex("^\\d{4}-\\d{2}-\\d{2}$")
|
||||
val dtmPattern = Regex("^\\d{4}-\\d{2}-\\d{2}\\s\\d{2}:\\d{2}$")
|
||||
val timePattern = Regex("^\\d{2}:\\d{2}$")
|
||||
|
||||
|
||||
if (isDate
|
||||
&& !dtPattern.matches(valueFromUser)
|
||||
&& !isValidDate(valueFromUser)
|
||||
) {
|
||||
throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType")
|
||||
}
|
||||
|
||||
|
||||
if (isDateTime
|
||||
&& !dtmPattern.matches(valueFromUser)
|
||||
&& !isValidDateTime(valueFromUser)
|
||||
) {
|
||||
throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType")
|
||||
}
|
||||
|
||||
if (isTime
|
||||
&& !timePattern.matches(valueFromUser)
|
||||
&& !isValidTime(valueFromUser)
|
||||
) {
|
||||
throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (valueFromUser.javaClass.simpleName != expectedType) {
|
||||
throw BadRequestResponse("field $key, is of type ${valueFromUser.javaClass.simpleName} expected $expectedType")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (setupEntity.approvalLevels > 0) {
|
||||
|
||||
this.approvalStatus = ApprovalStatus.PENDING
|
||||
this.requiredApprovalLevels = setupEntity.approvalLevels
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun isValidDate(f: String) = try {
|
||||
LocalDate.parse(f, DateTimeFormatter.ofPattern("yyyy-MM-dd"))
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
private fun isValidDateTime(f: String) = try {
|
||||
LocalDateTime.parse(f, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
private fun isValidTime(f: String) = try {
|
||||
LocalTime.parse(f, DateTimeFormatter.ofPattern("HH:mm"))
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
@ -47,6 +47,7 @@ object Session {
|
||||
|
||||
fun currentUser() = currentUser.get().userName
|
||||
fun currentTenant() = currentUser.get().tenant
|
||||
fun currentRoles() = currentUser.get().roles
|
||||
|
||||
fun Database.findByEntityAndId(entity: String, id: String): DataModel {
|
||||
return find(DataModel::class.java)
|
||||
|
||||
@ -20,6 +20,10 @@ import javax.persistence.*
|
||||
|
||||
data class Comments(val text: String = "", val by: String = "", val at: LocalDateTime = LocalDateTime.now())
|
||||
|
||||
enum class ApprovalStatus {
|
||||
PENDING, APPROVED, REJECTED
|
||||
}
|
||||
|
||||
@MappedSuperclass
|
||||
abstract class BaseModel : Model() {
|
||||
@Id
|
||||
@ -51,6 +55,13 @@ abstract class BaseModel : Model() {
|
||||
|
||||
var deletedBy: String? = null
|
||||
|
||||
var currentApprovalLevel: Int = 0
|
||||
|
||||
var requiredApprovalLevels: Int = 0
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
var approvalStatus: ApprovalStatus = ApprovalStatus.PENDING
|
||||
|
||||
@DbArray
|
||||
var tags: MutableList<String> = arrayListOf()
|
||||
|
||||
@ -70,11 +81,15 @@ open class TenantModel : BaseModel() {
|
||||
}
|
||||
|
||||
enum class AuditType {
|
||||
CREATE, UPDATE, DELETE, VIEW
|
||||
CREATE, UPDATE, DELETE, VIEW, APPROVE, REJECT
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Index(columnNames = ["audit_type", "entity", "unique_identifier", "tenant_id", "created_by"])
|
||||
open class AuditLog : BaseModel() {
|
||||
@Enumerated(EnumType.STRING)
|
||||
var auditType: AuditType = AuditType.CREATE
|
||||
|
||||
var entity: String = ""
|
||||
var uniqueIdentifier: String = ""
|
||||
|
||||
@ -108,6 +123,10 @@ open class EntityModel : BaseModel() {
|
||||
@DbArray
|
||||
var allowedFields: List<String> = emptyList()
|
||||
|
||||
//enforce field types, if this is present, only fields that are present is validated
|
||||
@DbJsonB
|
||||
var allowedFieldTypes: Map<String, String> = hashMapOf()
|
||||
|
||||
//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()
|
||||
@ -116,7 +135,7 @@ open class EntityModel : BaseModel() {
|
||||
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
|
||||
//a user needs to have ROLE_ENTITY_APPROVE_LEVEL1, ROLE_ENTITY_APPROVE_LEVEL2 roles for further approvals
|
||||
var approvalLevels: Int = 0
|
||||
}
|
||||
|
||||
|
||||
@ -1,43 +1,17 @@
|
||||
package com.restapi.integ
|
||||
|
||||
import com.restapi.config.AppConfig.Companion.appConfig
|
||||
import java.io.File
|
||||
import javax.script.Invocable
|
||||
import javax.script.ScriptEngineManager
|
||||
|
||||
|
||||
object Scripting {
|
||||
const val a = "1"
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) {
|
||||
|
||||
|
||||
|
||||
k()
|
||||
|
||||
|
||||
}
|
||||
|
||||
fun k(){
|
||||
fun execute(name: String, params: Map<String, Any>): Any {
|
||||
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)
|
||||
engine.eval(File(appConfig.scriptsPath(), "$name.kts").reader())
|
||||
val invocator = engine as Invocable
|
||||
return invocator.invokeFunction("execute", params)
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,42 @@
|
||||
-- apply changes
|
||||
create table data_model (
|
||||
create table audit_log (
|
||||
sys_pk bigint generated by default as identity not null,
|
||||
deleted_on timestamp,
|
||||
current_approval_level integer not null,
|
||||
required_approval_levels integer not null,
|
||||
deleted boolean default false not null,
|
||||
version integer not null,
|
||||
created_at timestamp not null,
|
||||
modified_at timestamp not null,
|
||||
tenant_id varchar(255) not null,
|
||||
deleted_by varchar(255),
|
||||
approval_status varchar(8) not null,
|
||||
tags varchar[] not null,
|
||||
comments jsonb not null,
|
||||
audit_type varchar(7) not null,
|
||||
entity varchar(255) not null,
|
||||
unique_identifier varchar(255) not null,
|
||||
data jsonb not null,
|
||||
changes jsonb not null,
|
||||
created_by varchar(255) not null,
|
||||
modified_by varchar(255) not null,
|
||||
constraint ck_audit_log_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
|
||||
constraint ck_audit_log_audit_type check ( audit_type in ('CREATE','UPDATE','DELETE','VIEW','APPROVE','REJECT')),
|
||||
constraint pk_audit_log primary key (sys_pk)
|
||||
);
|
||||
|
||||
create table data_model (
|
||||
sys_pk bigint generated by default as identity not null,
|
||||
deleted_on timestamp,
|
||||
current_approval_level integer not null,
|
||||
required_approval_levels integer not null,
|
||||
deleted boolean default false not null,
|
||||
version integer not null,
|
||||
created_at timestamp not null,
|
||||
modified_at timestamp not null,
|
||||
tenant_id varchar(255) not null,
|
||||
deleted_by varchar(255),
|
||||
approval_status varchar(8) not null,
|
||||
tags varchar[] not null,
|
||||
comments jsonb not null,
|
||||
unique_identifier varchar(255) not null,
|
||||
@ -15,9 +44,95 @@ create table data_model (
|
||||
data jsonb not null,
|
||||
created_by varchar(255) not null,
|
||||
modified_by varchar(255) not null,
|
||||
constraint ck_data_model_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
|
||||
constraint entity_unique_id unique (entity_name,unique_identifier,tenant_id),
|
||||
constraint pk_data_model primary key (sys_pk)
|
||||
);
|
||||
|
||||
create table entity_model (
|
||||
sys_pk bigint generated by default as identity not null,
|
||||
deleted_on timestamp,
|
||||
current_approval_level integer not null,
|
||||
required_approval_levels integer not null,
|
||||
approval_levels integer not null,
|
||||
deleted boolean default false not null,
|
||||
version integer not null,
|
||||
created_at timestamp not null,
|
||||
modified_at timestamp not null,
|
||||
tenant_id varchar(255) not null,
|
||||
deleted_by varchar(255),
|
||||
approval_status varchar(8) not null,
|
||||
tags varchar[] not null,
|
||||
comments jsonb not null,
|
||||
name varchar(255) not null,
|
||||
pre_save_script varchar(255) not null,
|
||||
post_save_script varchar(255) not null,
|
||||
actions varchar[] not null,
|
||||
allowed_fields varchar[] not null,
|
||||
allowed_field_types jsonb not null,
|
||||
audit_log_fields varchar[] not null,
|
||||
preferences jsonb not null,
|
||||
created_by varchar(255) not null,
|
||||
modified_by varchar(255) not null,
|
||||
constraint ck_entity_model_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
|
||||
constraint uq_entity_model_name unique (name),
|
||||
constraint pk_entity_model primary key (sys_pk)
|
||||
);
|
||||
|
||||
create table job_model (
|
||||
sys_pk bigint generated by default as identity not null,
|
||||
deleted_on timestamp,
|
||||
current_approval_level integer not null,
|
||||
required_approval_levels integer not null,
|
||||
deleted boolean default false not null,
|
||||
version integer not null,
|
||||
created_at timestamp not null,
|
||||
modified_at timestamp not null,
|
||||
tenant_id varchar(255) not null,
|
||||
deleted_by varchar(255),
|
||||
approval_status varchar(8) not null,
|
||||
tags varchar[] not null,
|
||||
comments jsonb not null,
|
||||
job_name varchar(255) not null,
|
||||
job_type varchar(6) not null,
|
||||
job_path varchar(255) not null,
|
||||
tenants varchar[] not null,
|
||||
job_frequency_type varchar(8) not null,
|
||||
frequency varchar(255) not null,
|
||||
created_by varchar(255) not null,
|
||||
modified_by varchar(255) not null,
|
||||
constraint ck_job_model_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
|
||||
constraint ck_job_model_job_type check ( job_type in ('SCRIPT','DB')),
|
||||
constraint ck_job_model_job_frequency_type check ( job_frequency_type in ('SPECIFIC','EVERY','CRON')),
|
||||
constraint uq_job_model_job_name unique (job_name),
|
||||
constraint pk_job_model primary key (sys_pk)
|
||||
);
|
||||
|
||||
create table tenant_model (
|
||||
sys_pk bigint generated by default as identity not null,
|
||||
deleted_on timestamp,
|
||||
current_approval_level integer not null,
|
||||
required_approval_levels integer not null,
|
||||
deleted boolean default false not null,
|
||||
version integer not null,
|
||||
created_at timestamp not null,
|
||||
modified_at timestamp not null,
|
||||
tenant_id varchar(255) not null,
|
||||
deleted_by varchar(255),
|
||||
approval_status varchar(8) not null,
|
||||
tags varchar[] not null,
|
||||
comments jsonb not null,
|
||||
name varchar(255) not null,
|
||||
domain varchar(255) not null,
|
||||
preferences jsonb not null,
|
||||
created_by varchar(255) not null,
|
||||
modified_by varchar(255) not null,
|
||||
constraint ck_tenant_model_approval_status check ( approval_status in ('PENDING','APPROVED','REJECTED')),
|
||||
constraint pk_tenant_model primary key (sys_pk)
|
||||
);
|
||||
|
||||
-- foreign keys and indices
|
||||
create index if not exists ix_audit_log_audit_type_entity_unique_identifier_tenant_i_1 on audit_log (audit_type,entity,unique_identifier,tenant_id,created_by);
|
||||
create index audit_log_values_idx on audit_log using GIN (data) ;
|
||||
create index audit_log_changes_idx on audit_log using GIN (changes) ;
|
||||
create index data_jsonb_idx on data_model using GIN (data) ;
|
||||
|
||||
@ -1,11 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<migration xmlns="http://ebean-orm.github.io/xml/ns/dbmigration">
|
||||
<changeSet type="apply">
|
||||
<createTable name="audit_log" pkName="pk_audit_log">
|
||||
<column name="sys_pk" type="bigint" primaryKey="true"/>
|
||||
<column name="tenant_id" type="varchar" notnull="true"/>
|
||||
<column name="deleted_on" type="localdatetime"/>
|
||||
<column name="deleted_by" type="varchar"/>
|
||||
<column name="current_approval_level" type="integer" notnull="true"/>
|
||||
<column name="required_approval_levels" type="integer" notnull="true"/>
|
||||
<column name="approval_status" type="varchar(8)" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_audit_log_approval_status"/>
|
||||
<column name="tags" type="varchar[]" notnull="true"/>
|
||||
<column name="comments" type="jsonb" notnull="true"/>
|
||||
<column name="audit_type" type="varchar(7)" notnull="true" checkConstraint="check ( audit_type in ('CREATE','UPDATE','DELETE','VIEW','APPROVE','REJECT'))" checkConstraintName="ck_audit_log_audit_type"/>
|
||||
<column name="entity" type="varchar" notnull="true"/>
|
||||
<column name="unique_identifier" type="varchar" notnull="true"/>
|
||||
<column name="data" type="jsonb" notnull="true"/>
|
||||
<column name="changes" type="jsonb" notnull="true"/>
|
||||
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
|
||||
<column name="version" type="integer" notnull="true"/>
|
||||
<column name="created_at" type="localdatetime" notnull="true"/>
|
||||
<column name="modified_at" type="localdatetime" notnull="true"/>
|
||||
<column name="created_by" type="varchar" notnull="true"/>
|
||||
<column name="modified_by" type="varchar" notnull="true"/>
|
||||
</createTable>
|
||||
<createTable name="data_model" pkName="pk_data_model">
|
||||
<column name="sys_pk" type="bigint" primaryKey="true"/>
|
||||
<column name="tenant_id" type="varchar" notnull="true"/>
|
||||
<column name="deleted_on" type="localdatetime"/>
|
||||
<column name="deleted_by" type="varchar"/>
|
||||
<column name="current_approval_level" type="integer" notnull="true"/>
|
||||
<column name="required_approval_levels" type="integer" notnull="true"/>
|
||||
<column name="approval_status" type="varchar(8)" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_data_model_approval_status"/>
|
||||
<column name="tags" type="varchar[]" notnull="true"/>
|
||||
<column name="comments" type="jsonb" notnull="true"/>
|
||||
<column name="unique_identifier" type="varchar" notnull="true"/>
|
||||
@ -19,6 +44,80 @@
|
||||
<column name="modified_by" type="varchar" notnull="true"/>
|
||||
<uniqueConstraint name="entity_unique_id" columnNames="entity_name,unique_identifier,tenant_id" oneToOne="false" nullableColumns=""/>
|
||||
</createTable>
|
||||
<createTable name="entity_model" pkName="pk_entity_model">
|
||||
<column name="sys_pk" type="bigint" primaryKey="true"/>
|
||||
<column name="tenant_id" type="varchar" notnull="true"/>
|
||||
<column name="deleted_on" type="localdatetime"/>
|
||||
<column name="deleted_by" type="varchar"/>
|
||||
<column name="current_approval_level" type="integer" notnull="true"/>
|
||||
<column name="required_approval_levels" type="integer" notnull="true"/>
|
||||
<column name="approval_status" type="varchar(8)" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_entity_model_approval_status"/>
|
||||
<column name="tags" type="varchar[]" notnull="true"/>
|
||||
<column name="comments" type="jsonb" notnull="true"/>
|
||||
<column name="name" type="varchar" notnull="true"/>
|
||||
<column name="pre_save_script" type="varchar" notnull="true"/>
|
||||
<column name="post_save_script" type="varchar" notnull="true"/>
|
||||
<column name="actions" type="varchar[]" notnull="true"/>
|
||||
<column name="allowed_fields" type="varchar[]" notnull="true"/>
|
||||
<column name="allowed_field_types" type="jsonb" notnull="true"/>
|
||||
<column name="audit_log_fields" type="varchar[]" notnull="true"/>
|
||||
<column name="preferences" type="jsonb" notnull="true"/>
|
||||
<column name="approval_levels" type="integer" notnull="true"/>
|
||||
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
|
||||
<column name="version" type="integer" notnull="true"/>
|
||||
<column name="created_at" type="localdatetime" notnull="true"/>
|
||||
<column name="modified_at" type="localdatetime" notnull="true"/>
|
||||
<column name="created_by" type="varchar" notnull="true"/>
|
||||
<column name="modified_by" type="varchar" notnull="true"/>
|
||||
<uniqueConstraint name="uq_entity_model_name" columnNames="name" oneToOne="false" nullableColumns=""/>
|
||||
</createTable>
|
||||
<createTable name="job_model" pkName="pk_job_model">
|
||||
<column name="sys_pk" type="bigint" primaryKey="true"/>
|
||||
<column name="tenant_id" type="varchar" notnull="true"/>
|
||||
<column name="deleted_on" type="localdatetime"/>
|
||||
<column name="deleted_by" type="varchar"/>
|
||||
<column name="current_approval_level" type="integer" notnull="true"/>
|
||||
<column name="required_approval_levels" type="integer" notnull="true"/>
|
||||
<column name="approval_status" type="varchar(8)" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_job_model_approval_status"/>
|
||||
<column name="tags" type="varchar[]" notnull="true"/>
|
||||
<column name="comments" type="jsonb" notnull="true"/>
|
||||
<column name="job_name" type="varchar" notnull="true"/>
|
||||
<column name="job_type" type="varchar(6)" notnull="true" checkConstraint="check ( job_type in ('SCRIPT','DB'))" checkConstraintName="ck_job_model_job_type"/>
|
||||
<column name="job_path" type="varchar" notnull="true"/>
|
||||
<column name="tenants" type="varchar[]" notnull="true"/>
|
||||
<column name="job_frequency_type" type="varchar(8)" notnull="true" checkConstraint="check ( job_frequency_type in ('SPECIFIC','EVERY','CRON'))" checkConstraintName="ck_job_model_job_frequency_type"/>
|
||||
<column name="frequency" type="varchar" notnull="true"/>
|
||||
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
|
||||
<column name="version" type="integer" notnull="true"/>
|
||||
<column name="created_at" type="localdatetime" notnull="true"/>
|
||||
<column name="modified_at" type="localdatetime" notnull="true"/>
|
||||
<column name="created_by" type="varchar" notnull="true"/>
|
||||
<column name="modified_by" type="varchar" notnull="true"/>
|
||||
<uniqueConstraint name="uq_job_model_job_name" columnNames="job_name" oneToOne="false" nullableColumns=""/>
|
||||
</createTable>
|
||||
<createTable name="tenant_model" pkName="pk_tenant_model">
|
||||
<column name="sys_pk" type="bigint" primaryKey="true"/>
|
||||
<column name="tenant_id" type="varchar" notnull="true"/>
|
||||
<column name="deleted_on" type="localdatetime"/>
|
||||
<column name="deleted_by" type="varchar"/>
|
||||
<column name="current_approval_level" type="integer" notnull="true"/>
|
||||
<column name="required_approval_levels" type="integer" notnull="true"/>
|
||||
<column name="approval_status" type="varchar(8)" notnull="true" checkConstraint="check ( approval_status in ('PENDING','APPROVED','REJECTED'))" checkConstraintName="ck_tenant_model_approval_status"/>
|
||||
<column name="tags" type="varchar[]" notnull="true"/>
|
||||
<column name="comments" type="jsonb" notnull="true"/>
|
||||
<column name="name" type="varchar" notnull="true"/>
|
||||
<column name="domain" type="varchar" notnull="true"/>
|
||||
<column name="preferences" type="jsonb" notnull="true"/>
|
||||
<column name="deleted" type="boolean" defaultValue="false" notnull="true"/>
|
||||
<column name="version" type="integer" notnull="true"/>
|
||||
<column name="created_at" type="localdatetime" notnull="true"/>
|
||||
<column name="modified_at" type="localdatetime" notnull="true"/>
|
||||
<column name="created_by" type="varchar" notnull="true"/>
|
||||
<column name="modified_by" type="varchar" notnull="true"/>
|
||||
</createTable>
|
||||
<createIndex indexName="ix_audit_log_audit_type_entity_unique_identifier_tenant_i_1" tableName="audit_log" columns="audit_type,entity,unique_identifier,tenant_id,created_by"/>
|
||||
<createIndex indexName="ix_audit_log_data" tableName="audit_log" columns="data" definition="create index audit_log_values_idx on audit_log using GIN (data) " platforms="POSTGRES"/>
|
||||
<createIndex indexName="ix_audit_log_changes" tableName="audit_log" columns="changes" definition="create index audit_log_changes_idx on audit_log using GIN (changes) " platforms="POSTGRES"/>
|
||||
<createIndex indexName="ix_data_model_data" tableName="data_model" columns="data" definition="create index data_jsonb_idx on data_model using GIN (data) " platforms="POSTGRES"/>
|
||||
</changeSet>
|
||||
</migration>
|
||||
Loading…
x
Reference in New Issue
Block a user