3131import io .micronaut .core .annotation .AnnotationClassValue ;
3232import io .micronaut .core .annotation .AnnotationValue ;
3333import io .micronaut .core .annotation .Internal ;
34- import io .micronaut .core .annotation .NonNull ;
3534import io .micronaut .core .annotation .Nullable ;
3635import io .micronaut .core .beans .BeanMap ;
3736import io .micronaut .core .bind .annotation .Bindable ;
117116import java .util .OptionalLong ;
118117import java .util .Set ;
119118import java .util .UUID ;
119+ import java .util .concurrent .atomic .AtomicReference ;
120120import java .util .function .BiConsumer ;
121121import java .util .function .Function ;
122122
147147import static io .micronaut .openapi .visitor .OpenApiApplicationVisitor .resolvePlaceholders ;
148148import static io .micronaut .openapi .visitor .OpenApiConfigProperty .MICRONAUT_OPENAPI_FIELD_VISIBILITY_LEVEL ;
149149import static io .micronaut .openapi .visitor .OpenApiModelProp .DISCRIMINATOR ;
150+ import static io .micronaut .openapi .visitor .OpenApiModelProp .PROP_ACCESS ;
150151import static io .micronaut .openapi .visitor .OpenApiModelProp .PROP_ACCESS_MODE ;
151152import static io .micronaut .openapi .visitor .OpenApiModelProp .PROP_ADDITIONAL_PROPERTIES ;
152153import static io .micronaut .openapi .visitor .OpenApiModelProp .PROP_ALLOWABLE_VALUES ;
@@ -356,7 +357,8 @@ public static Schema<?> getSchemaDefinition(OpenAPI openAPI,
356357 if (type instanceof EnumElement enumEl ) {
357358 schema = setSpecVersion (new Schema <>());
358359 schema .setName (schemaName );
359- if (javadoc != null && StringUtils .hasText (javadoc .getMethodDescription ())) {
360+ processJacksonDescription (enumEl , schema );
361+ if (schema .getDescription () == null && javadoc != null && StringUtils .hasText (javadoc .getMethodDescription ())) {
360362 schema .setDescription (javadoc .getMethodDescription ());
361363 }
362364 schemas .put (schemaName , schema );
@@ -372,7 +374,8 @@ public static Schema<?> getSchemaDefinition(OpenAPI openAPI,
372374 if (schemaWithSuperTypes != null ) {
373375 schema = schemaWithSuperTypes ;
374376 }
375- if (schema != null && javadoc != null && StringUtils .hasText (javadoc .getMethodDescription ())) {
377+ processJacksonDescription (type , schema );
378+ if (schema != null && schema .getDescription () == null && javadoc != null && StringUtils .hasText (javadoc .getMethodDescription ())) {
376379 schema .setDescription (javadoc .getMethodDescription ());
377380 }
378381
@@ -432,7 +435,7 @@ public static Schema<?> getSchemaDefinition(OpenAPI openAPI,
432435 if (externalDocs != null ) {
433436 schema .setExternalDocs (externalDocs );
434437 }
435- setSchemaDocumentation (type , schema );
438+ setSchemaDescription (type , schema );
436439 var schemaRef = setSpecVersion (new Schema <>());
437440 schemaRef .set$ref (SchemaUtils .schemaRef (schema .getName ()));
438441 if (definingElement instanceof ClassElement classEl && classEl .isIterable ()) {
@@ -838,6 +841,8 @@ public static Schema<?> resolveSchema(OpenAPI openApi, @Nullable Element definin
838841 processSchemaAnn (schema , context , definingElement , type , schemaAnnotationValue );
839842 }
840843
844+ processJacksonDescription (definingElement , schema );
845+
841846 if (definingElement != null && StringUtils .isEmpty (schema .getDescription ())) {
842847 if (fieldJavadoc != null ) {
843848 if (StringUtils .hasText (fieldJavadoc .getMethodDescription ())) {
@@ -928,7 +933,7 @@ public static Schema<?> bindSchemaForElement(VisitorContext context, TypedElemen
928933 }
929934
930935 boolean notOnlyRef = false ;
931- setSchemaDocumentation (element , topLevelSchema );
936+ setSchemaDescription (element , topLevelSchema );
932937 if (StringUtils .isNotEmpty (topLevelSchema .getDescription ())) {
933938 notOnlyRef = true ;
934939 }
@@ -952,11 +957,14 @@ && isProtobufGenerated(propertyEl.getOwningType())
952957 SchemaUtils .setNullable (topLevelSchema );
953958 notOnlyRef = true ;
954959 }
955- final String defaultJacksonValue = stringValue (element , JsonProperty .class , PROP_DEFAULT_VALUE ).orElse (null );
956- if (defaultJacksonValue != null && schemaToBind .getDefault () == null ) {
957- setDefaultValueObject (topLevelSchema , defaultJacksonValue , elementType , schemaToBind .getType (), schemaToBind .getFormat (), false , context );
960+ if (processJacksonPropertyAnn (element , elementType , topLevelSchema , schemaAnn , context )) {
958961 notOnlyRef = true ;
959962 }
963+ // final String defaultJacksonValue = stringValue(element, JsonProperty.class, PROP_DEFAULT_VALUE).orElse(null);
964+ // if (defaultJacksonValue != null && schemaToBind.getDefault() == null) {
965+ // setDefaultValueObject(topLevelSchema, defaultJacksonValue, elementType, schemaToBind.getType(), schemaToBind.getFormat(), false, context);
966+ // notOnlyRef = true;
967+ // }
960968
961969 boolean addSchemaToBind = !SchemaUtils .isEmptySchema (schemaToBind );
962970
@@ -1731,37 +1739,59 @@ private static void checkAllOf(Schema<Object> composedSchema) {
17311739 composedSchema .addAllOfItem (propSchema );
17321740 }
17331741
1734- private static void setSchemaDocumentation (Element element , Schema <?> schemaToBind ) {
1735- if (StringUtils .isEmpty (schemaToBind .getDescription ())) {
1736- // First, find getter method javadoc
1737- String doc = element .getDocumentation ().orElse (null );
1738- if (StringUtils .isEmpty (doc )) {
1739- // next, find field javadoc
1740- if (element instanceof MemberElement memberEl ) {
1741- List <FieldElement > fields = memberEl .getDeclaringType ().getFields ();
1742- if (CollectionUtils .isNotEmpty (fields )) {
1743- for (FieldElement field : fields ) {
1744- if (field .getName ().equals (element .getName ())) {
1745- doc = field .getDocumentation ().orElse (null );
1746- break ;
1747- }
1742+ private static void processJacksonDescription (@ Nullable Element element , @ Nullable Schema <?> schemaToBind ) {
1743+ if (element == null || schemaToBind == null || StringUtils .isNotEmpty (schemaToBind .getDescription ())) {
1744+ return ;
1745+ }
1746+ findAnnotation (element , element instanceof ClassElement
1747+ ? "com.fasterxml.jackson.annotation.JsonClassDescription"
1748+ : "com.fasterxml.jackson.annotation.JsonPropertyDescription"
1749+ )
1750+ .flatMap (ann -> ann .stringValue (PROP_VALUE ))
1751+ .ifPresent (schemaToBind ::setDescription );
1752+ }
1753+
1754+ private static void setSchemaDescription (Element element , Schema <?> schemaToBind ) {
1755+ if (StringUtils .isNotEmpty (schemaToBind .getDescription ())) {
1756+ return ;
1757+ }
1758+
1759+ processJacksonDescription (element , schemaToBind );
1760+ if (StringUtils .isNotEmpty (schemaToBind .getDescription ())) {
1761+ return ;
1762+ }
1763+
1764+ // First, find getter method javadoc
1765+ String doc = element .getDocumentation ().orElse (null );
1766+ if (StringUtils .isEmpty (doc )) {
1767+ // next, find field javadoc
1768+ if (element instanceof MemberElement memberEl ) {
1769+ List <FieldElement > fields = memberEl .getDeclaringType ().getFields ();
1770+ if (CollectionUtils .isNotEmpty (fields )) {
1771+ for (FieldElement field : fields ) {
1772+ if (field .getName ().equals (element .getName ())) {
1773+ doc = field .getDocumentation ().orElse (null );
1774+ break ;
17481775 }
17491776 }
17501777 }
17511778 }
1752- if ( doc != null ) {
1753- JavadocDescription desc = Utils . getJavadocParser (). parse ( doc );
1754- if ( StringUtils . hasText ( desc . getMethodDescription ())) {
1755- schemaToBind . setDescription (desc .getMethodDescription ());
1756- }
1779+ }
1780+ if ( doc != null ) {
1781+ JavadocDescription desc = Utils . getJavadocParser (). parse ( doc );
1782+ if ( StringUtils . hasText (desc .getMethodDescription ())) {
1783+ schemaToBind . setDescription ( desc . getMethodDescription ());
17571784 }
17581785 }
17591786 }
17601787
17611788 private static void processSchemaAnn (Schema schemaToBind , VisitorContext context , Element element ,
17621789 @ Nullable ClassElement classEl ,
1763- @ NonNull AnnotationValue <io .swagger .v3 .oas .annotations .media .Schema > schemaAnn ) {
1790+ @ Nullable AnnotationValue <io .swagger .v3 .oas .annotations .media .Schema > schemaAnn ) {
17641791
1792+ if (schemaAnn == null ) {
1793+ return ;
1794+ }
17651795 Map <CharSequence , Object > annValues = schemaAnn .getValues ();
17661796 if (annValues .containsKey (PROP_NAME )) {
17671797 schemaToBind .setName ((String ) annValues .get (PROP_NAME ));
@@ -2136,6 +2166,59 @@ private static void processArgTypeAnnotations(ClassElement type, @Nullable Schem
21362166 processJakartaValidationAnnotations (type , type , schema );
21372167 }
21382168
2169+ private static boolean processJacksonPropertyAnn (Element element , ClassElement elType , Schema <?> schemaToBind ,
2170+ @ Nullable AnnotationValue <io .swagger .v3 .oas .annotations .media .Schema > schemaAnn ,
2171+ VisitorContext context ) {
2172+
2173+ var swaggerAccessMode = schemaAnn != null ? schemaAnn .stringValue (PROP_ACCESS_MODE ).orElse (null ) : null ;
2174+ var swaggerDefaultValue = schemaAnn != null ? schemaAnn .stringValue (PROP_DEFAULT_VALUE ).orElse (null ) : null ;
2175+
2176+ var reference = new AtomicReference <>(false );
2177+ findAnnotation (element , "com.fasterxml.jackson.annotation.JsonProperty" )
2178+ .ifPresent (ann -> {
2179+ if (swaggerAccessMode == null ) {
2180+ ann .get (PROP_ACCESS , JsonProperty .Access .class ).ifPresent (access -> {
2181+ switch (access ) {
2182+ case READ_ONLY :
2183+ schemaToBind .setWriteOnly (null );
2184+ schemaToBind .setReadOnly (true );
2185+ reference .set (true );
2186+ break ;
2187+ case WRITE_ONLY :
2188+ schemaToBind .setWriteOnly (true );
2189+ schemaToBind .setReadOnly (null );
2190+ reference .set (true );
2191+ break ;
2192+ case READ_WRITE :
2193+ schemaToBind .setWriteOnly (null );
2194+ schemaToBind .setReadOnly (null );
2195+ break ;
2196+ default :
2197+ break ;
2198+ }
2199+ });
2200+ }
2201+ if (swaggerDefaultValue == null ) {
2202+ ann .stringValue (PROP_DEFAULT_VALUE ).ifPresent (defaultValue -> {
2203+ Pair <String , String > typeAndFormat ;
2204+ if (elType .isIterable ()) {
2205+ typeAndFormat = Pair .of (TYPE_ARRAY , null );
2206+ } else if (elType instanceof EnumElement enumEl ) {
2207+ typeAndFormat = ConvertUtils .checkEnumJsonValueType (context , enumEl , null , null );
2208+ } else {
2209+ typeAndFormat = ConvertUtils .getTypeAndFormatByClass (elType .getName (), elType .isArray ());
2210+ }
2211+ setDefaultValueObject (schemaToBind , defaultValue , elType , typeAndFormat .getFirst (), typeAndFormat .getSecond (), false , context );
2212+ if (schemaToBind .getDefault () != null ) {
2213+ reference .set (true );
2214+ }
2215+ });
2216+ }
2217+ });
2218+
2219+ return reference .get ();
2220+ }
2221+
21392222 private static void processJakartaValidationAnnotations (Element element , ClassElement elementType , Schema <?> schemaToBind ) {
21402223
21412224 final boolean isIterableOrMap = elementType .isIterable () || elementType .isAssignable (Map .class );
0 commit comments