andyglow / scala-jsonschema

Scala JSON Schema
Other
122 stars 38 forks source link

field level @title+@description annotations needed #290

Open askarbozcan opened 1 year ago

askarbozcan commented 1 year ago

As title says. Would be nice if we could define "title" property of our fields. Or, going even further: Customizable annotations?

andyglow commented 1 year ago

@askarbozcan what do you mean by customizable annotations? free key-value pairs? like

askarbozcan commented 1 year ago

Something like (just an example):

case class myCustomAnnotation(value: String) extends JSONSchemaAnnotation

case class Person
(
  @title("Person Name")
  @myCustomAnnotation("Annotation Value")
  name: String
)

and it would be reflected in the schema as such:

{
  "properties": {
    "name": {
      "type": "string"
      "title": "Person Name",
      "myCustomAnnotation": "Annotation Value"
    }
 }, // .. etc
}

Not sure if this is legal JSON Schema, but it could be used to add custom, field-level metadata.

By the way, you changed the title to field level @title + @description, however field level descriptions are already present.

askarbozcan commented 1 year ago

For anyone finding this issue, here's my temporary fix (for Circe)

import io.circe._

object JSONSchemaUtils {
    implicit class circeJsonSchema(schema: Json) {
        def fieldProperty(fieldName: String, propName: String, value: Json): Json = {
            val cursor = schema.hcursor
            val newSchemaOpt = cursor
                .downField("properties")
                .downField(fieldName)
                .withFocus(_.mapObject(j => j.+:(propName, value)))
                .top

            newSchemaOpt.getOrElse(schema)
        }

        def fieldTitle(fieldName: String, title: String) = {
            this.fieldProperty(fieldName, "title", Json.fromString(title))
        }

        def withEnum(fieldName: String, fieldOptions: Seq[String]) = {
            this.fieldProperty(fieldName, "enum", Json.fromValues(fieldOptions.map(Json.fromString(_))))
        }
    }
}

And the usage:

final case class Create(
    user_name: UserInfo.Name,
    department_code: Department.Code,
    password: UserInfo.PasswordHash
)

val intermediateSchema = json.Json.objectSchema[Create]()
                .asCirce(json.schema.Version.Draft07("createView"))
                .fieldTitle("user_name", "User Name")
                .fieldTitle("department_code", "User Name")
                .fieldTitle("password", "Password")
                .withEnum("department_code", List("CNG", "PSY"))