Skip to content

Commit 860003c

Browse files
committed
RouteSpec Attributes + route spec: make source code optional for mvc routes fix #617 #685
1 parent 9ce2299 commit 860003c

9 files changed

Lines changed: 262 additions & 190 deletions

File tree

coverage-report/src/test/java/org/jooby/issues/Issue536.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public void shouldGenerateAValidSwagger() throws Exception {
7979
" \"tags\" : [ \"cat\" ],\n" +
8080
" \"summary\" : \"Another description\",\n" +
8181
" \"description\" : \"Another description\\nAnother line\",\n" +
82+
" \"operationId\" : \"get\",\n" +
8283
" \"produces\" : [ \"application/json\" ],\n" +
8384
" \"parameters\" : [ {\n" +
8485
" \"name\" : \"name\",\n" +

coverage-report/src/test/java/org/jooby/issues/Issue538.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ public void shouldGenerateAValidSwagger() throws Exception {
9797
" \"/api/cat/{name}\" : {\n" +
9898
" \"get\" : {\n" +
9999
" \"tags\" : [ \"cat\" ],\n" +
100-
" \"summary\" : \"CatResource.get\",\n" +
100+
" \"summary\" : \"get\",\n" +
101+
" \"operationId\" : \"get\",\n" +
101102
" \"produces\" : [ \"application/json\" ],\n" +
102103
" \"parameters\" : [ {\n" +
103104
" \"name\" : \"name\",\n" +

coverage-report/src/test/java/org/jooby/swagger/SwaggerFeature.java

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ public void json() throws Exception {
5151
" \"/api/pets\" : {\n" +
5252
" \"get\" : {\n" +
5353
" \"tags\" : [ \"pets\" ],\n" +
54-
" \"summary\" : \"Pets.list\",\n" +
54+
" \"summary\" : \"list\",\n" +
55+
" \"operationId\" : \"list\",\n" +
5556
" \"parameters\" : [ {\n" +
5657
" \"name\" : \"size\",\n" +
5758
" \"in\" : \"query\",\n" +
@@ -70,7 +71,8 @@ public void json() throws Exception {
7071
" },\n" +
7172
" \"post\" : {\n" +
7273
" \"tags\" : [ \"pets\" ],\n" +
73-
" \"summary\" : \"Pets.create\",\n" +
74+
" \"summary\" : \"create\",\n" +
75+
" \"operationId\" : \"create\",\n" +
7476
" \"parameters\" : [ {\n" +
7577
" \"in\" : \"body\",\n" +
7678
" \"name\" : \"pet\",\n" +
@@ -92,7 +94,8 @@ public void json() throws Exception {
9294
" \"/api/pets/{id}\" : {\n" +
9395
" \"get\" : {\n" +
9496
" \"tags\" : [ \"pets\" ],\n" +
95-
" \"summary\" : \"Pets.get\",\n" +
97+
" \"summary\" : \"get\",\n" +
98+
" \"operationId\" : \"get\",\n" +
9699
" \"parameters\" : [ {\n" +
97100
" \"name\" : \"id\",\n" +
98101
" \"in\" : \"path\",\n" +
@@ -146,7 +149,8 @@ public void json() throws Exception {
146149
" \"/api/pets\" : {\n" +
147150
" \"get\" : {\n" +
148151
" \"tags\" : [ \"pets\" ],\n" +
149-
" \"summary\" : \"Pets.list\",\n" +
152+
" \"summary\" : \"list\",\n" +
153+
" \"operationId\" : \"list\",\n" +
150154
" \"parameters\" : [ {\n" +
151155
" \"name\" : \"size\",\n" +
152156
" \"in\" : \"query\",\n" +
@@ -165,7 +169,8 @@ public void json() throws Exception {
165169
" },\n" +
166170
" \"post\" : {\n" +
167171
" \"tags\" : [ \"pets\" ],\n" +
168-
" \"summary\" : \"Pets.create\",\n" +
172+
" \"summary\" : \"create\",\n" +
173+
" \"operationId\" : \"create\",\n" +
169174
" \"parameters\" : [ {\n" +
170175
" \"in\" : \"body\",\n" +
171176
" \"name\" : \"pet\",\n" +
@@ -187,7 +192,8 @@ public void json() throws Exception {
187192
" \"/api/pets/{id}\" : {\n" +
188193
" \"get\" : {\n" +
189194
" \"tags\" : [ \"pets\" ],\n" +
190-
" \"summary\" : \"Pets.get\",\n" +
195+
" \"summary\" : \"get\",\n" +
196+
" \"operationId\" : \"get\",\n" +
191197
" \"parameters\" : [ {\n" +
192198
" \"name\" : \"id\",\n" +
193199
" \"in\" : \"path\",\n" +
@@ -246,7 +252,8 @@ public void yml() throws Exception {
246252
" get:\n" +
247253
" tags:\n" +
248254
" - \"pets\"\n" +
249-
" summary: \"Pets.list\"\n" +
255+
" summary: \"list\"\n" +
256+
" operationId: \"list\"\n" +
250257
" parameters:\n" +
251258
" - name: \"size\"\n" +
252259
" in: \"query\"\n" +
@@ -261,7 +268,8 @@ public void yml() throws Exception {
261268
" post:\n" +
262269
" tags:\n" +
263270
" - \"pets\"\n" +
264-
" summary: \"Pets.create\"\n" +
271+
" summary: \"create\"\n" +
272+
" operationId: \"create\"\n" +
265273
" parameters:\n" +
266274
" - in: \"body\"\n" +
267275
" name: \"pet\"\n" +
@@ -277,7 +285,8 @@ public void yml() throws Exception {
277285
" get:\n" +
278286
" tags:\n" +
279287
" - \"pets\"\n" +
280-
" summary: \"Pets.get\"\n" +
288+
" summary: \"get\"\n" +
289+
" operationId: \"get\"\n" +
281290
" parameters:\n" +
282291
" - name: \"id\"\n" +
283292
" in: \"path\"\n" +
@@ -321,7 +330,8 @@ public void yml() throws Exception {
321330
" get:\n" +
322331
" tags:\n" +
323332
" - \"pets\"\n" +
324-
" summary: \"Pets.list\"\n" +
333+
" summary: \"list\"\n" +
334+
" operationId: \"list\"\n" +
325335
" parameters:\n" +
326336
" - name: \"size\"\n" +
327337
" in: \"query\"\n" +
@@ -336,7 +346,8 @@ public void yml() throws Exception {
336346
" post:\n" +
337347
" tags:\n" +
338348
" - \"pets\"\n" +
339-
" summary: \"Pets.create\"\n" +
349+
" summary: \"create\"\n" +
350+
" operationId: \"create\"\n" +
340351
" parameters:\n" +
341352
" - in: \"body\"\n" +
342353
" name: \"pet\"\n" +
@@ -352,7 +363,8 @@ public void yml() throws Exception {
352363
" get:\n" +
353364
" tags:\n" +
354365
" - \"pets\"\n" +
355-
" summary: \"Pets.get\"\n" +
366+
" summary: \"get\"\n" +
367+
" operationId: \"get\"\n" +
356368
" parameters:\n" +
357369
" - name: \"id\"\n" +
358370
" in: \"path\"\n" +

jooby-raml/src/test/java/org/jooby/raml/RamlBuilderTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.lang.reflect.Type;
66
import java.util.ArrayList;
77
import java.util.Arrays;
8+
import java.util.Collections;
89
import java.util.LinkedHashMap;
910
import java.util.List;
1011
import java.util.Map;
@@ -251,6 +252,11 @@ public Spec rsp(final Consumer<Response> rsp) {
251252
return this;
252253
}
253254

255+
@Override
256+
public Map<String, Object> attributes() {
257+
return Collections.emptyMap();
258+
}
259+
254260
}
255261

256262
private Config conf = ConfigFactory.empty()

jooby-spec/src/main/java/org/jooby/internal/spec/DocCollector.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.stream.Collectors;
3131

3232
import org.jooby.Status;
33+
import org.slf4j.Logger;
3334

3435
import com.github.javaparser.ast.Node;
3536
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
@@ -57,16 +58,26 @@ public class DocCollector extends VoidVisitorAdapter<Context> {
5758

5859
private Map<String, Object> doc = new HashMap<>();
5960

61+
private Logger log;
62+
63+
public DocCollector(final Logger log) {
64+
this.log = log;
65+
}
66+
6067
public Map<String, Object> accept(final Node node, final String method, final Context ctx) {
61-
node.accept(this, ctx);
62-
if (!doc.containsKey("@statusCodes")) {
63-
Map<Object, Object> codes = new LinkedHashMap<>();
64-
Status status = Status.OK;
65-
if ("DELETE".equals(method)) {
66-
status = Status.NO_CONTENT;
68+
try {
69+
node.accept(this, ctx);
70+
if (!doc.containsKey("@statusCodes")) {
71+
Map<Object, Object> codes = new LinkedHashMap<>();
72+
Status status = Status.OK;
73+
if ("DELETE".equals(method)) {
74+
status = Status.NO_CONTENT;
75+
}
76+
codes.put(status.value(), status.reason());
77+
doc.put("@statusCodes", codes);
6778
}
68-
codes.put(status.value(), status.reason());
69-
doc.put("@statusCodes", codes);
79+
} catch (Exception x) {
80+
log.debug("Doc collector resulted in exception", x);
7081
}
7182
return doc;
7283
}

jooby-spec/src/main/java/org/jooby/internal/spec/RouteSpecImpl.java

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
*/
1919
package org.jooby.internal.spec;
2020

21+
import java.util.HashMap;
2122
import java.util.List;
23+
import java.util.Map;
2224
import java.util.Optional;
2325
import java.util.function.Function;
2426
import java.util.stream.Collectors;
@@ -34,11 +36,10 @@ public class RouteSpecImpl extends SerObject implements RouteSpec {
3436
/** default serial. */
3537
private static final long serialVersionUID = 1L;
3638

37-
public RouteSpecImpl(final Route.Definition route, final String summary, final String doc,
38-
final List<RouteParam> params, final RouteResponse rsp) {
39+
public RouteSpecImpl(final Route.Definition route, final String name, final String summary,
40+
final String doc, final List<RouteParam> params, final RouteResponse rsp) {
3941
put("method", route.method());
4042
put("pattern", route.pattern());
41-
String name = route.name();
4243
if (!name.equals("/anonymous")) {
4344
put("name", name);
4445
}
@@ -52,6 +53,7 @@ public RouteSpecImpl(final Route.Definition route, final String summary, final S
5253
put("doc", doc);
5354
put("params", params);
5455
put("response", rsp);
56+
put("attributes", new HashMap<>(route.attributes()));
5557
}
5658

5759
protected RouteSpecImpl() {
@@ -102,6 +104,11 @@ public RouteResponse response() {
102104
return get("response");
103105
}
104106

107+
@Override
108+
public Map<String, Object> attributes() {
109+
return get("attributes");
110+
}
111+
105112
@Override
106113
public String toString() {
107114
int len = 80;

jooby-spec/src/main/java/org/jooby/spec/RouteProcessor.java

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import java.lang.reflect.Type;
3030
import java.nio.file.Path;
3131
import java.util.ArrayList;
32+
import java.util.Collections;
33+
import java.util.HashMap;
3234
import java.util.HashSet;
3335
import java.util.List;
3436
import java.util.Map;
@@ -66,7 +68,6 @@
6668
import org.slf4j.LoggerFactory;
6769

6870
import com.github.javaparser.JavaParser;
69-
import com.github.javaparser.ParseException;
7071
import com.github.javaparser.ast.CompilationUnit;
7172
import com.github.javaparser.ast.Node;
7273
import com.google.common.base.Throwables;
@@ -222,37 +223,44 @@ private List<RouteSpec> processInternal(final Class<? extends Jooby> appClass,
222223
return ifspecs.get();
223224
}
224225

225-
/**
226-
* Main AST
227-
*/
228-
CompilationUnit unit = JavaParser.parse(src.resolveSource(appClass).get(), true);
229-
230-
/**
231-
* Find out app node.
232-
*/
233-
Node appNode = new AppCollector().accept(unit, ctx);
234-
if (appNode == null) {
235-
throw new IllegalStateException("Default constructor not found");
236-
}
237-
238226
/** Collect all routes and process them. */
239227
Set<String> owners = new HashSet<>();
240228
owners.add(appClass.getName());
241-
List<Entry<Object, Node>> routeNodes = new RouteCollector(owners::add).accept(appNode, ctx);
229+
230+
List<Entry<Object, Node>> routeNodes = new ArrayList<>();
231+
try {
232+
/**
233+
* Main AST
234+
*/
235+
CompilationUnit unit = JavaParser.parse(src.resolveSource(appClass).get(), true);
236+
237+
/**
238+
* Find out app node.
239+
*/
240+
Node appNode = new AppCollector().accept(unit, ctx);
241+
if (appNode == null) {
242+
throw new IllegalStateException("Default constructor not found");
243+
}
244+
245+
routeNodes = new RouteCollector(owners::add).accept(appNode, ctx);
246+
} catch (Exception x) {
247+
// ignore source code error
248+
log.debug("source code not found", x);
249+
}
242250

243251
int j = 0;
244252
for (int i = 0; i < routes.size(); i++) {
245253
Route.Definition route = routes.get(i);
246254
Object cursor = null;
255+
Method method = null;
247256
try {
248257
Route.Filter handler = route.filter();
249-
Method method = null;
250-
251258
// find out where the route was defined
252259
final String owner;
253260
if (handler instanceof Route.MethodHandler) {
254261
method = ((Route.MethodHandler) handler).method();
255262
owner = method.getDeclaringClass().getName();
263+
owners.add(owner);
256264
} else {
257265
// lambda script
258266
owner = handler.getClass().getName();
@@ -268,8 +276,8 @@ private List<RouteSpec> processInternal(final Class<? extends Jooby> appClass,
268276
cursor = candidate;
269277
log.debug("\n{}\n", candidate);
270278
/** doc and response codes . */
271-
Map<String, Object> doc = new DocCollector().accept((Node) candidate, route.method(),
272-
ctx);
279+
Map<String, Object> doc = new DocCollector(log).accept((Node) candidate,
280+
route.method(), ctx);
273281
Map<Integer, String> codes = (Map<Integer, String>) doc.remove("@statusCodes");
274282
String desc = (String) doc.remove("@text");
275283
String summary = (String) doc.remove("@summary");
@@ -279,6 +287,7 @@ private List<RouteSpec> processInternal(final Class<? extends Jooby> appClass,
279287
/** params and return type */
280288
List<RouteParam> params;
281289
RouteResponse rsp;
290+
String name = route.name();
282291
if (method == null) {
283292
// script params
284293
params = new RouteParamCollector(doc, route.method(), route.pattern())
@@ -287,6 +296,7 @@ private List<RouteSpec> processInternal(final Class<? extends Jooby> appClass,
287296
rsp = new ResponseTypeCollector().accept(entry.getValue(), ctx, retType, retDoc,
288297
codes);
289298
} else {
299+
name = method.getName();
290300
params = mvcParams(route, method, doc);
291301
rsp = new RouteResponseImpl(
292302
retType == null ? method.getGenericReturnType() : retType,
@@ -310,20 +320,33 @@ private List<RouteSpec> processInternal(final Class<? extends Jooby> appClass,
310320
});
311321

312322
/** Create spec . */
313-
specs.add(new RouteSpecImpl(route, summary, desc, params, rsp));
323+
specs.add(new RouteSpecImpl(route, name, summary, desc, params, rsp));
314324
} else {
315325
specs.add((RouteSpec) candidate);
316326
}
317327
} else {
318328
log.debug(" ignoring {} {} from {}", route.method(), route.pattern(), owner);
319329
}
320330
} catch (Exception ex) {
321-
log.error("ignoring {} {} with {}", route.method(), route.pattern(),
322-
cursor == null ? "<no source code>" : cursor.toString(), ex);
331+
if (method != null) {
332+
// MVC:
333+
String name = method.getName();
334+
List<RouteParam> params = mvcParams(route, method, Collections.emptyMap());
335+
RouteResponseImpl rsp = new RouteResponseImpl(method.getGenericReturnType(), null,
336+
new HashMap<>());
337+
/** Create spec . */
338+
specs.add(new RouteSpecImpl(route, name, null, null, params, rsp));
339+
} else {
340+
if (cursor == null) {
341+
log.debug("ignoring {} {} no source code available", route.method(), route.pattern(),
342+
ex);
343+
log.info("ignoring {} {} no source code available", route.method(), route.pattern());
344+
} else {
345+
log.error("ignoring {} {} reason {}", route.method(), route.pattern(), ex);
346+
}
347+
}
323348
}
324349
}
325-
} catch (ParseException ex) {
326-
throw new IllegalStateException("Error while processing " + appClass.getName(), ex);
327350
} catch (Exception ex) {
328351
throw new IllegalStateException("Error while processing " + appClass, ex);
329352
}

0 commit comments

Comments
 (0)