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 ;
118117import java .util .OptionalLong ;
119118import java .util .Set ;
120119import java .util .UUID ;
120+ import java .util .concurrent .atomic .AtomicReference ;
121121import java .util .function .BiConsumer ;
122122import java .util .function .Function ;
123123
148148import static io .micronaut .openapi .visitor .OpenApiApplicationVisitor .resolvePlaceholders ;
149149import static io .micronaut .openapi .visitor .OpenApiConfigProperty .MICRONAUT_OPENAPI_FIELD_VISIBILITY_LEVEL ;
150150import static io .micronaut .openapi .visitor .OpenApiModelProp .DISCRIMINATOR ;
151+ import static io .micronaut .openapi .visitor .OpenApiModelProp .PROP_ACCESS ;
151152import static io .micronaut .openapi .visitor .OpenApiModelProp .PROP_ACCESS_MODE ;
152153import static io .micronaut .openapi .visitor .OpenApiModelProp .PROP_ADDITIONAL_PROPERTIES ;
153154import static io .micronaut .openapi .visitor .OpenApiModelProp .PROP_ALLOWABLE_VALUES ;
@@ -358,7 +359,8 @@ public static Schema<?> getSchemaDefinition(OpenAPI openAPI,
358359 if (type instanceof EnumElement enumEl ) {
359360 schema = setSpecVersion (new Schema <>());
360361 schema .setName (schemaName );
361- if (javadoc != null && StringUtils .hasText (javadoc .getMethodDescription ())) {
362+ processJacksonDescription (enumEl , schema );
363+ if (schema .getDescription () == null && javadoc != null && StringUtils .hasText (javadoc .getMethodDescription ())) {
362364 schema .setDescription (javadoc .getMethodDescription ());
363365 }
364366 schemas .put (schemaName , schema );
@@ -374,7 +376,8 @@ public static Schema<?> getSchemaDefinition(OpenAPI openAPI,
374376 if (schemaWithSuperTypes != null ) {
375377 schema = schemaWithSuperTypes ;
376378 }
377- if (schema != null && javadoc != null && StringUtils .hasText (javadoc .getMethodDescription ())) {
379+ processJacksonDescription (type , schema );
380+ if (schema != null && schema .getDescription () == null && javadoc != null && StringUtils .hasText (javadoc .getMethodDescription ())) {
378381 schema .setDescription (javadoc .getMethodDescription ());
379382 }
380383
@@ -434,7 +437,7 @@ public static Schema<?> getSchemaDefinition(OpenAPI openAPI,
434437 if (externalDocs != null ) {
435438 schema .setExternalDocs (externalDocs );
436439 }
437- setSchemaDocumentation (type , schema );
440+ setSchemaDescription (type , schema );
438441 var schemaRef = setSpecVersion (new Schema <>());
439442 schemaRef .set$ref (SchemaUtils .schemaRef (schema .getName ()));
440443 if (definingElement instanceof ClassElement classEl && classEl .isIterable ()) {
@@ -840,6 +843,8 @@ public static Schema<?> resolveSchema(OpenAPI openApi, @Nullable Element definin
840843 processSchemaAnn (schema , context , definingElement , type , schemaAnnotationValue );
841844 }
842845
846+ processJacksonDescription (definingElement , schema );
847+
843848 if (definingElement != null && StringUtils .isEmpty (schema .getDescription ())) {
844849 if (fieldJavadoc != null ) {
845850 if (StringUtils .hasText (fieldJavadoc .getMethodDescription ())) {
@@ -930,7 +935,7 @@ public static Schema<?> bindSchemaForElement(VisitorContext context, TypedElemen
930935 }
931936
932937 boolean notOnlyRef = false ;
933- setSchemaDocumentation (element , topLevelSchema );
938+ setSchemaDescription (element , topLevelSchema );
934939 if (StringUtils .isNotEmpty (topLevelSchema .getDescription ())) {
935940 notOnlyRef = true ;
936941 }
@@ -954,11 +959,14 @@ && isProtobufGenerated(propertyEl.getOwningType())
954959 SchemaUtils .setNullable (topLevelSchema );
955960 notOnlyRef = true ;
956961 }
957- final String defaultJacksonValue = stringValue (element , JsonProperty .class , PROP_DEFAULT_VALUE ).orElse (null );
958- if (defaultJacksonValue != null && schemaToBind .getDefault () == null ) {
959- setDefaultValueObject (topLevelSchema , defaultJacksonValue , elementType , schemaToBind .getType (), schemaToBind .getFormat (), false , context );
962+ if (processJacksonPropertyAnn (element , elementType , topLevelSchema , schemaAnn , context )) {
960963 notOnlyRef = true ;
961964 }
965+ // final String defaultJacksonValue = stringValue(element, JsonProperty.class, PROP_DEFAULT_VALUE).orElse(null);
966+ // if (defaultJacksonValue != null && schemaToBind.getDefault() == null) {
967+ // setDefaultValueObject(topLevelSchema, defaultJacksonValue, elementType, schemaToBind.getType(), schemaToBind.getFormat(), false, context);
968+ // notOnlyRef = true;
969+ // }
962970
963971 boolean addSchemaToBind = !SchemaUtils .isEmptySchema (schemaToBind );
964972
@@ -1728,37 +1736,59 @@ private static void checkAllOf(Schema<Object> composedSchema) {
17281736 composedSchema .addAllOfItem (propSchema );
17291737 }
17301738
1731- private static void setSchemaDocumentation (Element element , Schema <?> schemaToBind ) {
1732- if (StringUtils .isEmpty (schemaToBind .getDescription ())) {
1733- // First, find getter method javadoc
1734- String doc = element .getDocumentation ().orElse (null );
1735- if (StringUtils .isEmpty (doc )) {
1736- // next, find field javadoc
1737- if (element instanceof MemberElement memberEl ) {
1738- List <FieldElement > fields = memberEl .getDeclaringType ().getFields ();
1739- if (CollectionUtils .isNotEmpty (fields )) {
1740- for (FieldElement field : fields ) {
1741- if (field .getName ().equals (element .getName ())) {
1742- doc = field .getDocumentation ().orElse (null );
1743- break ;
1744- }
1739+ private static void processJacksonDescription (@ Nullable Element element , @ Nullable Schema <?> schemaToBind ) {
1740+ if (element == null || schemaToBind == null || StringUtils .isNotEmpty (schemaToBind .getDescription ())) {
1741+ return ;
1742+ }
1743+ findAnnotation (element , element instanceof ClassElement
1744+ ? "com.fasterxml.jackson.annotation.JsonClassDescription"
1745+ : "com.fasterxml.jackson.annotation.JsonPropertyDescription"
1746+ )
1747+ .flatMap (ann -> ann .stringValue (PROP_VALUE ))
1748+ .ifPresent (schemaToBind ::setDescription );
1749+ }
1750+
1751+ private static void setSchemaDescription (Element element , Schema <?> schemaToBind ) {
1752+ if (StringUtils .isNotEmpty (schemaToBind .getDescription ())) {
1753+ return ;
1754+ }
1755+
1756+ processJacksonDescription (element , schemaToBind );
1757+ if (StringUtils .isNotEmpty (schemaToBind .getDescription ())) {
1758+ return ;
1759+ }
1760+
1761+ // First, find getter method javadoc
1762+ String doc = element .getDocumentation ().orElse (null );
1763+ if (StringUtils .isEmpty (doc )) {
1764+ // next, find field javadoc
1765+ if (element instanceof MemberElement memberEl ) {
1766+ List <FieldElement > fields = memberEl .getDeclaringType ().getFields ();
1767+ if (CollectionUtils .isNotEmpty (fields )) {
1768+ for (FieldElement field : fields ) {
1769+ if (field .getName ().equals (element .getName ())) {
1770+ doc = field .getDocumentation ().orElse (null );
1771+ break ;
17451772 }
17461773 }
17471774 }
17481775 }
1749- if ( doc != null ) {
1750- JavadocDescription desc = Utils . getJavadocParser (). parse ( doc );
1751- if ( StringUtils . hasText ( desc . getMethodDescription ())) {
1752- schemaToBind . setDescription (desc .getMethodDescription ());
1753- }
1776+ }
1777+ if ( doc != null ) {
1778+ JavadocDescription desc = Utils . getJavadocParser (). parse ( doc );
1779+ if ( StringUtils . hasText (desc .getMethodDescription ())) {
1780+ schemaToBind . setDescription ( desc . getMethodDescription ());
17541781 }
17551782 }
17561783 }
17571784
17581785 private static void processSchemaAnn (Schema schemaToBind , VisitorContext context , Element element ,
17591786 @ Nullable ClassElement classEl ,
1760- @ NonNull AnnotationValue <io .swagger .v3 .oas .annotations .media .Schema > schemaAnn ) {
1787+ @ Nullable AnnotationValue <io .swagger .v3 .oas .annotations .media .Schema > schemaAnn ) {
17611788
1789+ if (schemaAnn == null ) {
1790+ return ;
1791+ }
17621792 Map <CharSequence , Object > annValues = schemaAnn .getValues ();
17631793 if (annValues .containsKey (PROP_NAME )) {
17641794 schemaToBind .setName ((String ) annValues .get (PROP_NAME ));
@@ -2133,6 +2163,59 @@ private static void processArgTypeAnnotations(ClassElement type, @Nullable Schem
21332163 processJakartaValidationAnnotations (type , type , schema );
21342164 }
21352165
2166+ private static boolean processJacksonPropertyAnn (Element element , ClassElement elType , Schema <?> schemaToBind ,
2167+ @ Nullable AnnotationValue <io .swagger .v3 .oas .annotations .media .Schema > schemaAnn ,
2168+ VisitorContext context ) {
2169+
2170+ var swaggerAccessMode = schemaAnn != null ? schemaAnn .stringValue (PROP_ACCESS_MODE ).orElse (null ) : null ;
2171+ var swaggerDefaultValue = schemaAnn != null ? schemaAnn .stringValue (PROP_DEFAULT_VALUE ).orElse (null ) : null ;
2172+
2173+ var reference = new AtomicReference <>(false );
2174+ findAnnotation (element , "com.fasterxml.jackson.annotation.JsonProperty" )
2175+ .ifPresent (ann -> {
2176+ if (swaggerAccessMode == null ) {
2177+ ann .get (PROP_ACCESS , JsonProperty .Access .class ).ifPresent (access -> {
2178+ switch (access ) {
2179+ case READ_ONLY :
2180+ schemaToBind .setWriteOnly (null );
2181+ schemaToBind .setReadOnly (true );
2182+ reference .set (true );
2183+ break ;
2184+ case WRITE_ONLY :
2185+ schemaToBind .setWriteOnly (true );
2186+ schemaToBind .setReadOnly (null );
2187+ reference .set (true );
2188+ break ;
2189+ case READ_WRITE :
2190+ schemaToBind .setWriteOnly (null );
2191+ schemaToBind .setReadOnly (null );
2192+ break ;
2193+ default :
2194+ break ;
2195+ }
2196+ });
2197+ }
2198+ if (swaggerDefaultValue == null ) {
2199+ ann .stringValue (PROP_DEFAULT_VALUE ).ifPresent (defaultValue -> {
2200+ Pair <String , String > typeAndFormat ;
2201+ if (elType .isIterable ()) {
2202+ typeAndFormat = Pair .of (TYPE_ARRAY , null );
2203+ } else if (elType instanceof EnumElement enumEl ) {
2204+ typeAndFormat = ConvertUtils .checkEnumJsonValueType (context , enumEl , null , null );
2205+ } else {
2206+ typeAndFormat = ConvertUtils .getTypeAndFormatByClass (elType .getName (), elType .isArray ());
2207+ }
2208+ setDefaultValueObject (schemaToBind , defaultValue , elType , typeAndFormat .getFirst (), typeAndFormat .getSecond (), false , context );
2209+ if (schemaToBind .getDefault () != null ) {
2210+ reference .set (true );
2211+ }
2212+ });
2213+ }
2214+ });
2215+
2216+ return reference .get ();
2217+ }
2218+
21362219 private static void processJakartaValidationAnnotations (Element element , ClassElement elementType , Schema <?> schemaToBind ) {
21372220
21382221 final boolean isIterableOrMap = elementType .isIterable () || elementType .isAssignable (Map .class );
0 commit comments