@@ -21,25 +21,60 @@ public class OAR031ExamplesCheck extends BaseCheck {
2121
2222 private static final String EXAMPLE = "example" ;
2323 private static final String EXAMPLES = "examples" ;
24+ private static final String SCHEMA = "schema" ;
25+ private static final String PROPERTIES = "properties" ;
26+ private static final String ITEMS = "items" ;
27+
28+ private static final String ERROR_RESPONSE = "OAR031.error-response" ;
2429
2530 private final ExternalRefHandler handleExternalRef = new ExternalRefHandler ();
2631
2732 @ Override
2833 public Set <AstNodeType > subscribedKinds () {
29- return ImmutableSet .of (OpenApi2Grammar .SCHEMA , OpenApi2Grammar .RESPONSES , OpenApi3Grammar .SCHEMA , OpenApi3Grammar .RESPONSES , OpenApi31Grammar .SCHEMA , OpenApi31Grammar .RESPONSES , OpenApi3Grammar .REQUEST_BODY , OpenApi31Grammar .REQUEST_BODY , OpenApi2Grammar .PATH , OpenApi3Grammar .PATH , OpenApi31Grammar .PATH );
34+ return ImmutableSet .of (
35+ OpenApi2Grammar .SCHEMA , OpenApi2Grammar .RESPONSES , OpenApi2Grammar .PARAMETER ,
36+ OpenApi3Grammar .SCHEMA , OpenApi3Grammar .RESPONSES , OpenApi3Grammar .PARAMETER ,
37+ OpenApi31Grammar .SCHEMA , OpenApi31Grammar .RESPONSES , OpenApi31Grammar .PARAMETER ,
38+ OpenApi3Grammar .REQUEST_BODY , OpenApi31Grammar .REQUEST_BODY ,
39+ OpenApi2Grammar .PATH , OpenApi3Grammar .PATH , OpenApi31Grammar .PATH
40+ );
3041 }
3142
3243 @ Override
3344 public void visitNode (JsonNode node ) {
34- if (OpenApi2Grammar .PATH .equals (node .getType ()) || OpenApi3Grammar .PATH .equals (node .getType ()) || OpenApi31Grammar .PATH .equals (node .getType ())) {
45+ AstNodeType type = node .getType ();
46+ if (OpenApi2Grammar .PATH .equals (type ) || OpenApi3Grammar .PATH .equals (type ) || OpenApi31Grammar .PATH .equals (type )) {
3547 visitPathNode (node );
36- } else if (node .getType ().equals (OpenApi2Grammar .RESPONSES ) || node .getType ().equals (OpenApi2Grammar .SCHEMA )) {
48+ } else if (OpenApi2Grammar .PARAMETER .equals (type ) || OpenApi3Grammar .PARAMETER .equals (type ) || OpenApi31Grammar .PARAMETER .equals (type )) {
49+ visitParameterNode (node );
50+ } else if (type .equals (OpenApi2Grammar .RESPONSES ) || type .equals (OpenApi2Grammar .SCHEMA )) {
3751 visitV2Node (node );
3852 } else {
3953 visitV3Node (node );
4054 }
4155 }
4256
57+ private void visitParameterNode (JsonNode node ) {
58+ handleExternalRef .resolve (node , resolved -> {
59+ if (OpenApi2Grammar .PARAMETER .equals (resolved .getType ())) {
60+ JsonNode inNode = resolved .get ("in" );
61+ if (!inNode .isMissing () && !"body" .equals (inNode .getTokenValue ())) {
62+ return ;
63+ }
64+ }
65+
66+ JsonNode schema = resolved .get (SCHEMA );
67+
68+ boolean hasExample = !resolved .get (EXAMPLE ).isMissing ()
69+ || !resolved .get (EXAMPLES ).isMissing ()
70+ || (!schema .isMissing () && isSchemaCovered (schema ));
71+
72+ if (!hasExample ) {
73+ addIssue (KEY , translate ("OAR031.error-parameter" ), handleExternalRef .getTrueNode (node ));
74+ }
75+ });
76+ }
77+
4378 private void visitV2Node (JsonNode node ) {
4479 AstNodeType type = node .getType ();
4580 if (OpenApi2Grammar .RESPONSES .equals (type )) {
@@ -50,9 +85,15 @@ private void visitV2Node(JsonNode node) {
5085 }
5186
5287 private void visitResponseV2Node (JsonNode node ) {
53- if (node .get (EXAMPLES ).isMissing ()) {
54- addIssue (KEY , translate ("OAR031.error-response" ), node .key ());
55- }
88+ handleExternalRef .resolve (node , resolved -> {
89+ JsonNode schemaNode = resolved .get (SCHEMA );
90+ boolean hasExample = !resolved .get (EXAMPLES ).isMissing ()
91+ || (!schemaNode .isMissing () && isSchemaCovered (schemaNode ));
92+
93+ if (!hasExample ) {
94+ addIssue (KEY , translate (ERROR_RESPONSE ), handleExternalRef .getTrueNode (node .key ()));
95+ }
96+ });
5697 }
5798
5899 private void visitV3Node (JsonNode node ) {
@@ -75,35 +116,58 @@ private void processResponses(JsonNode node, java.util.function.Consumer<JsonNod
75116
76117 private void visitRequestBodyOrResponseV3Node (JsonNode node ) {
77118 JsonNode content = node .at ("/content" );
119+
78120 if (content .isMissing ()) {
121+ String errorKey = node .getType ().equals (OpenApi3Grammar .REQUEST_BODY ) ? "OAR031.error-request" : ERROR_RESPONSE ;
122+ addIssue (KEY , translate (errorKey ), handleExternalRef .getTrueNode (node .key ()));
79123 return ;
80124 }
125+
81126 for (JsonNode mediaTypeNode : content .propertyMap ().values ()) {
82- AstNodeType type = node .getType ();
83- JsonNode schemaNode = mediaTypeNode .get ("schema" );
84- boolean hasSchemaExample = false ;
85- if (schemaNode .getType ().equals (OpenApi3Grammar .SCHEMA ) && !schemaNode .get (EXAMPLE ).isMissing ()) {
86- hasSchemaExample = true ;
87- }
88- if (!hasSchemaExample && mediaTypeNode .get (EXAMPLES ).isMissing () && mediaTypeNode .get (EXAMPLE ).isMissing ()) {
89- if (type .equals (OpenApi3Grammar .REQUEST_BODY )) {
90- addIssue (KEY , translate ("OAR031.error-request" ), handleExternalRef .getTrueNode (node .key ()));
91- } else {
92- addIssue (KEY , translate ("OAR031.error-response" ), handleExternalRef .getTrueNode (node .key ()));
93- }
127+ JsonNode schemaNode = mediaTypeNode .get (SCHEMA );
128+ boolean hasExplicitExample = !mediaTypeNode .get (EXAMPLES ).isMissing ()
129+ || !mediaTypeNode .get (EXAMPLE ).isMissing ();
130+
131+ if (!hasExplicitExample && !isSchemaCovered (schemaNode )) {
132+ String errorKey = node .getType ().equals (OpenApi3Grammar .REQUEST_BODY ) ? "OAR031.error-request" : ERROR_RESPONSE ;
133+ addIssue (KEY , translate (errorKey ), handleExternalRef .getTrueNode (node .key ()));
94134 }
95135 }
96136 }
97137
138+ private boolean isSchemaCovered (JsonNode schemaNode ) {
139+ if (schemaNode .isMissing ()) return false ;
140+
141+ return handleExternalRef .resolve (schemaNode , resolved -> {
142+ if (!resolved .get (EXAMPLE ).isMissing () || !resolved .get (EXAMPLES ).isMissing ()) {
143+ return true ;
144+ }
145+
146+ JsonNode props = resolved .get (PROPERTIES );
147+ if (!props .isMissing () && props .isObject ()) {
148+ return props .propertyMap ().values ().stream ().anyMatch (this ::isSchemaCovered );
149+ }
150+
151+ JsonNode items = resolved .get (ITEMS );
152+ if (!items .isMissing ()) {
153+ return isSchemaCovered (items );
154+ }
155+
156+ return false ;
157+ });
158+ }
159+
98160 private void visitSchemaNode (JsonNode node ) {
99161 JsonNode parentNode = (JsonNode ) node .getParent ().getParent ();
100162
101163 if (parentNode .getType ().equals (OpenApi3Grammar .PARAMETER )) {
102- if (node .get (EXAMPLE ).isMissing () && parentNode .get (EXAMPLE ).isMissing () && parentNode .get (EXAMPLES ).isMissing ()) {
103- addIssue (KEY , translate ("OAR031.error-parameter" ), parentNode );
104- }
105- } else if (parentNode .getType ().equals (OpenApi3Grammar .SCHEMA_PROPERTIES )
106- || parentNode .getType ().toString ().equals ("BLOCK_MAPPING" ) || parentNode .getType ().toString ().equals ("FLOW_MAPPING" )) {
164+ return ;
165+ }
166+
167+ if (parentNode .getType ().equals (OpenApi3Grammar .SCHEMA_PROPERTIES )
168+ || parentNode .getType ().toString ().equals ("BLOCK_MAPPING" )
169+ || parentNode .getType ().toString ().equals ("FLOW_MAPPING" )) {
170+
107171 JsonNode schemaParent = (JsonNode ) parentNode .getParent ().getParent ();
108172 if (schemaParent != null && !schemaParent .get ("allOf" ).isMissing ()) {
109173 return ;
@@ -140,11 +204,11 @@ private void visitPathNode(JsonNode node) {
140204 }
141205
142206 private void visitSchemaNode2 (JsonNode responseNode ) {
143- JsonNode schemaNode = responseNode .value ().get ("schema" );
207+ JsonNode schemaNode = responseNode .value ().get (SCHEMA );
144208 if (schemaNode .isMissing ()) return ;
145209
146210 handleExternalRef .resolve (schemaNode , resolvedSchema -> {
147- JsonNode props = resolvedSchema .get ("properties" );
211+ JsonNode props = resolvedSchema .get (PROPERTIES );
148212 if (props .isMissing () || !props .isObject ()) return ;
149213
150214 props .propertyMap ().forEach ((key , propertyNode ) -> {
@@ -154,4 +218,4 @@ private void visitSchemaNode2(JsonNode responseNode) {
154218 });
155219 });
156220 }
157- }
221+ }
0 commit comments