in juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/swagger/BasicSwaggerProviderSession.java [96:568]
public Swagger getSwagger() throws Exception {
InputStream is = ff.getStream(rci.getSimpleName() + ".json", locale).orElse(null);
Predicate<String> ne = StringUtils::isNotEmpty;
Predicate<Collection<?>> nec = CollectionUtils::isNotEmpty;
Predicate<Map<?,?>> nem = CollectionUtils::isNotEmpty;
// Load swagger JSON from classpath.
JsonMap omSwagger = Json5.DEFAULT.read(is, JsonMap.class);
if (omSwagger == null)
omSwagger = new JsonMap();
// Combine it with @Rest(swagger)
for (Rest rr : rci.getAnnotations(context, Rest.class)) {
JsonMap sInfo = omSwagger.getMap("info", true);
sInfo
.appendIf(ne, "title",
firstNonEmpty(
sInfo.getString("title"),
resolve(rr.title())
)
)
.appendIf(ne, "description",
firstNonEmpty(
sInfo.getString("description"),
resolve(rr.description())
)
);
org.apache.juneau.rest.annotation.Swagger r = rr.swagger();
omSwagger.append(parseMap(r.value(), "@Swagger(value) on class {0}", c));
if (! SwaggerAnnotation.empty(r)) {
JsonMap info = omSwagger.getMap("info", true);
info
.appendIf(ne, "title", resolve(r.title()))
.appendIf(ne, "description", resolve(r.description()))
.appendIf(ne, "version", resolve(r.version()))
.appendIf(ne, "termsOfService", resolve(r.termsOfService()))
.appendIf(nem, "contact",
merge(
info.getMap("contact"),
toMap(r.contact(), "@Swagger(contact) on class {0}", c)
)
)
.appendIf(nem, "license",
merge(
info.getMap("license"),
toMap(r.license(), "@Swagger(license) on class {0}", c)
)
);
}
omSwagger
.appendIf(nem, "externalDocs",
merge(
omSwagger.getMap("externalDocs"),
toMap(r.externalDocs(), "@Swagger(externalDocs) on class {0}", c)
)
)
.appendIf(nec, "tags",
merge(
omSwagger.getList("tags"),
toList(r.tags(), "@Swagger(tags) on class {0}", c)
)
);
}
omSwagger.appendIf(nem, "externalDocs", parseMap(mb.findFirstString("externalDocs"), "Messages/externalDocs on class {0}", c));
JsonMap info = omSwagger.getMap("info", true);
info
.appendIf(ne, "title", resolve(mb.findFirstString("title")))
.appendIf(ne, "description", resolve(mb.findFirstString("description")))
.appendIf(ne, "version", resolve(mb.findFirstString("version")))
.appendIf(ne, "termsOfService", resolve(mb.findFirstString("termsOfService")))
.appendIf(nem, "contact", parseMap(mb.findFirstString("contact"), "Messages/contact on class {0}", c))
.appendIf(nem, "license", parseMap(mb.findFirstString("license"), "Messages/license on class {0}", c));
if (info.isEmpty())
omSwagger.remove("info");
JsonList
produces = omSwagger.getList("produces", true),
consumes = omSwagger.getList("consumes", true);
if (consumes.isEmpty())
consumes.addAll(context.getConsumes());
if (produces.isEmpty())
produces.addAll(context.getProduces());
Map<String,JsonMap> tagMap = map();
if (omSwagger.containsKey("tags")) {
for (JsonMap om : omSwagger.getList("tags").elements(JsonMap.class)) {
String name = om.getString("name");
if (name == null)
throw new SwaggerException(null, "Tag definition found without name in swagger JSON.");
tagMap.put(name, om);
}
}
String s = mb.findFirstString("tags");
if (s != null) {
for (JsonMap m : parseListOrCdl(s, "Messages/tags on class {0}", c).elements(JsonMap.class)) {
String name = m.getString("name");
if (name == null)
throw new SwaggerException(null, "Tag definition found without name in resource bundle on class {0}", c) ;
if (tagMap.containsKey(name))
tagMap.get(name).putAll(m);
else
tagMap.put(name, m);
}
}
// Load our existing bean definitions into our session.
JsonMap definitions = omSwagger.getMap("definitions", true);
for (String defId : definitions.keySet())
js.addBeanDef(defId, new JsonMap(definitions.getMap(defId)));
// Iterate through all the @RestOp methods.
for (RestOpContext sm : context.getRestOperations().getOpContexts()) {
BeanSession bs = sm.getBeanContext().getSession();
Method m = sm.getJavaMethod();
MethodInfo mi = MethodInfo.of(m);
AnnotationList al = mi.getAnnotationList(REST_OP_GROUP);
String mn = m.getName();
// Get the operation from the existing swagger so far.
JsonMap op = getOperation(omSwagger, sm.getPathPattern(), sm.getHttpMethod().toLowerCase());
// Add @RestOp(swagger)
Value<OpSwagger> _ms = Value.empty();
al.forEachValue(OpSwagger.class, "swagger", OpSwaggerAnnotation::notEmpty, x -> _ms.set(x));
OpSwagger ms = _ms.orElseGet(()->OpSwaggerAnnotation.create().build());
op.append(parseMap(ms.value(), "@OpSwagger(value) on class {0} method {1}", c, m));
op.appendIf(ne, "operationId",
firstNonEmpty(
resolve(ms.operationId()),
op.getString("operationId"),
mn
)
);
Value<String> _summary = Value.empty();
al.forEachValue(String.class, "summary", NOT_EMPTY, x -> _summary.set(x));
op.appendIf(ne, "summary",
firstNonEmpty(
resolve(ms.summary()),
resolve(mb.findFirstString(mn + ".summary")),
op.getString("summary"),
resolve(_summary.orElse(null))
)
);
Value<String[]> _description = Value.empty();
al.forEachValue(String[].class, "description",x -> x.length > 0, x -> _description.set(x));
op.appendIf(ne, "description",
firstNonEmpty(
resolve(ms.description()),
resolve(mb.findFirstString(mn + ".description")),
op.getString("description"),
resolve(_description.orElse(new String[0]))
)
);
op.appendIf(ne, "deprecated",
firstNonEmpty(
resolve(ms.deprecated()),
(m.getAnnotation(Deprecated.class) != null || m.getDeclaringClass().getAnnotation(Deprecated.class) != null) ? "true" : null
)
);
op.appendIf(nec, "tags",
merge(
parseListOrCdl(mb.findFirstString(mn + ".tags"), "Messages/tags on class {0} method {1}", c, m),
parseListOrCdl(ms.tags(), "@OpSwagger(tags) on class {0} method {1}", c, m)
)
);
op.appendIf(nec, "schemes",
merge(
parseListOrCdl(mb.findFirstString(mn + ".schemes"), "Messages/schemes on class {0} method {1}", c, m),
parseListOrCdl(ms.schemes(), "@OpSwagger(schemes) on class {0} method {1}", c, m)
)
);
op.appendIf(nec, "consumes",
firstNonEmpty(
parseListOrCdl(mb.findFirstString(mn + ".consumes"), "Messages/consumes on class {0} method {1}", c, m),
parseListOrCdl(ms.consumes(), "@OpSwagger(consumes) on class {0} method {1}", c, m)
)
);
op.appendIf(nec, "produces",
firstNonEmpty(
parseListOrCdl(mb.findFirstString(mn + ".produces"), "Messages/produces on class {0} method {1}", c, m),
parseListOrCdl(ms.produces(), "@OpSwagger(produces) on class {0} method {1}", c, m)
)
);
op.appendIf(nec, "parameters",
merge(
parseList(mb.findFirstString(mn + ".parameters"), "Messages/parameters on class {0} method {1}", c, m),
parseList(ms.parameters(), "@OpSwagger(parameters) on class {0} method {1}", c, m)
)
);
op.appendIf(nem, "responses",
merge(
parseMap(mb.findFirstString(mn + ".responses"), "Messages/responses on class {0} method {1}", c, m),
parseMap(ms.responses(), "@OpSwagger(responses) on class {0} method {1}", c, m)
)
);
op.appendIf(nem, "externalDocs",
merge(
op.getMap("externalDocs"),
parseMap(mb.findFirstString(mn + ".externalDocs"), "Messages/externalDocs on class {0} method {1}", c, m),
toMap(ms.externalDocs(), "@OpSwagger(externalDocs) on class {0} method {1}", c, m)
)
);
if (op.containsKey("tags"))
for (String tag : op.getList("tags").elements(String.class))
if (! tagMap.containsKey(tag))
tagMap.put(tag, JsonMap.of("name", tag));
JsonMap paramMap = new JsonMap();
if (op.containsKey("parameters"))
for (JsonMap param : op.getList("parameters").elements(JsonMap.class))
paramMap.put(param.getString("in") + '.' + ("body".equals(param.getString("in")) ? "body" : param.getString("name")), param);
// Finally, look for parameters defined on method.
for (ParamInfo mpi : mi.getParams()) {
ClassInfo pt = mpi.getParameterType();
Type type = pt.innerType();
if (mpi.hasAnnotation(Content.class) || pt.hasAnnotation(Content.class)) {
JsonMap param = paramMap.getMap(BODY + ".body", true).append("in", BODY);
JsonMap schema = getSchema(param.getMap("schema"), type, bs);
mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(schema, x));
mpi.forEachAnnotation(Content.class, x -> true, x -> merge(schema, x.schema()));
pushupSchemaFields(BODY, param, schema);
param.appendIf(nem, "schema", schema);
param.putIfAbsent("required", true);
addBodyExamples(sm, param, false, type, locale);
} else if (mpi.hasAnnotation(Query.class) || pt.hasAnnotation(Query.class)) {
String name = QueryAnnotation.findName(mpi).orElse(null);
JsonMap param = paramMap.getMap(QUERY + "." + name, true).append("name", name).append("in", QUERY);
mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(param, x));
mpi.forEachAnnotation(Query.class, x -> true, x -> merge(param, x.schema()));
pushupSchemaFields(QUERY, param, getSchema(param.getMap("schema"), type, bs));
addParamExample(sm, param, QUERY, type);
} else if (mpi.hasAnnotation(FormData.class) || pt.hasAnnotation(FormData.class)) {
String name = FormDataAnnotation.findName(mpi).orElse(null);
JsonMap param = paramMap.getMap(FORM_DATA + "." + name, true).append("name", name).append("in", FORM_DATA);
mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(param, x));
mpi.forEachAnnotation(FormData.class, x -> true, x -> merge(param, x.schema()));
pushupSchemaFields(FORM_DATA, param, getSchema(param.getMap("schema"), type, bs));
addParamExample(sm, param, FORM_DATA, type);
} else if (mpi.hasAnnotation(Header.class) || pt.hasAnnotation(Header.class)) {
String name = HeaderAnnotation.findName(mpi).orElse(null);
JsonMap param = paramMap.getMap(HEADER + "." + name, true).append("name", name).append("in", HEADER);
mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(param, x));
mpi.forEachAnnotation(Header.class, x -> true, x -> merge(param, x.schema()));
pushupSchemaFields(HEADER, param, getSchema(param.getMap("schema"), type, bs));
addParamExample(sm, param, HEADER, type);
} else if (mpi.hasAnnotation(Path.class) || pt.hasAnnotation(Path.class)) {
String name = PathAnnotation.findName(mpi).orElse(null);
JsonMap param = paramMap.getMap(PATH + "." + name, true).append("name", name).append("in", PATH);
mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(param, x));
mpi.forEachAnnotation(Path.class, x -> true, x -> merge(param, x.schema()));
pushupSchemaFields(PATH, param, getSchema(param.getMap("schema"), type, bs));
addParamExample(sm, param, PATH, type);
param.putIfAbsent("required", true);
}
}
if (! paramMap.isEmpty())
op.put("parameters", paramMap.values());
JsonMap responses = op.getMap("responses", true);
for (ClassInfo eci : mi.getExceptionTypes()) {
if (eci.hasAnnotation(Response.class)) {
List<Response> la = eci.getAnnotations(context, Response.class);
List<StatusCode> la2 = eci.getAnnotations(context, StatusCode.class);
Set<Integer> codes = getCodes(la2, 500);
for (Response a : la) {
for (Integer code : codes) {
JsonMap om = responses.getMap(String.valueOf(code), true);
merge(om, a);
JsonMap schema = getSchema(om.getMap("schema"), m.getGenericReturnType(), bs);
eci.forEachAnnotation(Schema.class, x -> true, x -> merge(schema, x));
pushupSchemaFields(RESPONSE, om, schema);
om.appendIf(nem, "schema", schema);
}
}
List<MethodInfo> methods = eci.getMethods();
for (int i = methods.size()-1; i>=0; i--) {
MethodInfo ecmi = methods.get(i);
Header a = ecmi.getAnnotation(Header.class);
if (a == null)
a = ecmi.getReturnType().unwrap(Value.class,Optional.class).getAnnotation(Header.class);
if (a != null && ! isMulti(a)) {
String ha = a.name();
for (Integer code : codes) {
JsonMap header = responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(ha, true);
ecmi.forEachAnnotation(context, Schema.class, x-> true, x -> merge(header, x));
ecmi.getReturnType().unwrap(Value.class,Optional.class).forEachAnnotation(Schema.class, x -> true, x -> merge(header, x));
pushupSchemaFields(RESPONSE_HEADER, header, getSchema(header.getMap("schema"), ecmi.getReturnType().unwrap(Value.class,Optional.class).innerType(), bs));
}
}
}
}
}
if (mi.hasAnnotation(Response.class) || mi.getReturnType().unwrap(Value.class,Optional.class).hasAnnotation(Response.class)) {
List<Response> la = list();
mi.forEachAnnotation(context, Response.class, x -> true, x -> la.add(x));
List<StatusCode> la2 = list();
mi.forEachAnnotation(context, StatusCode.class, x -> true, x -> la2.add(x));
Set<Integer> codes = getCodes(la2, 200);
for (Response a : la) {
for (Integer code : codes) {
JsonMap om = responses.getMap(String.valueOf(code), true);
merge(om, a);
JsonMap schema = getSchema(om.getMap("schema"), m.getGenericReturnType(), bs);
mi.forEachAnnotation(context, Schema.class, x -> true, x -> merge(schema, x));
pushupSchemaFields(RESPONSE, om, schema);
om.appendIf(nem, "schema", schema);
addBodyExamples(sm, om, true, m.getGenericReturnType(), locale);
}
}
if (mi.getReturnType().hasAnnotation(Response.class)) {
List<MethodInfo> methods = mi.getReturnType().getMethods();
for (int i = methods.size()-1; i>=0; i--) {
MethodInfo ecmi = methods.get(i);
if (ecmi.hasAnnotation(Header.class)) {
Header a = ecmi.getAnnotation(Header.class);
String ha = a.name();
if (! isMulti(a)) {
for (Integer code : codes) {
JsonMap header = responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(ha, true);
ecmi.forEachAnnotation(context, Schema.class, x -> true, x -> merge(header, x));
ecmi.getReturnType().unwrap(Value.class,Optional.class).forEachAnnotation(Schema.class, x -> true, x -> merge(header, x));
merge(header, a.schema());
pushupSchemaFields(RESPONSE_HEADER, header, getSchema(header, ecmi.getReturnType().innerType(), bs));
}
}
}
}
}
} else if (m.getGenericReturnType() != void.class) {
JsonMap om = responses.getMap("200", true);
ClassInfo pt2 = ClassInfo.of(m.getGenericReturnType());
JsonMap schema = getSchema(om.getMap("schema"), m.getGenericReturnType(), bs);
pt2.forEachAnnotation(Schema.class, x -> true, x -> merge(schema, x));
pushupSchemaFields(RESPONSE, om, schema);
om.appendIf(nem, "schema", schema);
addBodyExamples(sm, om, true, m.getGenericReturnType(), locale);
}
// Finally, look for Value @Header parameters defined on method.
for (ParamInfo mpi : mi.getParams()) {
ClassInfo pt = mpi.getParameterType();
if (pt.is(Value.class) && (mpi.hasAnnotation(Header.class) || pt.hasAnnotation(Header.class))) {
List<Header> la = list();
mpi.forEachAnnotation(Header.class, x -> true, x -> la.add(x));
pt.forEachAnnotation(Header.class, x -> true, x -> la.add(x));
List<StatusCode> la2 = list();
mpi.forEachAnnotation(StatusCode.class, x -> true, x -> la2.add(x));
pt.forEachAnnotation(StatusCode.class, x -> true, x -> la2.add(x));
Set<Integer> codes = getCodes(la2, 200);
String name = HeaderAnnotation.findName(mpi).orElse(null);
Type type = Value.unwrap(mpi.getParameterType().innerType());
for (Header a : la) {
if (! isMulti(a)) {
for (Integer code : codes) {
JsonMap header = responses.getMap(String.valueOf(code), true).getMap("headers", true).getMap(name, true);
mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(header, x));
merge(header, a.schema());
pushupSchemaFields(RESPONSE_HEADER, header, getSchema(header, type, bs));
}
}
}
} else if (mpi.hasAnnotation(Response.class) || pt.hasAnnotation(Response.class)) {
List<Response> la = list();
mpi.forEachAnnotation(Response.class, x -> true, x -> la.add(x));
pt.forEachAnnotation(Response.class, x -> true, x -> la.add(x));
List<StatusCode> la2 = list();
mpi.forEachAnnotation(StatusCode.class, x -> true, x -> la2.add(x));
pt.forEachAnnotation(StatusCode.class, x -> true, x -> la2.add(x));
Set<Integer> codes = getCodes(la2, 200);
Type type = Value.unwrap(mpi.getParameterType().innerType());
for (Response a : la) {
for (Integer code : codes) {
JsonMap om = responses.getMap(String.valueOf(code), true);
merge(om, a);
JsonMap schema = getSchema(om.getMap("schema"), type, bs);
mpi.forEachAnnotation(Schema.class, x -> true, x -> merge(schema, x));
la.forEach(x -> merge(schema, x.schema()));
pushupSchemaFields(RESPONSE, om, schema);
om.appendIf(nem, "schema", schema);
}
}
}
}
// Add default response descriptions.
for (Map.Entry<String,Object> e : responses.entrySet()) {
String key = e.getKey();
JsonMap val = responses.getMap(key);
if (StringUtils.isDecimal(key))
val.appendIfAbsentIf(ne, "description", RestUtils.getHttpResponseText(Integer.parseInt(key)));
}
if (responses.isEmpty())
op.remove("responses");
else
op.put("responses", new TreeMap<>(responses));
if (! op.containsKey("consumes")) {
List<MediaType> mConsumes = sm.getSupportedContentTypes();
if (! mConsumes.equals(consumes))
op.put("consumes", mConsumes);
}
if (! op.containsKey("produces")) {
List<MediaType> mProduces = sm.getSupportedAcceptTypes();
if (! mProduces.equals(produces))
op.put("produces", mProduces);
}
}
if (js.getBeanDefs() != null)
for (Map.Entry<String,JsonMap> e : js.getBeanDefs().entrySet())
definitions.put(e.getKey(), fixSwaggerExtensions(e.getValue()));
if (definitions.isEmpty())
omSwagger.remove("definitions");
if (! tagMap.isEmpty())
omSwagger.put("tags", tagMap.values());
if (consumes.isEmpty())
omSwagger.remove("consumes");
if (produces.isEmpty())
omSwagger.remove("produces");
// try {
// if (! omSwagger.isEmpty())
// assertNoEmpties(omSwagger);
// } catch (SwaggerException e1) {
// System.err.println(omSwagger.toString(Json5Serializer.DEFAULT_READABLE));
// throw e1;
// }
try {
String swaggerJson = Json5Serializer.DEFAULT_READABLE.toString(omSwagger);
// System.err.println(swaggerJson);
return jp.parse(swaggerJson, Swagger.class);
} catch (Exception e) {
throw new ServletException("Error detected in swagger.", e);
}
}