Skip to content

Commit 000eebf

Browse files
author
bnasslahsen
committed
Empty API docs with nested routes. Fixes #879.
1 parent fcce8cc commit 000eebf

File tree

15 files changed

+1254
-13
lines changed

15 files changed

+1254
-13
lines changed

springdoc-openapi-common/src/main/java/org/springdoc/core/fn/AbstractRouterFunctionVisitor.java

Lines changed: 113 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
package org.springdoc.core.fn;
2222

2323
import java.util.ArrayList;
24+
import java.util.Arrays;
2425
import java.util.List;
2526
import java.util.Set;
2627

28+
import org.apache.commons.lang3.StringUtils;
29+
2730
import org.springframework.http.HttpHeaders;
2831
import org.springframework.http.HttpMethod;
2932

@@ -38,6 +41,36 @@ public class AbstractRouterFunctionVisitor {
3841
*/
3942
protected List<RouterFunctionData> routerFunctionDatas = new ArrayList<>();
4043

44+
/**
45+
* The Nested or paths.
46+
*/
47+
protected List<String> nestedOrPaths = new ArrayList<>();
48+
49+
/**
50+
* The Nested and paths.
51+
*/
52+
protected List<String> nestedAndPaths = new ArrayList<>();
53+
54+
/**
55+
* The Nested accept headers.
56+
*/
57+
protected List<String> nestedAcceptHeaders = new ArrayList<>();
58+
59+
/**
60+
* The Nested content type headers.
61+
*/
62+
protected List<String> nestedContentTypeHeaders = new ArrayList<>();
63+
64+
/**
65+
* The Is or.
66+
*/
67+
protected boolean isOr;
68+
69+
/**
70+
* The Is and.
71+
*/
72+
protected boolean isAnd;
73+
4174
/**
4275
* The Router function data.
4376
*/
@@ -58,7 +91,12 @@ public void method(Set<HttpMethod> methods) {
5891
* @param pattern the pattern
5992
*/
6093
public void path(String pattern) {
61-
routerFunctionData.setPath(pattern);
94+
if (routerFunctionData != null)
95+
routerFunctionData.setPath(pattern);
96+
else if (isAnd)
97+
nestedAndPaths.add(pattern);
98+
else if (isOr)
99+
nestedOrPaths.add(pattern);
62100
}
63101

64102
/**
@@ -68,10 +106,12 @@ public void path(String pattern) {
68106
* @param value the value
69107
*/
70108
public void header(String name, String value) {
71-
if (HttpHeaders.ACCEPT.equals(name))
72-
routerFunctionData.addProduces(value);
73-
else if (HttpHeaders.CONTENT_TYPE.equals(name))
74-
routerFunctionData.addConsumes(value);
109+
if (HttpHeaders.ACCEPT.equals(name)) {
110+
calculateAccept(value);
111+
}
112+
else if (HttpHeaders.CONTENT_TYPE.equals(name)) {
113+
calculateContentType(value);
114+
}
75115
else
76116
routerFunctionData.addHeaders(name + "=" + value);
77117
}
@@ -118,7 +158,7 @@ public void param(String name, String value) {
118158
* Start and.
119159
*/
120160
public void startAnd() {
121-
// Not yet needed
161+
isAnd = true;
122162
}
123163

124164
/**
@@ -139,7 +179,7 @@ public void endAnd() {
139179
* Start or.
140180
*/
141181
public void startOr() {
142-
// Not yet needed
182+
isOr = true;
143183
}
144184

145185
/**
@@ -170,4 +210,70 @@ public void endNegate() {
170210
// Not yet needed
171211
}
172212

213+
protected void computeNested() {
214+
if (!nestedAndPaths.isEmpty()) {
215+
String nestedPath = String.join(StringUtils.EMPTY, nestedAndPaths);
216+
routerFunctionDatas.forEach(existingRouterFunctionData -> existingRouterFunctionData.setPath(nestedPath+existingRouterFunctionData.getPath()));
217+
}
218+
if (!nestedOrPaths.isEmpty()) {
219+
List<RouterFunctionData> routerFunctionDatasClone = new ArrayList<>();
220+
for (RouterFunctionData functionData : routerFunctionDatas) {
221+
for (String nestedOrPath : nestedOrPaths) {
222+
RouterFunctionData routerFunctionDataClone = new RouterFunctionData(nestedOrPath +functionData.getPath(),functionData.getConsumes(),functionData.getProduces(), functionData.getHeaders(), functionData.getQueryParams(), functionData.getMethods());
223+
routerFunctionDatasClone.add(routerFunctionDataClone);
224+
}
225+
}
226+
this.routerFunctionDatas = routerFunctionDatasClone;
227+
}
228+
if (!nestedAcceptHeaders.isEmpty())
229+
routerFunctionDatas.forEach(existingRouterFunctionData -> existingRouterFunctionData.addProduces(nestedAcceptHeaders));
230+
if (!nestedContentTypeHeaders.isEmpty())
231+
routerFunctionDatas.forEach(existingRouterFunctionData -> existingRouterFunctionData.addConsumes(nestedContentTypeHeaders));
232+
}
233+
234+
/**
235+
* Calculate content type.
236+
*
237+
* @param value the value
238+
*/
239+
private void calculateContentType(String value) {
240+
if (value.contains(",")) {
241+
String[] mediaTypes = value.substring(1, value.length() - 1).split(", ");
242+
for (String mediaType : mediaTypes)
243+
if (routerFunctionData != null)
244+
routerFunctionData.addConsumes(mediaType);
245+
else
246+
nestedContentTypeHeaders.addAll(Arrays.asList(mediaTypes));
247+
}
248+
else {
249+
if (routerFunctionData != null)
250+
routerFunctionData.addConsumes(value);
251+
else
252+
nestedContentTypeHeaders.add(value);
253+
}
254+
}
255+
256+
/**
257+
* Calculate accept.
258+
*
259+
* @param value the value
260+
*/
261+
private void calculateAccept(String value) {
262+
if (value.contains(",")) {
263+
String[] mediaTypes = value.substring(1, value.length() - 1).split(", ");
264+
for (String mediaType : mediaTypes)
265+
if (routerFunctionData != null)
266+
routerFunctionData.addProduces(mediaType);
267+
else
268+
nestedAcceptHeaders.addAll(Arrays.asList(mediaTypes));
269+
}
270+
else {
271+
if (routerFunctionData != null)
272+
routerFunctionData.addProduces(value);
273+
else
274+
nestedAcceptHeaders.add(value);
275+
}
276+
}
277+
278+
173279
}

springdoc-openapi-common/src/main/java/org/springdoc/core/fn/RouterFunctionData.java

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package org.springdoc.core.fn;
2222

2323
import java.util.ArrayList;
24+
import java.util.Arrays;
2425
import java.util.HashMap;
2526
import java.util.List;
2627
import java.util.Map;
@@ -69,6 +70,30 @@ public class RouterFunctionData {
6970
*/
7071
private RequestMethod[] methods;
7172

73+
/**
74+
* Instantiates a new Router function data.
75+
*/
76+
public RouterFunctionData() { }
77+
78+
/**
79+
* Instantiates a new Router function data.
80+
*
81+
* @param path the path
82+
* @param consumes the consumes
83+
* @param produces the produces
84+
* @param headers the headers
85+
* @param queryParams the query params
86+
* @param methods the methods
87+
*/
88+
public RouterFunctionData(String path, String[] consumes, String[] produces, String[] headers, Map<String, String> queryParams, RequestMethod[] methods) {
89+
this.path = path;
90+
this.consumes = Arrays.asList(consumes);
91+
this.produces = Arrays.asList(produces);
92+
this.headers = Arrays.asList(headers);
93+
this.queryParams = queryParams;
94+
this.methods = methods;
95+
}
96+
7297
/**
7398
* Gets path.
7499
*
@@ -172,6 +197,24 @@ public void addProduces(String produces) {
172197
this.produces.add(produces);
173198
}
174199

200+
/**
201+
* Add produces.
202+
*
203+
* @param produces the produces
204+
*/
205+
public void addProduces(List<String> produces) {
206+
this.produces.addAll(produces);
207+
}
208+
209+
/**
210+
* Add consumes.
211+
*
212+
* @param consumes the consumes
213+
*/
214+
public void addConsumes(List<String> consumes) {
215+
this.consumes.addAll(consumes);
216+
}
217+
175218
/**
176219
* Get method request method [ ].
177220
*

springdoc-openapi-webflux-core/src/main/java/org/springdoc/webflux/core/visitor/RouterFunctionVisitor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ public void route(RequestPredicate predicate, HandlerFunction<?> handlerFunction
4949

5050
@Override
5151
public void startNested(RequestPredicate predicate) {
52-
// Not yet needed
52+
predicate.accept(this);
5353
}
5454

5555
@Override
5656
public void endNested(RequestPredicate predicate) {
57-
// Not yet needed
57+
computeNested();
5858
}
5959

6060
@Override

springdoc-openapi-webflux-core/src/test/java/test/org/springdoc/api/app74/BookRouter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ class BookRouter {
4848
,parameters = { @Parameter(in = ParameterIn.PATH, name = "author")})) })
4949
RouterFunction<?> routes(BookRepository br) {
5050
return
51-
route(GET("/books").and(accept(MediaType.APPLICATION_JSON)).and(accept(MediaType.APPLICATION_XML)), req -> ok().body(br.findAll(), Book.class))
52-
.and(route(GET("/books").and(accept(MediaType.APPLICATION_XML)).and(accept(MediaType.TEXT_PLAIN)), req -> ok().body(br.findAll(), Book.class)))
51+
route(GET("/books").and(accept(MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML)), req -> ok().body(br.findAll(), Book.class))
52+
.and(route(GET("/books").and(accept(MediaType.APPLICATION_XML,MediaType.TEXT_PLAIN)), req -> ok().body(br.findAll(), Book.class)))
5353
.andRoute(GET("/books/{author}"), req -> ok().body(br.findByAuthor(req.pathVariable("author")), Book.class));
5454
}
5555
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
*
3+
* *
4+
* * * Copyright 2019-2020 the original author or authors.
5+
* * *
6+
* * * Licensed under the Apache License, Version 2.0 (the "License");
7+
* * * you may not use this file except in compliance with the License.
8+
* * * You may obtain a copy of the License at
9+
* * *
10+
* * * https://www.apache.org/licenses/LICENSE-2.0
11+
* * *
12+
* * * Unless required by applicable law or agreed to in writing, software
13+
* * * distributed under the License is distributed on an "AS IS" BASIS,
14+
* * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* * * See the License for the specific language governing permissions and
16+
* * * limitations under the License.
17+
* *
18+
*
19+
*/
20+
21+
package test.org.springdoc.api.app80;
22+
23+
public class Book {
24+
25+
private String id;
26+
private String title;
27+
private String author;
28+
29+
public Book(String id, String title, String author) {
30+
this.id = id;
31+
this.title = title;
32+
this.author = author;
33+
}
34+
35+
public String getId() {
36+
return id;
37+
}
38+
39+
public void setId(String id) {
40+
this.id = id;
41+
}
42+
43+
public String getTitle() {
44+
return title;
45+
}
46+
47+
public void setTitle(String title) {
48+
this.title = title;
49+
}
50+
51+
public String getAuthor() {
52+
return author;
53+
}
54+
55+
public void setAuthor(String author) {
56+
this.author = author;
57+
}
58+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
*
3+
* *
4+
* * * Copyright 2019-2020 the original author or authors.
5+
* * *
6+
* * * Licensed under the Apache License, Version 2.0 (the "License");
7+
* * * you may not use this file except in compliance with the License.
8+
* * * You may obtain a copy of the License at
9+
* * *
10+
* * * https://www.apache.org/licenses/LICENSE-2.0
11+
* * *
12+
* * * Unless required by applicable law or agreed to in writing, software
13+
* * * distributed under the License is distributed on an "AS IS" BASIS,
14+
* * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* * * See the License for the specific language governing permissions and
16+
* * * limitations under the License.
17+
* *
18+
*
19+
*/
20+
21+
package test.org.springdoc.api.app80;
22+
23+
import reactor.core.publisher.Flux;
24+
25+
import org.springframework.stereotype.Component;
26+
27+
@Component
28+
public class BookRepository {
29+
30+
31+
Flux<Book> findByAuthor(String author){
32+
return Flux.just(new Book("1", "title1","author1"));
33+
}
34+
35+
Flux<Book> findAll() {
36+
return Flux.just(new Book("2", "title2","author2"));
37+
}
38+
}

0 commit comments

Comments
 (0)