Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/validate mandatory data #79

Merged
merged 3 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading