In the context of http4k + openapi: ```data class ...
# http4k
a
In the context of http4k + openapi:
Copy code
data class CustomerId(
    val id: String = UUID.randomUUID().toString(),
)
How do I specify to the spec that this
id
is a string of type UUID? And maybe some description?
I tried:
Copy code
contract {
            routes += registerCustomerRoute()
            renderer = OpenApi3(
                ApiInfo("My API", "0.0.1"),
                typeToMetadata = mapOf(
                    CustomerId::id.returnType to FieldMetadata(mapOf("format" to "uuid", "description" to "Customer identifier (UUID) from custom schema bla bla"))
                )
            )
            this.descriptionPath = descriptionPath
        }
But unfortunately this does not work:
Copy code
"CustomerId": {
"properties": {
"id": {
"example": "5ba7358e-cf20-46d9-b8db-df9554e268b3",
"type": "string",
"nullable": false
}
},
"example": {
"id": "5ba7358e-cf20-46d9-b8db-df9554e268b3"
},
"type": "object",
"required": [
"id"
]
}
It seems it's not actually using these metadata types for anything?
d
What happens if you make the field a UUID?
you can use a direct UUID to put the format in and then create your own
FieldMetadataRetrievalStrategy
and inject it into the
AutoJsonToJsonSchema
instance
a
Well, that is technically true, but maybe I want a special format
I want to be able to express that
d
Then you can just use FieldMetadataRetrievalStrategy to do that
a
Let's say a
maskedPan
in a
CreditCard
type:
Copy code
CreditCard:
      type: object
      properties:
        type:
          type: string
          enum:
            - CREDIT_CARD
        brand:
          type: string
          enum:
            - VISA
            - MASTERCARD
            - AMEX
        maskedPan:
          type: string
          description: Masked primary account number (e.g., "**** **** **** 1234")
          example: "**** **** **** 1234"
          pattern: "^\\*{4} \\*{4} \\*{4} \\d{4}$"
How do I express this?
d
by implementing a FieldMetadataRetrievalStrategy. Typically these look at field level annotations. You can use JacksonFieldMetadataRetrievalStrategy as an example of how that works
a
I think I am doing it wrong somehow. As far as I understand, JacksonFieldMetadataRetrievalStrategy returns a FieldMetaData. So I tried using that directly:
Copy code
contract {
            routes += registerCustomerRoute()
            renderer = OpenApi3(
                ApiInfo("My API", "0.0.1"),
                typeToMetadata = mapOf(
                    CustomerId::id.returnType to FieldMetadata(mapOf("description" to "hello world"))
                )
            )
            this.descriptionPath = descriptionPath
        }
But that apparently isn't enough? I couldn't find any docs on this either and I can't find examples of usages of
JacksonFieldMetadataRetrievalStrategy
on github 😞
Copy code
val myJsonSchemaCreator = AutoJsonToJsonSchema(
        myJson,
        FieldRetrieval.compose(
            SimpleLookup(
                metadataRetrievalStrategy =
                    JacksonFieldMetadataRetrievalStrategy
                        .then(PrimitivesFieldMetadataRetrievalStrategy)
                        .then(CreditCardFieldMetadataStrategy)
            ),
            FieldRetrieval.compose(JacksonJsonPropertyAnnotated, JacksonJsonNamingAnnotated(myJson))
        )
    )

object CreditCardFieldMetadataStrategy : FieldMetadataRetrievalStrategy {
	override fun invoke(target: Any, fieldName: String): FieldMetadata =
		when (fieldName) {
			"maskedPan" -> FieldMetadata(
				"description" to "Masked primary account number (e.g., \"**** **** **** 1234\")",
				"example" to "**** **** **** 1234",
				"pattern" to "^\\*{4} \\*{4} \\*{4} \\d{4}$"
			)
			else -> FieldMetadata.empty
		}
}
How do I plug this into my contract route?
Copy code
contract {
            routes += customerRoutes
            renderer = OpenApi3(apiInfo = ApiInfo("my api", "0.0.1"))
            this.descriptionPath = descriptionPath
        }