Skip to content

Commit

Permalink
add optional and required annotation
Browse files Browse the repository at this point in the history
  • Loading branch information
SMILEY4 committed Aug 3, 2024
1 parent f9546ab commit 9a3c1bb
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.github.smiley4.schemakenerator.core.annotations


/**
* Specifies that the annotated object is optional, i.e. not required.
*/
@Target(
AnnotationTarget.PROPERTY,
AnnotationTarget.FIELD,
AnnotationTarget.FUNCTION
)
@Retention(AnnotationRetention.RUNTIME)
annotation class Optional
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.github.smiley4.schemakenerator.core.annotations


/**
* Specifies that the annotated object is required.
*/
@Target(
AnnotationTarget.PROPERTY,
AnnotationTarget.FIELD,
AnnotationTarget.FUNCTION
)
@Retention(AnnotationRetention.RUNTIME)
annotation class Required
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotati
import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationDeprecatedStep
import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationDescriptionStep
import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationExamplesStep
import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationOptionalAndRequiredStep
import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationTitleStep
import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCustomizeStep
import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaGenerationStep
Expand Down Expand Up @@ -62,10 +63,11 @@ fun Bundle<JsonSchema>.withAutoTitle(type: TitleType = TitleType.FULL): Bundle<J

/**
* See [JsonSchemaCoreAnnotationDefaultStep], [JsonSchemaCoreAnnotationDeprecatedStep], [JsonSchemaCoreAnnotationDescriptionStep],
* [JsonSchemaCoreAnnotationExamplesStep], [JsonSchemaCoreAnnotationTitleStep]
* [JsonSchemaCoreAnnotationExamplesStep], [JsonSchemaCoreAnnotationTitleStep], [JsonSchemaCoreAnnotationOptionalAndRequiredStep]
*/
fun Bundle<JsonSchema>.handleCoreAnnotations(): Bundle<JsonSchema> {
return this
.let { JsonSchemaCoreAnnotationOptionalAndRequiredStep().process(this) }
.let { JsonSchemaCoreAnnotationDefaultStep().process(this) }
.let { JsonSchemaCoreAnnotationDeprecatedStep().process(this) }
.let { JsonSchemaCoreAnnotationDescriptionStep().process(this) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.github.smiley4.schemakenerator.jsonschema.steps

import io.github.smiley4.schemakenerator.core.annotations.Optional
import io.github.smiley4.schemakenerator.core.annotations.Required
import io.github.smiley4.schemakenerator.core.data.Bundle
import io.github.smiley4.schemakenerator.core.data.PropertyData
import io.github.smiley4.schemakenerator.jsonschema.data.JsonSchema
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonArray
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonNode
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonObject
import io.github.smiley4.schemakenerator.jsonschema.jsonDsl.JsonTextValue
import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaAnnotationUtils.iterateProperties

/**
* Sets properties as optional/required from core [Optional] and [Required]-annotation.
*/
class JsonSchemaCoreAnnotationOptionalAndRequiredStep {

fun process(bundle: Bundle<JsonSchema>): Bundle<JsonSchema> {
return bundle.also { schema ->
process(schema.data)
schema.supporting.forEach { process(it) }
}
}

private fun process(schema: JsonSchema) {
iterateProperties(schema) { prop, data ->
determineRequired(data)?.also { required ->
if (required) {
addRequired(schema, data.name)
} else {
removeRequired(schema, data.name)
}
}
}
}

private fun determineRequired(typeData: PropertyData): Boolean? {
if (typeData.annotations.any { it.name == Required::class.qualifiedName }) {
return true
}
if (typeData.annotations.any { it.name == Optional::class.qualifiedName }) {
return false
}
return null
}

private fun getRequiredList(schema: JsonSchema): MutableList<JsonNode> {
val json = schema.json
if (json is JsonObject) {
val required = json.properties["required"]
if (required is JsonArray) {
return required.items
}
}
return mutableListOf()
}

private fun addRequired(schema: JsonSchema, propertyName: String) {
val list = getRequiredList(schema)
if (list.none { (it as JsonTextValue).value == propertyName }) {
list.add(JsonTextValue(propertyName))
}
}

private fun removeRequired(schema: JsonSchema, propertyName: String) {
val list = getRequiredList(schema)
list.removeIf { (it as JsonTextValue).value == propertyName }
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotati
import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationDeprecatedStep
import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationDescriptionStep
import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationExamplesStep
import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationOptionalAndRequiredStep
import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCoreAnnotationTitleStep
import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaCustomizeStep
import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaGenerationStep
Expand Down Expand Up @@ -64,10 +65,11 @@ fun Bundle<SwaggerSchema>.withAutoTitle(type: TitleType = TitleType.FULL): Bundl

/**
* See [SwaggerSchemaCoreAnnotationDefaultStep], [SwaggerSchemaCoreAnnotationDeprecatedStep], [SwaggerSchemaCoreAnnotationDescriptionStep],
* [SwaggerSchemaCoreAnnotationExamplesStep], [SwaggerSchemaCoreAnnotationTitleStep]
* [SwaggerSchemaCoreAnnotationExamplesStep], [SwaggerSchemaCoreAnnotationTitleStep], [SwaggerSchemaCoreAnnotationOptionalAndRequiredStep]
*/
fun Bundle<SwaggerSchema>.handleCoreAnnotations(): Bundle<SwaggerSchema> {
return this
.let { SwaggerSchemaCoreAnnotationOptionalAndRequiredStep().process(this) }
.let { SwaggerSchemaCoreAnnotationDefaultStep().process(this) }
.let { SwaggerSchemaCoreAnnotationDeprecatedStep().process(this) }
.let { SwaggerSchemaCoreAnnotationDescriptionStep().process(this) }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package io.github.smiley4.schemakenerator.swagger.steps

import io.github.smiley4.schemakenerator.core.annotations.Optional
import io.github.smiley4.schemakenerator.core.annotations.Required
import io.github.smiley4.schemakenerator.core.data.Bundle
import io.github.smiley4.schemakenerator.core.data.PropertyData
import io.github.smiley4.schemakenerator.swagger.data.SwaggerSchema
import io.github.smiley4.schemakenerator.swagger.steps.SwaggerSchemaAnnotationUtils.iterateProperties

/**
* Sets properties as optional/required from core [Optional] and [Required]-annotation.
*/
class SwaggerSchemaCoreAnnotationOptionalAndRequiredStep {

fun process(bundle: Bundle<SwaggerSchema>): Bundle<SwaggerSchema> {
return bundle.also { schema ->
process(schema.data)
schema.supporting.forEach { process(it) }
}
}

private fun process(schema: SwaggerSchema) {
iterateProperties(schema) { prop, data ->
determineRequired(data)?.also { required ->
if (required) {
addRequired(schema, data.name)
} else {
removeRequired(schema, data.name)
}
}
}
}

private fun determineRequired(typeData: PropertyData): Boolean? {
if (typeData.annotations.any { it.name == Required::class.qualifiedName }) {
return true
}
if (typeData.annotations.any { it.name == Optional::class.qualifiedName }) {
return false
}
return null
}

private fun getRequiredList(schema: SwaggerSchema): MutableList<String> {
return schema.swagger.required ?: mutableListOf()
}

private fun addRequired(schema: SwaggerSchema, propertyName: String) {
val list = getRequiredList(schema)
if (list.none { it == propertyName }) {
list.add(propertyName)
}
}

private fun removeRequired(schema: SwaggerSchema, propertyName: String) {
val list = getRequiredList(schema)
list.removeIf { it == propertyName }
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ package io.github.smiley4.schemakenerator.test

import com.fasterxml.jackson.annotation.JsonInclude
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import io.github.smiley4.schemakenerator.core.annotations.Optional
import io.github.smiley4.schemakenerator.core.data.AnnotationData
import io.github.smiley4.schemakenerator.core.data.BaseTypeData
import io.github.smiley4.schemakenerator.core.data.Bundle
import io.github.smiley4.schemakenerator.core.data.PrimitiveTypeData
import io.github.smiley4.schemakenerator.core.data.TypeId
import io.github.smiley4.schemakenerator.jsonschema.OptionalHandling
import io.github.smiley4.schemakenerator.jsonschema.compileInlining
import io.github.smiley4.schemakenerator.jsonschema.generateJsonSchema
import io.github.smiley4.schemakenerator.jsonschema.steps.JsonSchemaCoreAnnotationOptionalAndRequiredStep
import io.github.smiley4.schemakenerator.reflection.processReflection
import io.github.smiley4.schemakenerator.serialization.processKotlinxSerialization
import io.github.smiley4.schemakenerator.swagger.compileInlining
Expand Down Expand Up @@ -123,13 +124,8 @@ class Test : StringSpec({

private val json = jacksonObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL).writerWithDefaultPrettyPrinter()!!

class ClassWithOptionals(
val ctorRequired: String,
val ctorOptional: String = "test",
val ctorRequiredNullable: String?,
val ctorOptionalNullable: String? = null
) {
val propOptional: String = "hello"
class ClassWithOptionals {
val propOptional: String = ""
}

}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.github.smiley4.schemakenerator.test.models.reflection

class ClassWithOptionalParameters(
val ctorRequired: String,
val ctorOptional: String = "test",
val ctorRequiredNullable: String?,
val ctorOptionalNullable: String? = null
val someList: List<String>,
val someSet: Set<String>,
val someMap: Map<String, Int>,
val someArray: IntArray,
)

0 comments on commit 9a3c1bb

Please sign in to comment.