88import static io .jooby .internal .openapi .javadoc .JavaDocNode .getText ;
99import static io .jooby .internal .openapi .javadoc .JavaDocStream .*;
1010import static io .jooby .internal .openapi .javadoc .JavaDocStream .children ;
11+ import static java .util .Optional .ofNullable ;
1112
1213import java .util .*;
14+ import java .util .function .Consumer ;
1315import java .util .function .Function ;
1416import java .util .function .Predicate ;
17+ import java .util .stream .Stream ;
1518
1619import com .puppycrawl .tools .checkstyle .api .DetailNode ;
1720import com .puppycrawl .tools .checkstyle .api .JavadocTokenTypes ;
2124import io .jooby .internal .openapi .ResponseExt ;
2225import io .swagger .v3 .oas .models .info .Contact ;
2326import io .swagger .v3 .oas .models .info .License ;
27+ import io .swagger .v3 .oas .models .security .*;
2428import io .swagger .v3 .oas .models .servers .Server ;
2529import io .swagger .v3 .oas .models .tags .Tag ;
2630
@@ -31,6 +35,13 @@ public class JavaDocTag {
3135 CUSTOM_TAG .and (it -> it .getText ().startsWith ("@tag." ) || it .getText ().equals ("@tag" ));
3236 private static final Predicate <DetailNode > SERVER =
3337 CUSTOM_TAG .and (it -> it .getText ().startsWith ("@server." ));
38+ private static final Predicate <DetailNode > SECURITY =
39+ CUSTOM_TAG .and (
40+ it -> it .getText ().equals ("@security" ) || it .getText ().equals ("@securityRequirement" ));
41+ private static final Predicate <DetailNode > SECURITY_REQUIREMENT =
42+ CUSTOM_TAG .and (it -> it .getText ().equals ("@securityRequirement" ));
43+ private static final Predicate <DetailNode > SECURITY_SCHEME =
44+ CUSTOM_TAG .and (it -> it .getText ().startsWith ("@securityScheme." ));
3445 private static final Predicate <DetailNode > CONTACT =
3546 CUSTOM_TAG .and (it -> it .getText ().startsWith ("@contact." ));
3647 private static final Predicate <DetailNode > LICENSE =
@@ -42,6 +53,109 @@ public class JavaDocTag {
4253 private static final Predicate <DetailNode > THROWS =
4354 it -> tree (it ).anyMatch (javadocToken (JavadocTokenTypes .THROWS_LITERAL ));
4455
56+ public static List <SecurityRequirement > securityRequirement (DetailNode node ) {
57+ return parse (node , SECURITY , null ).stream ()
58+ .map (
59+ hash ->
60+ hash .containsKey ("security" )
61+ ? hash .get ("security" )
62+ : hash .get ("securityRequirement" ))
63+ .map (Object ::toString )
64+ .map (String ::trim )
65+ .map (
66+ value -> {
67+ // look first space
68+ var indexOf = value .indexOf (' ' );
69+ String key ;
70+ String scopes ;
71+ if (indexOf > 0 ) {
72+ key = value .substring (0 , indexOf ).trim ();
73+ scopes = value .substring (indexOf + 1 ).trim ();
74+ } else {
75+ key = value ;
76+ scopes = "" ;
77+ }
78+ if (scopes .startsWith ("[" ) && scopes .endsWith ("]" )) {
79+ scopes = scopes .substring (1 , scopes .length () - 1 );
80+ }
81+ var scopeList = Stream .of (scopes .split ("," )).map (String ::trim ).toList ();
82+ var security = new SecurityRequirement ();
83+ security .addList (key , scopeList );
84+ return security ;
85+ })
86+ .toList ();
87+ }
88+
89+ public static List <SecurityScheme > securitySchemes (DetailNode node ) {
90+ return parse (node , SECURITY_SCHEME , "securityScheme" ).stream ()
91+ .map (
92+ hash -> {
93+ var item = new SecurityScheme ();
94+ item .setDescription ((String ) hash .get ("description" ));
95+ item .setName ((String ) hash .get ("name" ));
96+ ofNullable ((String ) hash .get ("in" ))
97+ .map (String ::toUpperCase )
98+ .map (SecurityScheme .In ::valueOf )
99+ .ifPresent (item ::setIn );
100+ ofNullable ((String ) hash .get ("type" ))
101+ .map (String ::toUpperCase )
102+ .map (SecurityScheme .Type ::valueOf )
103+ .ifPresent (item ::setType );
104+ item .setBearerFormat ((String ) hash .get ("bearerFormat" ));
105+ item .setOpenIdConnectUrl ((String ) hash .get ("openIdConnectUrl" ));
106+ item .setScheme ((String ) hash .get ("scheme" ));
107+ var objectFlows = hash .get ("flows" );
108+ if (objectFlows instanceof Map <?, ?> hashFlows ) {
109+ OAuthFlows flows = new OAuthFlows ();
110+ toOauthFlow ("implicit" , hashFlows , flows ::setImplicit );
111+ toOauthFlow ("password" , hashFlows , flows ::setPassword );
112+ toOauthFlow ("authorizationCode" , hashFlows , flows ::setAuthorizationCode );
113+ toOauthFlow ("clientCredentials" , hashFlows , flows ::setClientCredentials );
114+ item .setFlows (flows );
115+ }
116+ return item ;
117+ })
118+ .toList ();
119+ }
120+
121+ private static void toOauthFlow (String path , Map <?, ?> flows , Consumer <OAuthFlow > consumer ) {
122+ var flowHash = flows .get (path );
123+ if (flowHash instanceof Map hash ) {
124+ var oauthFlow = new OAuthFlow ();
125+ oauthFlow .setAuthorizationUrl ((String ) hash .get ("authorizationUrl" ));
126+ oauthFlow .setTokenUrl ((String ) hash .get ("tokenUrl" ));
127+ oauthFlow .setRefreshUrl ((String ) hash .get ("refreshUrl" ));
128+ var scopesObject = hash .get ("scopes" );
129+ List <String > scopeNames ;
130+ List <String > scopeDescriptions ;
131+ if (scopesObject instanceof Map <?, ?> scopesHash ) {
132+ scopeNames = ensureList (scopesHash .get ("name" ));
133+ scopeDescriptions = ensureList (scopesHash .get ("description" ));
134+ } else {
135+ scopeNames = ensureList (scopesObject );
136+ scopeDescriptions = List .of ();
137+ }
138+ if (!scopeNames .isEmpty ()) {
139+ Scopes scopes = new Scopes ();
140+ for (int i = 0 ; i < scopeNames .size (); i ++) {
141+ var description = i < scopeDescriptions .size () ? scopeDescriptions .get (i ) : "" ;
142+ scopes .addString (scopeNames .get (i ), description );
143+ }
144+ oauthFlow .setScopes (scopes );
145+ }
146+ consumer .accept (oauthFlow );
147+ }
148+ }
149+
150+ @ SuppressWarnings ({"unchecked" , "rawtypes" })
151+ private static List <String > ensureList (Object value ) {
152+ if (value == null ) return List .of ();
153+ if (value instanceof List list ) {
154+ return list .stream ().map (Objects ::toString ).toList ();
155+ }
156+ return List .of (value .toString ());
157+ }
158+
45159 public static List <Server > servers (DetailNode node ) {
46160 return openApiComponent (
47161 node ,
@@ -111,6 +225,21 @@ private static <T> List<T> openApiComponent(
111225 return result ;
112226 }
113227
228+ private static List <Map <String , Object >> parse (
229+ DetailNode node , Predicate <DetailNode > filter , String path ) {
230+ var values = new ArrayList <String >();
231+ javaDocTag (
232+ node ,
233+ filter ,
234+ (tag , value ) -> {
235+ values .add (tag .getText ().substring (1 ));
236+ values .add (value );
237+ });
238+ return ListToMapParser .parse (values ).stream ()
239+ .map (hash -> path == null ? hash : (Map <String , Object >) hash .get (path ))
240+ .toList ();
241+ }
242+
114243 public static Map <StatusCode , ResponseExt > throwList (DetailNode node ) {
115244 var result = new LinkedHashMap <StatusCode , ResponseExt >();
116245 javaDocTag (
0 commit comments