From 24fcc5be572b2382e2e5908804f4585865415a54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Fa=C5=82drowicz?= Date: Mon, 9 Sep 2024 13:55:37 +0200 Subject: [PATCH] workaround for problem with list of objects with optional fields in enricher's parameters --- .../extractor/ParametersExtractor.scala | 2 +- .../engine/json/swagger/SwaggerTyped.scala | 81 ++++++++++--------- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/components/openapi/src/main/scala/pl/touk/nussknacker/openapi/extractor/ParametersExtractor.scala b/components/openapi/src/main/scala/pl/touk/nussknacker/openapi/extractor/ParametersExtractor.scala index 4d1a7121ea8..38f2253685d 100644 --- a/components/openapi/src/main/scala/pl/touk/nussknacker/openapi/extractor/ParametersExtractor.scala +++ b/components/openapi/src/main/scala/pl/touk/nussknacker/openapi/extractor/ParametersExtractor.scala @@ -43,7 +43,7 @@ object ParametersExtractor { ParameterWithBodyFlag( Parameter( ParameterName(propertyName), - swaggerType.typingResult, + SwaggerTyped.typingResult(swaggerType, resolveListOfObjects = false), editor = swaggerType.editorOpt, validators = List.empty, defaultValue = None, diff --git a/utils/json-utils/src/main/scala/pl/touk/nussknacker/engine/json/swagger/SwaggerTyped.scala b/utils/json-utils/src/main/scala/pl/touk/nussknacker/engine/json/swagger/SwaggerTyped.scala index b405b9d1f19..cc876093962 100644 --- a/utils/json-utils/src/main/scala/pl/touk/nussknacker/engine/json/swagger/SwaggerTyped.scala +++ b/utils/json-utils/src/main/scala/pl/touk/nussknacker/engine/json/swagger/SwaggerTyped.scala @@ -173,44 +173,51 @@ object SwaggerTyped { Option(schema.getType) .orElse(Option(schema.getTypes).map(_.asScala.head)) - def typingResult(swaggerTyped: SwaggerTyped): TypingResult = swaggerTyped match { - case SwaggerObject(elementType, additionalProperties, patternProperties) => - handleSwaggerObject(elementType, additionalProperties, patternProperties) - case SwaggerArray(ofType) => - Typed.genericTypeClass(classOf[java.util.List[_]], List(typingResult(ofType))) - case SwaggerEnum(values) => - Typed.fromIterableOrUnknownIfEmpty(values.map(Typed.fromInstance)) - case SwaggerBool => - Typed.typedClass[java.lang.Boolean] - case SwaggerString => - Typed.typedClass[String] - case SwaggerInteger => - Typed.typedClass[java.lang.Integer] - case SwaggerLong => - Typed.typedClass[java.lang.Long] - case SwaggerBigInteger => - Typed.typedClass[java.math.BigInteger] - case SwaggerDouble => - Typed.typedClass[java.lang.Double] - case SwaggerBigDecimal => - Typed.typedClass[java.math.BigDecimal] - case SwaggerDateTime => - Typed.typedClass[ZonedDateTime] - case SwaggerDate => - Typed.typedClass[LocalDate] - case SwaggerTime => - Typed.typedClass[LocalTime] - case SwaggerUnion(types) => Typed.fromIterableOrUnknownIfEmpty(types.map(typingResult)) - case SwaggerAny => - Unknown - case SwaggerNull => - TypedNull - } + // `resolveListOfObjects` flag allows one to stop resolving Type recursion for SwaggerArray[SwaggerObject] + // this is needed for correct validations in openApi enrichers with input parameters that contains list of objects with optional fields + // TODO: validations in openApi enrichers should be based on actual schema instead of `TypingResult` instance + def typingResult(swaggerTyped: SwaggerTyped, resolveListOfObjects: Boolean = true): TypingResult = + swaggerTyped match { + case SwaggerObject(elementType, additionalProperties, patternProperties) => + handleSwaggerObject(elementType, additionalProperties, patternProperties, resolveListOfObjects) + case SwaggerArray(SwaggerObject(_, _, _)) if !resolveListOfObjects => + Typed.genericTypeClass(classOf[java.util.List[_]], List(Unknown)) + case SwaggerArray(ofType) => + Typed.genericTypeClass(classOf[java.util.List[_]], List(typingResult(ofType, resolveListOfObjects))) + case SwaggerEnum(values) => + Typed.fromIterableOrUnknownIfEmpty(values.map(Typed.fromInstance)) + case SwaggerBool => + Typed.typedClass[java.lang.Boolean] + case SwaggerString => + Typed.typedClass[String] + case SwaggerInteger => + Typed.typedClass[java.lang.Integer] + case SwaggerLong => + Typed.typedClass[java.lang.Long] + case SwaggerBigInteger => + Typed.typedClass[java.math.BigInteger] + case SwaggerDouble => + Typed.typedClass[java.lang.Double] + case SwaggerBigDecimal => + Typed.typedClass[java.math.BigDecimal] + case SwaggerDateTime => + Typed.typedClass[ZonedDateTime] + case SwaggerDate => + Typed.typedClass[LocalDate] + case SwaggerTime => + Typed.typedClass[LocalTime] + case SwaggerUnion(types) => Typed.fromIterableOrUnknownIfEmpty(types.map(typingResult(_, resolveListOfObjects))) + case SwaggerAny => + Unknown + case SwaggerNull => + TypedNull + } private def handleSwaggerObject( elementType: Map[PropertyName, SwaggerTyped], additionalProperties: AdditionalProperties, - patternProperties: List[PatternWithSwaggerTyped] + patternProperties: List[PatternWithSwaggerTyped], + resolveListOfObject: Boolean = true ): TypingResult = { import pl.touk.nussknacker.engine.util.Implicits.RichScalaMap def typedStringKeyMap(valueType: TypingResult) = { @@ -218,7 +225,7 @@ object SwaggerTyped { } if (elementType.isEmpty) { val patternPropertiesTypesSet = patternProperties.map { case PatternWithSwaggerTyped(_, propertySwaggerTyped) => - typingResult(propertySwaggerTyped) + typingResult(propertySwaggerTyped, resolveListOfObject) } additionalProperties match { case AdditionalPropertiesDisabled if patternPropertiesTypesSet.isEmpty => @@ -226,10 +233,10 @@ object SwaggerTyped { case AdditionalPropertiesDisabled => typedStringKeyMap(Typed.fromIterableOrUnknownIfEmpty(patternPropertiesTypesSet)) case AdditionalPropertiesEnabled(value) => - typedStringKeyMap(Typed(NonEmptyList(typingResult(value), patternPropertiesTypesSet))) + typedStringKeyMap(Typed(NonEmptyList(typingResult(value, resolveListOfObject), patternPropertiesTypesSet))) } } else { - Typed.record(elementType.mapValuesNow(typingResult)) + Typed.record(elementType.mapValuesNow(typingResult(_, resolveListOfObject))) } }