I have a modelling question… I think :rubber_duck:...
# compiler
a
I have a modelling question… I think rubber duck I have data classes that represents the fully valid state of my entities. My API also has a “patch” function where I can send in one or more attributes (as a JSON map), that maps to said data class. To accommodate this, I have to make all properties nullable. Is there a way to somehow represent “partial data classes” in a reliable way? Or some other way to talk about a subset of properties of a data class?
I thought about doing something like this:
Copy code
data class CustomerEntity(
  val id: String,
  val createdAt: LocalDate,
  val modifiedAt: LocalDate,
  val name: String,
  val status: EntityStatus,
  val email: String
) {
  val patchProps = setOf(::name, ::status, ::email)
}
That way, I can at least refer to the properties I want to be “patchable”. But the type of these properties are relatively weak, and I’m not sure how to somehow apply this set of KProperty instances to an incoming JSON map etc
h
I would annotate the properties with a custom annotation and generate a separate data class only with these properties. Then you can use the patch class as usual and don’t to use
var
. Ideally, the generator should support nested classes/usages too ;)
a
ah, interesting. What can I use to generate that data class? Some kind of compiler plugin?
h
Well, I am not aware of such plugin, but you can write a custom plugin by yourself. I would write a ksp plugin to extract the fields, and use eg kotlinpoet to generate the classes, and not a Kotlin compiler plugin, because KSP api is easier and more stable.
K 1
👍 1
Downside of ksp is no IntelliJ support until compiling the code. If you really need it, you have to use a Kotlin compiler plugin.
👍 1
t
Well, I think the solution depends on how deep you want to go into plugin writing. For example, I have this application level code. A compiler plugin analyses this code and builds an independent metadata which I use to generate UI, perform validation, send partial updates, serialize/deserialize, etc:
Copy code
@Adat
class StringTest(
    val someString : String
) {
    override fun descriptor() {
        properties {
            someString minLength 2 maxLength 4 default "1234" blank false pattern "[0-9]*" secret true
        }
    }
}
I actually have a
readonly
descriptor that I use to indicate read-only fields. The compiler plugin turns the
descriptor
(and the data fields) into this.
Copy code
{
  "version": 1,
  "name": "fun.adaptive.adat.validation.StringTest",
  "flags": 1,
  "properties": [
    {
      "name": "someString",
      "index": 0,
      "flags": 3,
      "signature": "T",
      "descriptors": [
        {
          "name": "StringMinLength",
          "parameters": "2"
        },
        {
          "name": "StringMaxLength",
          "parameters": "4"
        },
        {
          "name": "StringDefault",
          "parameters": "1234"
        },
        {
          "name": "StringBlank",
          "parameters": "false"
        },
        {
          "name": "StringPattern",
          "parameters": "[0-9]*"
        },
        {
          "name": "StringSecret",
          "parameters": "true"
        }
      ]
    }
  ]
}
You might also want to check out https://github.com/JavierSegoviaCordoba/kopy to see an example how it's done. (This is not the one I use, but it's quite good.)
🙌 1
a
thanks! Kopy looks interesting