Skip to content

Commit

Permalink
Merge pull request #79 from SchwarzIT/feature/validate-mandatory-data
Browse files Browse the repository at this point in the history
Feature/validate mandatory data
  • Loading branch information
sbra0902 authored Jan 12, 2024
2 parents 0116288 + c98e447 commit be45338
Show file tree
Hide file tree
Showing 20 changed files with 200 additions and 86 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ buildscript {
}
}
dependencies {
classpath 'com.android.tools.build:gradle:8.0.1'
classpath("com.android.library:com.android.library.gradle.plugin:8.1.4")
classpath("com.android.application:com.android.application.gradle.plugin:8.1.4")
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.21"
classpath "org.jlleitschuh.gradle:ktlint-gradle:11.5.1"
// NOTE: Do not place your application dependencies here; they belong
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.schwarz.crystalapi

abstract class CrystalCreator<T : MapSupport, V> {

abstract fun create(): T

abstract fun create(map: MutableMap<String, V>): T
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ package com.schwarz.crystalapi
import kotlin.reflect.KClass

@Retention(AnnotationRetention.BINARY)
annotation class Field(val name: String = "", val type: KClass<out Any>, val list: Boolean = false, val defaultValue: String = "", val readonly: Boolean = false, val comment: Array<String> = [])
annotation class Field(val name: String = "", val type: KClass<out Any>, val list: Boolean = false, val defaultValue: String = "", val readonly: Boolean = false, val comment: Array<String> = [], val mandatory: Boolean = false)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.schwarz.crystalapi

interface MandatoryCheck {
fun validate()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package com.schwarz.crystalapi

abstract class WrapperCompanion<T : MapSupport> : CrystalCreator<T, Any?>() {

fun fromMap(obj: MutableMap<String, Any?>?): T? {
if (obj == null) {
return null
}
return create(obj)
}

fun fromMap(obj: List<MutableMap<String, Any?>>?): List<T>? {
if (obj == null) {
return null
}
var result = ArrayList<T>()
for (entry in obj) {
result.add(create(entry))
}
return result
}

abstract fun toMap(obj: T?): MutableMap<String, Any>

fun toMap(obj: List<T>?): List<MutableMap<String, Any>> {
if (obj == null) {
return listOf()
}
var result = ArrayList<MutableMap<String, Any>>()
for (entry in obj) {
var temp = mutableMapOf<String, Any>()
temp.putAll(toMap(entry)!!)
result.add(temp)
}
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.schwarz.crystalapi.schema

data class EntitySchema(val name: String, val fields: List<Fields>, val basedOn: List<String>, val queries: List<Queries>, val docId: DocId?, val deprecatedSchema: DeprecatedSchema?)

data class Fields(val dbField: String, val fieldType: String, val isIterable: Boolean, val isConstant: Boolean, val defaultValue: String)
data class Fields(val dbField: String, val fieldType: String, val isIterable: Boolean, val isConstant: Boolean, val defaultValue: String, val mandatory: Boolean?)

data class DeprecatedSchema(val replacedBy: String?, val inUse: Boolean, val deprecatedFields: List<DeprecatedFields>)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ object CrystalWrap {
} ?: null
}

inline fun validate(
doc: MutableMap<String, Any>,
mandatoryFields: Array<String>
) {
for (mandatoryField in mandatoryFields) {
doc[mandatoryField]!!
}
}

inline fun <T> getList(
changes: MutableMap<String, Any?>,
doc: MutableMap<String, Any>,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.schwarz.crystalapi.util

import org.junit.Assert
import org.junit.Test

class CrystalWrapTest {
@Test
fun `test validate throws exception on null value`() {
val values = mutableMapOf<String, Any>("foo" to "bar")
try {
CrystalWrap.validate(values, arrayOf("foo", "foobar"))
Assert.fail("there should be an exception")
} catch (e: NullPointerException) {
}
}
}

This file was deleted.

4 changes: 2 additions & 2 deletions crystal-map-processor/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ dependencies {
testImplementation 'org.mockito:mockito-core:1.10.19'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0'
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
kapt "com.google.auto.service:auto-service:1.0-rc7"
compileOnly "com.google.auto.service:auto-service:1.0-rc7"
kapt "com.google.auto.service:auto-service:1.1.1"
compileOnly "com.google.auto.service:auto-service:1.1.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,23 @@ import com.squareup.kotlinpoet.TypeSpec
object BuilderClassGeneration {

fun generateBaseBuilder(holder: BaseEntityHolder): TypeSpec.Builder {
val builderBuilder = TypeSpec.classBuilder("Builder").primaryConstructor(FunSpec.constructorBuilder().addParameter("parent", holder.entityTypeName).build())
builderBuilder.addProperty(PropertySpec.builder("obj", holder.entityTypeName).initializer("parent").build())
builderBuilder.addFunction(FunSpec.builder("exit").addStatement("return obj").returns(holder.entityTypeName).build())
val builderBuilder = TypeSpec.classBuilder("Builder").primaryConstructor(
FunSpec.constructorBuilder().addParameter("parent", holder.entityTypeName).build()
)
builderBuilder.addProperty(
PropertySpec.builder("obj", holder.entityTypeName).initializer("parent").build()
)
builderBuilder.addFunction(
FunSpec.builder("exit")
.addStatement("obj.validate()")
.addStatement("return obj")
.returns(holder.entityTypeName).build()
)
return builderBuilder
}

fun generateBuilderFun(holder: BaseEntityHolder): FunSpec {
return FunSpec.builder("builder").addStatement("return Builder(this)").returns(ClassName(holder.sourcePackage, "${holder.entitySimpleName}.Builder")).build()
return FunSpec.builder("builder").addStatement("return Builder(this)")
.returns(ClassName(holder.sourcePackage, "${holder.entitySimpleName}.Builder")).build()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.jvm.throws
import com.schwarz.crystalapi.Entity
import com.schwarz.crystalapi.MandatoryCheck
import com.schwarz.crystalapi.PersistenceConfig
import com.schwarz.crystalapi.PersistenceException

Expand All @@ -31,6 +32,7 @@ class EntityGeneration {

fun generateModel(holder: EntityHolder, useSuspend: Boolean): FileSpec {
val companionSpec = TypeSpec.companionObjectBuilder()
companionSpec.superclass(TypeUtil.crystalCreator(TypeUtil.any(), holder.entityTypeName))
companionSpec.addProperty(idConstant())
companionSpec.addProperty(CblReduceGeneration.onlyIncludeProperty(holder))
companionSpec.addFunctions(create(holder, useSuspend))
Expand Down Expand Up @@ -58,10 +60,12 @@ class EntityGeneration {
.addModifiers(KModifier.PUBLIC)
.addSuperinterface(TypeUtil.iEntity())
.addSuperinterface(holder.interfaceTypeName)
.addSuperinterface(MandatoryCheck::class)
.addProperty(holder.dbNameProperty())
.addFunction(EnsureTypesGeneration.ensureTypes(holder, false))
.addFunction(CblDefaultGeneration.addDefaults(holder, false))
.addFunction(CblConstantGeneration.addConstants(holder, false))
.addFunction(ValidateMethodGeneration.generate(holder, true))
.addProperty(
PropertySpec.builder(
"mDoc",
Expand Down Expand Up @@ -250,11 +254,16 @@ class EntityGeneration {
.throws(PersistenceException::class)

val idFields = holder.docId?.distinctFieldAccessors(holder) ?: emptyList()
if (holder.deprecated?.addDeprecatedFunctions(idFields.toTypedArray(), saveBuilder) == true) {
if (holder.deprecated?.addDeprecatedFunctions(
idFields.toTypedArray(),
saveBuilder
) == true
) {
saveBuilder.addStatement("// workaround for kotlin poet to create brackets")
saveBuilder.addStatement("throw %T()", UnsupportedOperationException::class)
} else {
saveBuilder.addStatement("val doc = toMap()")
saveBuilder.addStatement("validate()")
var idResolve = "getId()"

holder.docId?.let {
Expand Down Expand Up @@ -299,13 +308,13 @@ class EntityGeneration {
PersistenceConfig::class,
holder.dbName
).returns(holder.entityTypeName).build(),
FunSpec.builder("create").addModifiers(evaluateModifiers(useSuspend))
FunSpec.builder("create").addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
.addAnnotation(JvmStatic::class).addStatement(
"return %N(%T())",
holder.entitySimpleName,
TypeUtil.hashMapStringAny()
).returns(holder.entityTypeName).build(),
FunSpec.builder("create").addModifiers(KModifier.PUBLIC)
FunSpec.builder("create").addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
.addParameter("map", TypeUtil.mutableMapStringAny()).addAnnotation(JvmStatic::class)
.addStatement(
"return %N(map)",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.schwarz.crystalprocessor.generation.model

import com.schwarz.crystalapi.util.CrystalWrap
import com.schwarz.crystalprocessor.model.entity.BaseEntityHolder
import com.schwarz.crystalprocessor.util.TypeUtil
import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier

object ValidateMethodGeneration {

fun generate(holder: BaseEntityHolder, useMDocChanges: Boolean): FunSpec {
val validateBuilder =
FunSpec.builder("validate").addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
val mandatoryFields = holder.fields.values.filter { it.mandatory }.map { it.constantName }

if (mandatoryFields.isNotEmpty()) {
val statement =
"%T.validate(toMap(), %M(${mandatoryFields.map { "%N" }.joinToString()}))"
val arguments = mutableListOf(CrystalWrap::class, TypeUtil.arrayOf())
for (mandatoryField in mandatoryFields) {
arguments.add(mandatoryField)
}

validateBuilder.addStatement(
statement,
*arguments.toTypedArray()
)
}

return validateBuilder.build()
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.schwarz.crystalprocessor.generation.model

import com.schwarz.crystalapi.MandatoryCheck
import com.schwarz.crystalprocessor.generation.MapifyableImplGeneration
import com.schwarz.crystalprocessor.model.entity.BaseEntityHolder
import com.schwarz.crystalprocessor.model.entity.WrapperEntityHolder
Expand All @@ -11,18 +12,21 @@ class WrapperGeneration {

fun generateModel(holder: WrapperEntityHolder, useSuspend: Boolean): FileSpec {
val companionSpec = TypeSpec.companionObjectBuilder()
companionSpec.superclass(TypeUtil.wrapperCompanion(holder.entityTypeName))

val builderBuilder = BuilderClassGeneration.generateBaseBuilder(holder)

val typeBuilder = TypeSpec.classBuilder(holder.entitySimpleName)
.addSuperinterface(TypeUtil.mapSupport())
.addModifiers(KModifier.PUBLIC)
.addSuperinterface(holder.interfaceTypeName)
.addSuperinterface(MandatoryCheck::class)
.addFunction(EnsureTypesGeneration.ensureTypes(holder, true))
.addFunction(CblDefaultGeneration.addDefaults(holder, true))
.addFunction(CblConstantGeneration.addConstants(holder, true))
.addFunction(SetAllMethodGeneration().generate(holder, false))
.addFunction(MapSupportGeneration.toMap(holder))
.addFunction(ValidateMethodGeneration.generate(holder, false))
.addProperty(PropertySpec.builder("mDoc", TypeUtil.mutableMapStringAnyNullable()).addModifiers(KModifier.PRIVATE).mutable().initializer("%T()", TypeUtil.linkedHashMapStringAnyNullable()).build())
.addFunction(constructorMap())
.addFunction(constructorDefault())
Expand Down Expand Up @@ -52,7 +56,6 @@ class WrapperGeneration {
}
}

companionSpec.addFunctions(fromMap(holder))
companionSpec.addFunctions(toMap(holder))
companionSpec.addFunctions(create(holder))
typeBuilder.addType(companionSpec.build())
Expand All @@ -66,54 +69,19 @@ class WrapperGeneration {

private fun toMap(holder: BaseEntityHolder): List<FunSpec> {
val nullCheck = CodeBlock.builder().beginControlFlow("if(obj == null)").addStatement("return mutableMapOf()").endControlFlow().build()
val nullCheckList = CodeBlock.builder().beginControlFlow("if(obj == null)").addStatement("return listOf()").endControlFlow().build()

return Arrays.asList(
FunSpec.builder("toMap").addModifiers(KModifier.PUBLIC)
FunSpec.builder("toMap").addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE)
.addParameter("obj", holder.entityTypeName.copy(nullable = true)).returns(TypeUtil.mutableMapStringAny())
.addAnnotation(JvmStatic::class)
.addCode(nullCheck).addStatement("var result = mutableMapOf<%T,%T>()", TypeUtil.string(), TypeUtil.any())
.beginControlFlow("obj.mDoc.forEach")
.beginControlFlow("if(it.value != null)").addStatement("result[it.key] = it.value!!").endControlFlow()
.endControlFlow()
.addStatement("return result").build(),

FunSpec.builder("toMap").addModifiers(KModifier.PUBLIC)
.addParameter("obj", TypeUtil.list(holder.entityTypeName).copy(nullable = true)).addAnnotation(JvmStatic::class)
.returns(TypeUtil.listWithMutableMapStringAny()).addCode(nullCheckList)
.addStatement("var result = %T()", TypeUtil.arrayListWithMutableMapStringAny())
.addCode(
CodeBlock.builder()
.beginControlFlow("for(entry in obj)")
.addStatement("var temp = mutableMapOf<%T,%T>()", TypeUtil.string(), TypeUtil.any())
.addStatement("temp.putAll(%N.toMap(entry)!!)", holder.entitySimpleName)
.addStatement("result.add(temp)", holder.entitySimpleName).endControlFlow().build()
)
.addStatement("return result").build()
)
}

private fun fromMap(holder: BaseEntityHolder): List<FunSpec> {
val nullCheck = CodeBlock.builder().beginControlFlow("if(obj == null)").addStatement("return null").endControlFlow().build()

return Arrays.asList(
FunSpec.builder("fromMap").addModifiers(KModifier.PUBLIC)
.addParameter("obj", TypeUtil.mutableMapStringAnyNullable().copy(nullable = true)).addAnnotation(JvmStatic::class)
.returns(holder.entityTypeName.copy(nullable = true)).addCode(nullCheck)
.addStatement("return %T(obj)", holder.entityTypeName).build(),

FunSpec.builder("fromMap").addModifiers(KModifier.PUBLIC).addAnnotation(JvmStatic::class)
.addParameter("obj", TypeUtil.listWithMutableMapStringAnyNullable().copy(nullable = true))
.returns(TypeUtil.list(holder.entityTypeName).copy(nullable = true)).addCode(nullCheck)
.addStatement("var result = %T()", TypeUtil.arrayList(holder.entityTypeName))
.addCode(
CodeBlock.builder().beginControlFlow("for(entry in obj)")
.addStatement("result.add(%N(entry))", holder.entitySimpleName)
.endControlFlow().build()
).addStatement("return result").build()
)
}

private fun constructorMap(): FunSpec {
return FunSpec.constructorBuilder().addModifiers(KModifier.PUBLIC).addParameter("doc", TypeUtil.mutableMapStringAnyNullable()).addStatement("rebind(ensureTypes(doc))").build()
}
Expand All @@ -125,11 +93,11 @@ class WrapperGeneration {

private fun create(holder: WrapperEntityHolder): List<FunSpec> {
return Arrays.asList(
FunSpec.builder("create").addModifiers(KModifier.PUBLIC).addParameter("doc", TypeUtil.mutableMapStringAnyNullable()).addAnnotation(JvmStatic::class).addStatement(
FunSpec.builder("create").addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE).addParameter("doc", TypeUtil.mutableMapStringAnyNullable()).addAnnotation(JvmStatic::class).addStatement(
"return %N(doc)",
holder.entitySimpleName
).returns(holder.entityTypeName).build(),
FunSpec.builder("create").addModifiers(KModifier.PUBLIC).addAnnotation(JvmStatic::class).addStatement(
FunSpec.builder("create").addModifiers(KModifier.PUBLIC, KModifier.OVERRIDE).addAnnotation(JvmStatic::class).addStatement(
"return %N(%T())",
holder.entitySimpleName,
TypeUtil.hashMapStringAnyNullable()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class SchemaGenerator(path: String, val fileName: String) {

private fun DeprecatedModel.deprecatedToSchema(): DeprecatedSchema = DeprecatedSchema(replacedBy = this.replacedByTypeMirror.toString(), inUse = this.deprecationType == DeprecationType.FIELD_DEPRECATION || this.deprecationType == DeprecationType.ENTITY_DEPRECATION, deprecatedFields = this.deprecatedFields.values.map { DeprecatedFields(field = it.field, replacedBy = it.replacedBy, inUse = it.inUse) })

private fun List<CblBaseFieldHolder>.fieldsToSchemaList(): List<Fields> = map { Fields(dbField = it.dbField, fieldType = it.fieldType.toString(), isIterable = it.isIterable, isConstant = it.isConstant, defaultValue = it.defaultValue) }
private fun List<CblBaseFieldHolder>.fieldsToSchemaList(): List<Fields> = map { Fields(dbField = it.dbField, fieldType = it.fieldType.toString(), isIterable = it.isIterable, isConstant = it.isConstant, defaultValue = it.defaultValue, mandatory = (if (it.mandatory) true else null)) }

private fun List<CblQueryHolder>.queriesToSchemaList(): List<Queries> = map { Queries(it.fields.asList()) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ abstract class CblBaseFieldHolder(val dbField: String, private val mField: Field
val defaultValue: String
get() = mField.defaultValue

val mandatory: Boolean
get() = mField.mandatory

val comment: Array<String>
get() = mField.comment

Expand Down
Loading

0 comments on commit be45338

Please sign in to comment.