Skip to content

Commit ed1d716

Browse files
authored
Fix bug for multiple required fields without annotation (#180)
* Add reproduction scenario only the first field is marked as required * Separate code paths for `isRequiredBasedOnAnnotation` for improved readability * Prevent "None" from being set as default when type is `Option[String]` * Simplify logic branch:
1 parent eece96a commit ed1d716

File tree

4 files changed

+64
-9
lines changed

4 files changed

+64
-9
lines changed

src/main/scala/com/github/swagger/scala/converter/SwaggerScalaModelConverter.scala

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,11 @@ class SwaggerScalaModelConverter extends ModelResolver(SwaggerScalaModelConverte
132132
// default values set in annotation leads to default values set in Scala constructor being ignored
133133
maybeDefault.foreach { default =>
134134
schemaProperties.get(propertyName).foreach { property =>
135-
property.setDefault(default())
135+
val defaultValue = default()
136+
defaultValue match {
137+
case None =>
138+
case _ => property.setDefault(defaultValue)
139+
}
136140
}
137141
}
138142
}
@@ -156,20 +160,20 @@ class SwaggerScalaModelConverter extends ModelResolver(SwaggerScalaModelConverte
156160
val requiredFlag = !isOptional && !hasDefaultValue
157161
if (!requiredFlag && schema.getRequired != null && schema.getRequired.contains(propertyName)) {
158162
schema.getRequired.remove(propertyName)
159-
} else if (requiredFlag && SwaggerScalaModelConverter.isRequiredBasedOnAnnotation) {
163+
} else if (requiredFlag) {
160164
addRequiredItem(schema, propertyName)
161165
}
162166
}
163167
case annotations => {
164-
val annotationSetting = getRequiredSettings(annotations).headOption.getOrElse(false)
165-
val required = if (isOptional || SwaggerScalaModelConverter.isRequiredBasedOnAnnotation) {
166-
annotationSetting
168+
val annotationRequired = getRequiredSettings(annotations).headOption.getOrElse(false)
169+
if (SwaggerScalaModelConverter.isRequiredBasedOnAnnotation) {
170+
setRequiredBasedOnAnnotation(schema, propertyName, annotationRequired)
167171
} else {
168-
!hasDefaultValue
172+
setRequiredBasedOnType(schema, propertyName, isOptional, hasDefaultValue, annotationRequired)
169173
}
170-
if (required) addRequiredItem(schema, propertyName)
171174
}
172175
}
176+
173177
}
174178
schema
175179
}
@@ -178,6 +182,29 @@ class SwaggerScalaModelConverter extends ModelResolver(SwaggerScalaModelConverte
178182
}
179183
}
180184

185+
private def setRequiredBasedOnAnnotation(
186+
schema: Schema[_],
187+
propertyName: String,
188+
annotationSetting: Boolean
189+
): Unit = {
190+
if (annotationSetting) addRequiredItem(schema, propertyName)
191+
}
192+
193+
private def setRequiredBasedOnType(
194+
schema: Schema[_],
195+
propertyName: String,
196+
isOptional: Boolean,
197+
hasDefaultValue: Boolean,
198+
annotationSetting: Boolean
199+
): Unit = {
200+
val required = if (isOptional) {
201+
annotationSetting
202+
} else {
203+
!hasDefaultValue
204+
}
205+
if (required) addRequiredItem(schema, propertyName)
206+
}
207+
181208
private def updateTypeOnItemsSchema(primitiveType: PrimitiveType, propertySchema: Schema[_]) = {
182209
val updatedSchema = correctSchema(propertySchema.getItems, primitiveType)
183210
propertySchema.setItems(updatedSchema)

src/test/scala/com/github/swagger/scala/converter/ModelPropertyParserTest.scala

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class ModelPropertyParserTest extends AnyFlatSpec with BeforeAndAfterEach with M
169169
nullSafeSeq(model.value.getRequired) shouldBe empty
170170
}
171171

172-
it should "prioritize required as specified in annotation by default" in new PropertiesScope[ModelWOptionIntSchemaOverrideForRequired] {
172+
it should "prioritize required as specified in annotation by default" in new PropertiesScope[ModelWOptionIntSchemaOverrideForRequired](true) {
173173
val requiredIntWithDefault = model.value.getProperties.get("requiredIntWithDefault")
174174
requiredIntWithDefault shouldBe an[IntegerSchema]
175175
requiredIntWithDefault.asInstanceOf[IntegerSchema].getDefault shouldEqual 5
@@ -186,6 +186,10 @@ class ModelPropertyParserTest extends AnyFlatSpec with BeforeAndAfterEach with M
186186
val annotatedOptionalIntWithSomeDefault = model.value.getProperties.get("annotatedOptionalIntWithSomeDefault")
187187
annotatedOptionalIntWithSomeDefault shouldBe an[IntegerSchema]
188188
annotatedOptionalIntWithSomeDefault.asInstanceOf[IntegerSchema].getDefault should be(5)
189+
190+
val annotatedOptionalStringWithNoneDefault = model.value.getProperties.get("annotatedOptionalStringWithNoneDefault")
191+
annotatedOptionalStringWithNoneDefault shouldBe an[StringSchema]
192+
annotatedOptionalStringWithNoneDefault.asInstanceOf[StringSchema].getDefault should be(null)
189193
}
190194

191195
nullSafeSeq(model.value.getRequired).toSet shouldEqual Set("annotatedOptionalInt", "requiredInt")
@@ -211,11 +215,27 @@ class ModelPropertyParserTest extends AnyFlatSpec with BeforeAndAfterEach with M
211215
val annotatedOptionalIntWithSomeDefault = model.value.getProperties.get("annotatedOptionalIntWithSomeDefault")
212216
annotatedOptionalIntWithSomeDefault shouldBe an[IntegerSchema]
213217
annotatedOptionalIntWithSomeDefault.asInstanceOf[IntegerSchema].getDefault should be(5)
218+
219+
val annotatedOptionalStringWithNoneDefault = model.value.getProperties.get("annotatedOptionalStringWithNoneDefault")
220+
annotatedOptionalStringWithNoneDefault shouldBe an[StringSchema]
221+
annotatedOptionalStringWithNoneDefault.asInstanceOf[StringSchema].getDefault should be(null)
214222
}
215223

216224
nullSafeSeq(model.value.getRequired).toSet shouldEqual Set("annotatedOptionalInt", "requiredInt", "annotatedRequiredInt")
217225
}
218226

227+
it should "consider fields that aren't optional required if `requiredBasedAnnotation == true`" in new PropertiesScope[
228+
ModelWMultipleRequiredFields
229+
](
230+
requiredBasedAnnotation = false
231+
) {
232+
nullSafeSeq(model.value.getRequired).toSet shouldEqual Set("first", "second", "third")
233+
}
234+
235+
it should "consider fields that aren't optional required" in new PropertiesScope[ModelWMultipleRequiredFields]() {
236+
nullSafeSeq(model.value.getRequired).toSet shouldEqual Set("first", "second", "third")
237+
}
238+
219239
it should "process Model with Scala Option Long" in new PropertiesScope[ModelWOptionLong] {
220240
val optLong = model.value.getProperties().get("optLong")
221241
optLong should not be (null)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package models
2+
3+
case class ModelWMultipleRequiredFields(
4+
first: Double,
5+
second: Double,
6+
third: Double
7+
)

src/test/scala/models/ModelWOptionInt.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,6 @@ case class ModelWOptionIntSchemaOverrideForRequired(
3131
@Schema(description = "annotated default", defaultValue = "10") annotatedIntWithDefault: Int,
3232
@Schema(description = "should become required", required = true) annotatedOptionalInt: Option[Int],
3333
@Schema(description = "should not have default") annotatedOptionalIntWithNoneDefault: Option[Int] = None,
34-
@Schema(description = "should have default") annotatedOptionalIntWithSomeDefault: Option[Int] = Some(5)
34+
@Schema(description = "should have default") annotatedOptionalIntWithSomeDefault: Option[Int] = Some(5),
35+
@Schema(description = "should not have a default") annotatedOptionalStringWithNoneDefault: Option[String] = None
3536
)

0 commit comments

Comments
 (0)