From a73d1c77e70f08a1798b1d94dad768286c8ff4ae Mon Sep 17 00:00:00 2001 From: Romain Pouclet Date: Fri, 20 Dec 2024 19:28:54 -0800 Subject: [PATCH 1/3] Add example for internalVisibility test case --- .../examples/internalVisibility/api.yaml | 70 +++++++++++++++++++ .../internalVisibility/controllers/Dummy.kt | 0 .../internalVisibility/models/Dummy.kt | 0 .../internalVisibility/models/SomeObj.kt | 13 ++++ .../internalVisibility/models/State.kt | 14 ++++ .../internalVisibility/models/StateA.kt | 11 +++ .../internalVisibility/models/StateB.kt | 14 ++++ .../internalVisibility/models/StateBMode.kt | 20 ++++++ .../internalVisibility/models/Status.kt | 20 ++++++ 9 files changed, 162 insertions(+) create mode 100644 src/test/resources/examples/internalVisibility/api.yaml create mode 100644 src/test/resources/examples/internalVisibility/controllers/Dummy.kt create mode 100644 src/test/resources/examples/internalVisibility/models/Dummy.kt create mode 100644 src/test/resources/examples/internalVisibility/models/SomeObj.kt create mode 100644 src/test/resources/examples/internalVisibility/models/State.kt create mode 100644 src/test/resources/examples/internalVisibility/models/StateA.kt create mode 100644 src/test/resources/examples/internalVisibility/models/StateB.kt create mode 100644 src/test/resources/examples/internalVisibility/models/StateBMode.kt create mode 100644 src/test/resources/examples/internalVisibility/models/Status.kt diff --git a/src/test/resources/examples/internalVisibility/api.yaml b/src/test/resources/examples/internalVisibility/api.yaml new file mode 100644 index 00000000..89e64e76 --- /dev/null +++ b/src/test/resources/examples/internalVisibility/api.yaml @@ -0,0 +1,70 @@ +openapi: 3.0.0 +info: +paths: + /some: + post: + operationId: sendSomeObject + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/SomeObj" + responses: + 200: + content: + application/json: + schema: + $ref: "#/components/schemas/SomeObj" + get: + operationId: getSomeObject + responses: + 200: + description: Success response + content: + application/json: + schema: + $ref: "#/components/schemas/SomeObj" + +components: + schemas: + SomeObj: + type: object + required: + - state + properties: + state: + $ref: "#/components/schemas/State" + State: + oneOf: + - $ref: "#/components/schemas/StateA" + - $ref: "#/components/schemas/StateB" + discriminator: + propertyName: status + mapping: + a: "#/components/schemas/StateA" + b: "#/components/schemas/StateB" + Status: + type: string + enum: + - a + - b + StateA: + type: object + required: + - status + properties: + status: + $ref: "#/components/schemas/Status" + StateB: + type: object + required: + - status + - mode + properties: + status: + $ref: "#/components/schemas/Status" + mode: + type: string + enum: + - mode1 + - mode2 diff --git a/src/test/resources/examples/internalVisibility/controllers/Dummy.kt b/src/test/resources/examples/internalVisibility/controllers/Dummy.kt new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/examples/internalVisibility/models/Dummy.kt b/src/test/resources/examples/internalVisibility/models/Dummy.kt new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/examples/internalVisibility/models/SomeObj.kt b/src/test/resources/examples/internalVisibility/models/SomeObj.kt new file mode 100644 index 00000000..e7fb8351 --- /dev/null +++ b/src/test/resources/examples/internalVisibility/models/SomeObj.kt @@ -0,0 +1,13 @@ +package examples.internalVisibility.models + +import com.fasterxml.jackson.`annotation`.JsonProperty +import javax.validation.Valid +import javax.validation.constraints.NotNull + +internal class SomeObj( + @param:JsonProperty("state") + @get:JsonProperty("state") + @get:NotNull + @get:Valid + internal val state: State, +) diff --git a/src/test/resources/examples/internalVisibility/models/State.kt b/src/test/resources/examples/internalVisibility/models/State.kt new file mode 100644 index 00000000..5397a5df --- /dev/null +++ b/src/test/resources/examples/internalVisibility/models/State.kt @@ -0,0 +1,14 @@ +package examples.internalVisibility.models + +import com.fasterxml.jackson.`annotation`.JsonSubTypes +import com.fasterxml.jackson.`annotation`.JsonTypeInfo + +@JsonTypeInfo( + use = JsonTypeInfo.Id.NAME, + include = JsonTypeInfo.As.EXISTING_PROPERTY, + property = "status", + visible = true, +) +@JsonSubTypes(JsonSubTypes.Type(value = StateA::class, name = "a"),JsonSubTypes.Type(value = + StateB::class, name = "b")) +internal sealed interface State diff --git a/src/test/resources/examples/internalVisibility/models/StateA.kt b/src/test/resources/examples/internalVisibility/models/StateA.kt new file mode 100644 index 00000000..9d762d07 --- /dev/null +++ b/src/test/resources/examples/internalVisibility/models/StateA.kt @@ -0,0 +1,11 @@ +package examples.internalVisibility.models + +import com.fasterxml.jackson.`annotation`.JsonProperty +import javax.validation.constraints.NotNull + +internal class StateA( + @get:JsonProperty("status") + @get:NotNull + @param:JsonProperty("status") + internal val status: Status = Status.A, +) : State diff --git a/src/test/resources/examples/internalVisibility/models/StateB.kt b/src/test/resources/examples/internalVisibility/models/StateB.kt new file mode 100644 index 00000000..d211dc64 --- /dev/null +++ b/src/test/resources/examples/internalVisibility/models/StateB.kt @@ -0,0 +1,14 @@ +package examples.internalVisibility.models + +import com.fasterxml.jackson.`annotation`.JsonProperty +import javax.validation.constraints.NotNull + +internal class StateB( + @get:JsonProperty("mode") + @get:NotNull + internal val mode: StateBMode, + @get:JsonProperty("status") + @get:NotNull + @param:JsonProperty("status") + internal val status: Status = Status.B, +) : State diff --git a/src/test/resources/examples/internalVisibility/models/StateBMode.kt b/src/test/resources/examples/internalVisibility/models/StateBMode.kt new file mode 100644 index 00000000..e3d49790 --- /dev/null +++ b/src/test/resources/examples/internalVisibility/models/StateBMode.kt @@ -0,0 +1,20 @@ +package examples.internalVisibility.models + +import com.fasterxml.jackson.`annotation`.JsonValue +import kotlin.String +import kotlin.collections.Map + +internal enum class StateBMode( + @JsonValue + internal val `value`: String, +) { + MODE1("mode1"), + MODE2("mode2"), + ; + + internal companion object { + private val mapping: Map = entries.associateBy(StateBMode::value) + + internal fun fromValue(`value`: String): StateBMode? = mapping[value] + } +} diff --git a/src/test/resources/examples/internalVisibility/models/Status.kt b/src/test/resources/examples/internalVisibility/models/Status.kt new file mode 100644 index 00000000..b2bab2fa --- /dev/null +++ b/src/test/resources/examples/internalVisibility/models/Status.kt @@ -0,0 +1,20 @@ +package examples.internalVisibility.models + +import com.fasterxml.jackson.`annotation`.JsonValue +import kotlin.String +import kotlin.collections.Map + +internal enum class Status( + @JsonValue + internal val `value`: String, +) { + A("a"), + B("b"), + ; + + internal companion object { + private val mapping: Map = entries.associateBy(Status::value) + + internal fun fromValue(`value`: String): Status? = mapping[value] + } +} From 4654062ddcf432c0e9a3b1f3622d4905f6409a9b Mon Sep 17 00:00:00 2001 From: Romain Pouclet Date: Fri, 20 Dec 2024 19:30:27 -0800 Subject: [PATCH 2/3] Update model generator to set model visibility --- .../com/cjbooms/fabrikt/cli/CodeGenOptions.kt | 5 +++ .../fabrikt/generators/MutableSettings.kt | 6 ++- .../fabrikt/generators/PropertyUtils.kt | 7 ++++ .../generators/model/ModelGenerator.kt | 40 +++++++++++++++++++ .../fabrikt/generators/ModelGeneratorTest.kt | 7 +++- 5 files changed, 63 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt b/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt index e0f45258..2c4264f7 100644 --- a/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt +++ b/src/main/kotlin/com/cjbooms/fabrikt/cli/CodeGenOptions.kt @@ -96,3 +96,8 @@ enum class SerializationLibrary(val description: String, val serializationAnnota override fun toString() = "`${super.toString()}` - $description" } + +enum class MemberVisibility(val description: String) { + PUBLIC("Generate models with public visibility"), + INTERNAL("Generate models with internal visibility") +} \ No newline at end of file diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/MutableSettings.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/MutableSettings.kt index 2b2e1929..9e9a151a 100644 --- a/src/main/kotlin/com/cjbooms/fabrikt/generators/MutableSettings.kt +++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/MutableSettings.kt @@ -14,6 +14,7 @@ object MutableSettings { private lateinit var validationLibrary: ValidationLibrary private lateinit var externalRefResolutionMode: ExternalReferencesResolutionMode private lateinit var serializationLibrary: SerializationLibrary + private lateinit var memberVisibility: MemberVisibility fun updateSettings( genTypes: Set = emptySet(), @@ -27,6 +28,7 @@ object MutableSettings { validationLibrary: ValidationLibrary = ValidationLibrary.JAVAX_VALIDATION, externalRefResolutionMode: ExternalReferencesResolutionMode = ExternalReferencesResolutionMode.TARGETED, serializationLibrary: SerializationLibrary = SerializationLibrary.JACKSON, + generatedMemberVisibility: MemberVisibility = MemberVisibility.PUBLIC ) { this.generationTypes = genTypes.toMutableSet() this.controllerOptions = controllerOptions.toMutableSet() @@ -39,11 +41,12 @@ object MutableSettings { this.validationLibrary = validationLibrary this.externalRefResolutionMode = externalRefResolutionMode this.serializationLibrary = serializationLibrary + this.memberVisibility = generatedMemberVisibility } fun addOption(option: ModelCodeGenOptionType) = modelOptions.add(option) fun addOption(override: CodeGenTypeOverride) = typeOverrides.add(override) - + fun addOption(visibility: MemberVisibility) { memberVisibility = visibility } fun generationTypes() = this.generationTypes.toSet() fun controllerOptions() = this.controllerOptions.toSet() fun controllerTarget() = this.controllerTarget @@ -55,4 +58,5 @@ object MutableSettings { fun validationLibrary() = this.validationLibrary fun externalRefResolutionMode() = this.externalRefResolutionMode fun serializationLibrary() = this.serializationLibrary + fun memberVisibility() = this.memberVisibility } diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/PropertyUtils.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/PropertyUtils.kt index 3ef4a4e9..9241f244 100644 --- a/src/main/kotlin/com/cjbooms/fabrikt/generators/PropertyUtils.kt +++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/PropertyUtils.kt @@ -1,5 +1,6 @@ package com.cjbooms.fabrikt.generators +import com.cjbooms.fabrikt.cli.MemberVisibility import com.cjbooms.fabrikt.generators.TypeFactory.maybeMakeMapValueNullable import com.cjbooms.fabrikt.generators.model.JacksonMetadata import com.cjbooms.fabrikt.model.SerializationAnnotations @@ -43,6 +44,7 @@ object PropertyUtils { classSettings: ClassSettings = ClassSettings(ClassSettings.PolymorphyType.NONE), validationAnnotations: ValidationAnnotations = JavaxValidationAnnotations, serializationAnnotations: SerializationAnnotations = JacksonAnnotations, + memberVisibility: MemberVisibility ) { if (this.typeInfo is KotlinTypeInfo.UntypedObject && !serializationAnnotations.supportsAdditionalProperties) throw UnsupportedOperationException("Untyped objects not supported by selected serialization library (${this.oasKey}: ${this.schema})") @@ -169,6 +171,11 @@ object PropertyUtils { } } + when (memberVisibility) { + MemberVisibility.PUBLIC -> property.addModifiers(KModifier.PUBLIC) + MemberVisibility.INTERNAL -> property.addModifiers(KModifier.INTERNAL) + } + classBuilder.addProperty(property.build()) } diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/model/ModelGenerator.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/model/ModelGenerator.kt index 987ef1f0..f16aff26 100644 --- a/src/main/kotlin/com/cjbooms/fabrikt/generators/model/ModelGenerator.kt +++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/model/ModelGenerator.kt @@ -1,6 +1,7 @@ package com.cjbooms.fabrikt.generators.model import com.cjbooms.fabrikt.cli.ExternalReferencesResolutionMode +import com.cjbooms.fabrikt.cli.MemberVisibility import com.cjbooms.fabrikt.cli.ModelCodeGenOptionType import com.cjbooms.fabrikt.cli.ModelCodeGenOptionType.SEALED_INTERFACES_FOR_ONE_OF import com.cjbooms.fabrikt.configurations.Packages @@ -76,6 +77,7 @@ class ModelGenerator( private val validationAnnotations: ValidationAnnotations = MutableSettings.validationLibrary().annotations private val serializationAnnotations: SerializationAnnotations = MutableSettings.serializationLibrary().serializationAnnotations private val externalRefResolutionMode: ExternalReferencesResolutionMode = MutableSettings.externalRefResolutionMode() + private val memberVisibility = MutableSettings.memberVisibility() companion object { fun toModelType(basePackage: String, typeInfo: KotlinTypeInfo, isNullable: Boolean = false): TypeName { @@ -400,6 +402,7 @@ class ModelGenerator( .addQuarkusReflectionAnnotation() .addMicronautIntrospectedAnnotation() .addMicronautReflectionAnnotation() + .addVisibility() enum.entries.forEach { val enumConstantBuilder = TypeSpec.anonymousClassBuilder() @@ -412,6 +415,7 @@ class ModelGenerator( } val valuePropSpecBuilder = PropertySpec.builder("value", String::class).initializer("value") + .addVisibility() serializationAnnotations.addEnumPropertyAnnotation(valuePropSpecBuilder) classBuilder.addProperty(valuePropSpecBuilder.build()) @@ -425,10 +429,12 @@ class ModelGenerator( .addFunction( FunSpec.builder("fromValue") .addParameter(ParameterSpec.builder("value", String::class).build()) + .addVisibility() .returns(enumType.copy(nullable = true)) .addStatement("return mapping[value]") .build(), ) + .addVisibility() .build() return classBuilder.addType(companion).build() @@ -469,6 +475,8 @@ class ModelGenerator( .addMicronautIntrospectedAnnotation() .addMicronautReflectionAnnotation() .addCompanionObject() + .addVisibility() + for (oneOfInterface in oneOfInterfaces) { classBuilder .addSuperinterface(generatedType(packages.base, ModelNameRegistry.getOrRegister(oneOfInterface))) @@ -570,6 +578,7 @@ class ModelGenerator( .addQuarkusReflectionAnnotation() .addMicronautIntrospectedAnnotation() .addMicronautReflectionAnnotation() + .addVisibility() return interfaceBuilder.build() } @@ -720,6 +729,7 @@ class ModelGenerator( classSettings = classType, validationAnnotations = validationAnnotations, serializationAnnotations = serializationAnnotations, + memberVisibility = memberVisibility ) } if (constructorBuilder.parameters.isNotEmpty() && classBuilder.modifiers.isEmpty()) { @@ -786,6 +796,14 @@ class ModelGenerator( return this } + private fun TypeSpec.Builder.addVisibility(): TypeSpec.Builder { + when (memberVisibility) { + MemberVisibility.PUBLIC -> this.addModifiers(KModifier.PUBLIC) + MemberVisibility.INTERNAL -> this.addModifiers(KModifier.INTERNAL) + } + return this + } + private fun TypeSpec.Builder.addOptionalAnnotation( optionType: ModelCodeGenOptionType, type: ClassName, @@ -804,4 +822,26 @@ class ModelGenerator( ExternalReferencesResolutionMode.TARGETED -> this.filter { apiSchema -> externalReferences.value.contains(apiSchema.name) } else -> this } + + private fun PropertySpec.Builder.addVisibility(): PropertySpec.Builder { + when (memberVisibility) { + MemberVisibility.PUBLIC -> this.addModifiers(KModifier.PUBLIC) + MemberVisibility.INTERNAL -> this.addModifiers(KModifier.INTERNAL) + } + + return this + } + + private fun FunSpec.Builder.addVisibility(): FunSpec.Builder { + when (memberVisibility) { + MemberVisibility.PUBLIC -> this.addModifiers(KModifier.PUBLIC) + MemberVisibility.INTERNAL -> this.addModifiers(KModifier.INTERNAL) + } + + return this + } + } + + + diff --git a/src/test/kotlin/com/cjbooms/fabrikt/generators/ModelGeneratorTest.kt b/src/test/kotlin/com/cjbooms/fabrikt/generators/ModelGeneratorTest.kt index f51a90d9..e39b75e8 100644 --- a/src/test/kotlin/com/cjbooms/fabrikt/generators/ModelGeneratorTest.kt +++ b/src/test/kotlin/com/cjbooms/fabrikt/generators/ModelGeneratorTest.kt @@ -3,6 +3,7 @@ package com.cjbooms.fabrikt.generators import com.beust.jcommander.ParameterException import com.cjbooms.fabrikt.cli.CodeGenTypeOverride import com.cjbooms.fabrikt.cli.CodeGenerationType +import com.cjbooms.fabrikt.cli.MemberVisibility import com.cjbooms.fabrikt.cli.ModelCodeGenOptionType import com.cjbooms.fabrikt.cli.ValidationLibrary import com.cjbooms.fabrikt.configurations.Packages @@ -67,6 +68,7 @@ class ModelGeneratorTest { "oneOfMarkerInterface", "byteArrayStream", "untypedObject", + "internalVisibility" ) @BeforeEach @@ -89,7 +91,7 @@ class ModelGeneratorTest { if (testCaseName == "instantDateTime") { MutableSettings.addOption(CodeGenTypeOverride.DATETIME_AS_INSTANT) } - if (testCaseName == "discriminatedOneOf" || testCaseName == "oneOfMarkerInterface") { + if (testCaseName == "discriminatedOneOf" || testCaseName == "oneOfMarkerInterface" || testCaseName == "internalVisibility") { MutableSettings.addOption(ModelCodeGenOptionType.SEALED_INTERFACES_FOR_ONE_OF) } if (testCaseName == "mapExamplesNonNullValues") { @@ -98,6 +100,9 @@ class ModelGeneratorTest { if (testCaseName == "byteArrayStream") { MutableSettings.addOption(CodeGenTypeOverride.BYTEARRAY_AS_INPUTSTREAM) } + if (testCaseName == "internalVisibility") { + MutableSettings.addOption(MemberVisibility.INTERNAL) + } val basePackage = "examples.${testCaseName.replace("/", ".")}" val apiLocation = javaClass.getResource("/examples/$testCaseName/api.yaml")!! val sourceApi = SourceApi(apiLocation.readText(), baseDir = Paths.get(apiLocation.toURI())) From bb44cbd35755bec489868f84d661d7c4c5b676d7 Mon Sep 17 00:00:00 2001 From: Romain Pouclet Date: Fri, 20 Dec 2024 19:48:51 -0800 Subject: [PATCH 3/3] Only set internal annotation --- .../kotlin/com/cjbooms/fabrikt/generators/PropertyUtils.kt | 5 ++--- .../com/cjbooms/fabrikt/generators/model/ModelGenerator.kt | 6 +++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/PropertyUtils.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/PropertyUtils.kt index 9241f244..83d9b0d9 100644 --- a/src/main/kotlin/com/cjbooms/fabrikt/generators/PropertyUtils.kt +++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/PropertyUtils.kt @@ -171,9 +171,8 @@ object PropertyUtils { } } - when (memberVisibility) { - MemberVisibility.PUBLIC -> property.addModifiers(KModifier.PUBLIC) - MemberVisibility.INTERNAL -> property.addModifiers(KModifier.INTERNAL) + if (memberVisibility == MemberVisibility.INTERNAL) { + property.addModifiers(KModifier.INTERNAL) } classBuilder.addProperty(property.build()) diff --git a/src/main/kotlin/com/cjbooms/fabrikt/generators/model/ModelGenerator.kt b/src/main/kotlin/com/cjbooms/fabrikt/generators/model/ModelGenerator.kt index f16aff26..e8a7639e 100644 --- a/src/main/kotlin/com/cjbooms/fabrikt/generators/model/ModelGenerator.kt +++ b/src/main/kotlin/com/cjbooms/fabrikt/generators/model/ModelGenerator.kt @@ -797,10 +797,10 @@ class ModelGenerator( } private fun TypeSpec.Builder.addVisibility(): TypeSpec.Builder { - when (memberVisibility) { - MemberVisibility.PUBLIC -> this.addModifiers(KModifier.PUBLIC) - MemberVisibility.INTERNAL -> this.addModifiers(KModifier.INTERNAL) + if (memberVisibility == MemberVisibility.INTERNAL) { + this.addModifiers(KModifier.INTERNAL) } + return this }