From 01e197b0f8305d1f58a2acda5ee6ba984fa1e032 Mon Sep 17 00:00:00 2001 From: "gowthaman.b" Date: Fri, 10 Nov 2023 11:06:14 +0530 Subject: [PATCH] simple api model --- .idea/misc.xml | 2 +- api.http | 21 ++- app-sample.properties | 7 + build.gradle.kts | 5 +- src/main/kotlin/com/readymixerp/Main.kt | 116 ------------ src/main/kotlin/com/readymixerp/domain/db.kt | 39 ---- src/main/kotlin/com/restapi/Main.kt | 177 ++++++++++++++++++ .../kotlin/com/restapi/config/AppConfig.kt | 48 +++++ src/main/kotlin/com/restapi/domain/db.kt | 75 ++++++++ .../domain/migration.kt | 4 +- .../{readymixerp => restapi}/domain/models.kt | 42 ++++- .../resources/dbmigration/1.0__initial.sql | 32 ---- .../dbmigration/model/1.0__initial.model.xml | 22 --- src/main/resources/ebean.mf | 6 +- 14 files changed, 364 insertions(+), 232 deletions(-) create mode 100644 app-sample.properties delete mode 100644 src/main/kotlin/com/readymixerp/Main.kt delete mode 100644 src/main/kotlin/com/readymixerp/domain/db.kt create mode 100644 src/main/kotlin/com/restapi/Main.kt create mode 100644 src/main/kotlin/com/restapi/config/AppConfig.kt create mode 100644 src/main/kotlin/com/restapi/domain/db.kt rename src/main/kotlin/com/{readymixerp => restapi}/domain/migration.kt (86%) rename src/main/kotlin/com/{readymixerp => restapi}/domain/models.kt (54%) delete mode 100644 src/main/resources/dbmigration/1.0__initial.sql delete mode 100644 src/main/resources/dbmigration/model/1.0__initial.model.xml diff --git a/.idea/misc.xml b/.idea/misc.xml index 9383d6f..fd4e49e 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -9,7 +9,7 @@ - + \ No newline at end of file diff --git a/api.http b/api.http index 3b3df0c..3b4c02a 100644 --- a/api.http +++ b/api.http @@ -9,22 +9,33 @@ Content-Type: application/json "uniqueIdentifier": "KA03HD6064" } +### create row, with autogenerated identifier +POST http://localhost:9001/api/log +Content-Type: application/json + +{ + "data": { + "user": "gowthaman", + "action": "logout" + } +} + ### get row -GET http://localhost:9001/api/vehicle/1 +GET http://localhost:9001/api/vehicle/KA03HD6064 ### query row POST http://localhost:9001/api/vehicle/query Content-Type: application/json { - "sql": "select id, tenant_id, deleted_on, deleted_by, deleted, version, created_at, modified_at, created_by, modified_by, data, tags, comments, unique_identifier, entity_name from data_model where data ->> 'number' = :number", + "sql": "select sys_pk, tenant_id, deleted_on, deleted_by, deleted, version, created_at, modified_at, created_by, modified_by, data, tags, comments, unique_identifier, entity_name from data_model where data ->> 'number' = :number", "params": { "number": "KA03HD6064" } } ### update field -PATCH http://localhost:9001/api/vehicle/1 +PATCH http://localhost:9001/api/vehicle/KA03HD6064 Content-Type: application/json { @@ -34,7 +45,7 @@ Content-Type: application/json ### upate a row -PUT http://localhost:9001/api/vehicle/1 +PUT http://localhost:9001/api/vehicle/KA03HD6064 Content-Type: application/json { @@ -44,4 +55,4 @@ Content-Type: application/json } ### delete a row -DELETE http://localhost:9001/api/vehicle/1 \ No newline at end of file +DELETE http://localhost:9001/api/vehicle/KA03HD6064 \ No newline at end of file diff --git a/app-sample.properties b/app-sample.properties new file mode 100644 index 0000000..61b1b23 --- /dev/null +++ b/app-sample.properties @@ -0,0 +1,7 @@ +app.port=9001 +app.cors.enabled=true +app.cors.hosts=www.readymixerp.com,app.readymixerp.com +app.db.user=postgres +app.db.pass=postgres +app.db.url=jdbc:postgresql://192.168.64.6/modules_app +app.db.run_migration=true \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index 42cf0c1..c66d053 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,7 +27,8 @@ dependencies { implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.+") implementation("org.slf4j:slf4j-simple:2.0.7") - + api ("net.cactusthorn.config:config-core:0.81") + kapt("net.cactusthorn.config:config-compiler:0.81") kapt("io.ebean:kotlin-querybean-generator:13.23.2") } @@ -40,5 +41,5 @@ kotlin { } application { - mainClass.set("com.readymixerp.MainKt") + mainClass.set("com.restapi.MainKt") } \ No newline at end of file diff --git a/src/main/kotlin/com/readymixerp/Main.kt b/src/main/kotlin/com/readymixerp/Main.kt deleted file mode 100644 index 33183d8..0000000 --- a/src/main/kotlin/com/readymixerp/Main.kt +++ /dev/null @@ -1,116 +0,0 @@ -package com.readymixerp - -import com.readymixerp.domain.DataModel -import com.readymixerp.domain.Session -import com.readymixerp.domain.Session.database -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 java.time.LocalDateTime - -fun main(args: Array) { - Javalin - .create { cfg -> - cfg.http.generateEtags = true - cfg.plugins.enableCors { container -> - container.add { - it.allowHost( - "http://localhost:5173", - "https://www.readymixerp.com", - "https://app.readymixerp.com" - ) - } - } - cfg.http.defaultContentType = ContentType.JSON - cfg.compression.gzipOnly() - cfg.jsonMapper(JavalinJackson(Session.objectMapper)) - } - .routes { - before("/*") { - //validate, auth token - } - path("/api") { - post("/execute/{name}"){ - val name = it.pathParam("name") - val params = it.bodyAsClass>() - 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.find(DataModel::class.java, it.pathParam("id")) ?: throw NotFoundResponse() - ) - } - post("/{entity}/query") { - val sql = it.bodyAsClass() - 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}") { - database.save( - it.bodyAsClass().apply { - this.entityName = it.pathParam("entity") - if(this.uniqueIdentifier.isEmpty()) { - //todo: set a counter - } - this.uniqueIdentifier = "${this.entityName}_${this.uniqueIdentifier}" - } - ) - } - put("/{entity}/{id}") { - val e = database.find(DataModel::class.java, it.pathParam("id")) ?: throw NotFoundResponse() - val newData = it.bodyAsClass>() - e.data.putAll(newData) - e.update() - } - patch("/{entity}/{id}") { - val e = database.find(DataModel::class.java, it.pathParam("id")) ?: throw NotFoundResponse() - val pv = it.bodyAsClass() - e.data[pv.key] = pv.value; - e.update() - } - delete("/{entity}/{id}") { - val id = it.pathParam("id") - val e = database.find(DataModel::class.java, id) ?: throw NotFoundResponse() - 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) - } - .start(9001) -} - -data class Query( - val sql: String, - val params: Map -) - -data class PatchValue(val key: String, val value: Any) \ No newline at end of file diff --git a/src/main/kotlin/com/readymixerp/domain/db.kt b/src/main/kotlin/com/readymixerp/domain/db.kt deleted file mode 100644 index ce9d088..0000000 --- a/src/main/kotlin/com/readymixerp/domain/db.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.readymixerp.domain - -import com.fasterxml.jackson.databind.DeserializationFeature -import com.fasterxml.jackson.databind.SerializationFeature -import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import io.ebean.Database -import io.ebean.DatabaseFactory -import io.ebean.config.CurrentTenantProvider -import io.ebean.config.CurrentUserProvider -import io.ebean.config.DatabaseConfig -import io.ebean.config.TenantMode - -data class CurrentUser( - val anon: Boolean = true, - val userId:Long = 0, - val tenantId: Long = 0 -) -object Session { - private val currentUser = object: ThreadLocal() { - override fun initialValue(): CurrentUser { - return CurrentUser() - } - } - private val sc = DatabaseConfig().apply { - loadFromProperties() - tenantMode = TenantMode.PARTITION - currentTenantProvider = CurrentTenantProvider { currentUser.get().tenantId } - currentUserProvider = CurrentUserProvider { currentUser.get().userId } - } - - val database: Database = DatabaseFactory.create(sc) - val objectMapper = jacksonObjectMapper().apply { - configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) - configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) - findAndRegisterModules() - } - fun currentUser() = currentUser.get().userId - -} \ No newline at end of file diff --git a/src/main/kotlin/com/restapi/Main.kt b/src/main/kotlin/com/restapi/Main.kt new file mode 100644 index 0000000..a2e6ee2 --- /dev/null +++ b/src/main/kotlin/com/restapi/Main.kt @@ -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) { + 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>() + 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() + 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() + 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().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>() + e.data.putAll(newData) + e.update() + } + patch("/{entity}/{id}") { + val e = database.findByEntityAndId(it.pathParam("entity"), it.pathParam("id")) + val pv = it.bodyAsClass() + 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 +) + +data class PatchValue(val key: String, val value: Any) \ No newline at end of file diff --git a/src/main/kotlin/com/restapi/config/AppConfig.kt b/src/main/kotlin/com/restapi/config/AppConfig.kt new file mode 100644 index 0000000..5cf8687 --- /dev/null +++ b/src/main/kotlin/com/restapi/config/AppConfig.kt @@ -0,0 +1,48 @@ +package com.restapi.config + +import net.cactusthorn.config.core.Config +import net.cactusthorn.config.core.Default +import net.cactusthorn.config.core.Key +import net.cactusthorn.config.core.factory.ConfigFactory +import net.cactusthorn.config.core.loader.LoadStrategy + +const val INITIAL_ROLES_JSON = """{ + "roles": [] +}""" + +@Config( + sources = [ + "file:~/app.properties", "system:env" + ], + loadStrategy = LoadStrategy.FIRST_KEYCASEINSENSITIVE +) +interface AppConfig { + @Key("app.cors.enabled") + @Default("false") + fun corsEnabled(): Boolean + + @Key("app.port") + @Default("9001") + fun portNumber(): Int + + @Key("app.cors.hosts") + @Default("*") + fun corsHosts(): List + + @Key("app.db.user") + fun dbUser(): String + + @Key("app.db.pass") + fun dbPass(): String + + @Key("app.db.url") + fun dbUrl(): String + + @Key("app.db.run_migration") + fun dbRunMigration(): Boolean + + + companion object { + val appConfig: AppConfig = ConfigFactory.builder().build().create(AppConfig::class.java) + } +} \ No newline at end of file diff --git a/src/main/kotlin/com/restapi/domain/db.kt b/src/main/kotlin/com/restapi/domain/db.kt new file mode 100644 index 0000000..a80867b --- /dev/null +++ b/src/main/kotlin/com/restapi/domain/db.kt @@ -0,0 +1,75 @@ +package com.restapi.domain + +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.Module +import com.fasterxml.jackson.databind.SerializationFeature +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import com.restapi.config.AppConfig.Companion.appConfig +import io.ebean.Database +import io.ebean.DatabaseFactory +import io.ebean.config.CurrentTenantProvider +import io.ebean.config.CurrentUserProvider +import io.ebean.config.DatabaseConfig +import io.ebean.config.TenantMode +import java.util.* + +data class CurrentUser( + val anon: Boolean = true, + val userId: String = "", + val tenantId: String = "" +) + + +object Session { + private val currentUser = object : ThreadLocal() { + override fun initialValue(): CurrentUser { + return CurrentUser() + } + } + private val sc = DatabaseConfig().apply { + loadFromProperties(Properties().apply { + setProperty("datasource.db.username", appConfig.dbUser()) + setProperty("datasource.db.password", appConfig.dbPass()) + setProperty("datasource.db.url", appConfig.dbUrl()) + setProperty("ebean.migration.run", appConfig.dbRunMigration().toString()) + }) + tenantMode = TenantMode.PARTITION + currentTenantProvider = CurrentTenantProvider { currentUser.get().tenantId } + currentUserProvider = CurrentUserProvider { currentUser.get().userId } + } + + val database: Database = DatabaseFactory.create(sc) + val objectMapper = jacksonObjectMapper().apply { + configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + findAndRegisterModules() + } + + fun currentUser() = currentUser.get().userId + + fun Database.findByEntityAndId(entity: String, id: String): DataModel { + return find(DataModel::class.java) + .where() + .eq("uniqueIdentifier", id) + .eq("entityName", entity) + .findOne() ?: throw DataNotFoundException + } + + private fun seqName(entity: String) = "sequence_$entity" + fun creatSeq(entity: String): Int { + return database.sqlUpdate("CREATE SEQUENCE IF NOT EXISTS ${seqName(entity)} START 1;").execute() + } + + fun nextUniqId(entity: String): String { + val s = database + .sqlQuery("SELECT nextval('${seqName(entity)}');") + .findOne()?.getLong("nextval") ?: throw DataNotFoundException + return String.format("%s-%s", entity, "$s".padStart(10, '0')) + } + +} + +object DataNotFoundException : Exception() { + private fun readResolve(): Any = DataNotFoundException +} \ No newline at end of file diff --git a/src/main/kotlin/com/readymixerp/domain/migration.kt b/src/main/kotlin/com/restapi/domain/migration.kt similarity index 86% rename from src/main/kotlin/com/readymixerp/domain/migration.kt rename to src/main/kotlin/com/restapi/domain/migration.kt index faf40e8..f3806de 100644 --- a/src/main/kotlin/com/readymixerp/domain/migration.kt +++ b/src/main/kotlin/com/restapi/domain/migration.kt @@ -1,11 +1,11 @@ -package com.readymixerp.domain +package com.restapi.domain import io.ebean.annotation.Platform import io.ebean.dbmigration.DbMigration object DBMigration { - fun create(){ + private fun create(){ val dbMigration: DbMigration = DbMigration.create() dbMigration.setPlatform(Platform.POSTGRES) diff --git a/src/main/kotlin/com/readymixerp/domain/models.kt b/src/main/kotlin/com/restapi/domain/models.kt similarity index 54% rename from src/main/kotlin/com/readymixerp/domain/models.kt rename to src/main/kotlin/com/restapi/domain/models.kt index 2391853..84cfde0 100644 --- a/src/main/kotlin/com/readymixerp/domain/models.kt +++ b/src/main/kotlin/com/restapi/domain/models.kt @@ -1,8 +1,14 @@ -package com.readymixerp.domain +package com.restapi.domain +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonDeserializer +import com.fasterxml.jackson.databind.annotation.JsonDeserialize import io.ebean.Model import io.ebean.annotation.DbArray import io.ebean.annotation.DbJsonB +import io.ebean.annotation.Index +import io.ebean.annotation.Platform import io.ebean.annotation.SoftDelete import io.ebean.annotation.TenantId import io.ebean.annotation.WhenCreated @@ -22,7 +28,7 @@ data class Comments(val text: String = "", val by: String = "", val at: LocalDat abstract class BaseModel : Model() { @Id @GeneratedValue - var id: Long = 0 + var sysPk: Long = 0 @SoftDelete var deleted: Boolean = false @@ -37,20 +43,17 @@ abstract class BaseModel : Model() { var modifiedAt: LocalDateTime? = null @TenantId - var tenantId: Long = 0L + var tenantId: String = "" @WhoCreated - var createdBy: Long = 0L + var createdBy: String = "" @WhoModified - var modifiedBy: Long? = null + var modifiedBy: String? = null var deletedOn: LocalDateTime? = null - var deletedBy: Long? = null - - @DbJsonB - var data: MutableMap = hashMapOf() + var deletedBy: String? = null @DbArray var tags: MutableList = arrayListOf() @@ -60,10 +63,29 @@ abstract class BaseModel : Model() { } @Entity +@Index(unique = true, name = "entity_unique_id", columnNames = ["entity_name", "unique_identifier", "tenant_id"]) open class DataModel : BaseModel() { + @JsonDeserialize(using = SafeStringDeserializer::class) var uniqueIdentifier: String = "" + + @JsonDeserialize(using = SafeStringDeserializer::class) var entityName: String = "" + @Index(definition = "create index data_jsonb_idx on data_model using GIN (data) ", platforms = [Platform.POSTGRES]) + @DbJsonB + var data: MutableMap = hashMapOf() -} \ No newline at end of file +} + +class SafeStringDeserializer : JsonDeserializer() { + private val regex = Regex("^[a-zA-Z0-9\\-_\\.]+$") + + override fun deserialize(p: JsonParser, ctxt: DeserializationContext?): String { + + val text = p.text + if (!regex.matches(text)) throw IllegalArgumentException() + return text + } + +} diff --git a/src/main/resources/dbmigration/1.0__initial.sql b/src/main/resources/dbmigration/1.0__initial.sql deleted file mode 100644 index 62c7453..0000000 --- a/src/main/resources/dbmigration/1.0__initial.sql +++ /dev/null @@ -1,32 +0,0 @@ --- apply changes -create table data_model -( - id bigint generated by default as identity not null, - tenant_id bigint not null, - deleted_on timestamp, - deleted_by bigint, - deleted boolean default false not null, - version integer not null, - created_at timestamp not null, - modified_at timestamp not null, - created_by bigint not null, - modified_by bigint not null, - data jsonb not null, - tags varchar[] not null, - comments jsonb not null, - unique_identifier varchar(255) not null, - entity_name varchar(255) not null, - constraint pk_data_model primary key (id) -); - -create unique index data_model_uniq_identifier on data_model (unique_identifier); -create index data_json_jdx on data_model using GIN (data); -create index data_json_meta_data on data_model (tenant_id, - deleted, - version, - created_at, - modified_at, - created_by, - modified_by, - entity_name - ); \ No newline at end of file diff --git a/src/main/resources/dbmigration/model/1.0__initial.model.xml b/src/main/resources/dbmigration/model/1.0__initial.model.xml deleted file mode 100644 index 6197135..0000000 --- a/src/main/resources/dbmigration/model/1.0__initial.model.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/main/resources/ebean.mf b/src/main/resources/ebean.mf index 1b1e74b..67a052b 100644 --- a/src/main/resources/ebean.mf +++ b/src/main/resources/ebean.mf @@ -1,4 +1,4 @@ -entity-packages: com.readymixerp.domain -querybean-packages: com.readymixerp.domain -transactional-packages: com.readymixerp +entity-packages: com.restapi.domain +querybean-packages: com.restapi.domain +transactional-packages: com.restapi profile-location: true