You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2018/04/28 23:38:58 UTC

[juneau] branch master updated: Swagger UI tests.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 1a3a0e4  Swagger UI tests.
1a3a0e4 is described below

commit 1a3a0e4a96819a30a4b4d10019724558787bca95
Author: JamesBognar <ja...@apache.org>
AuthorDate: Sat Apr 28 19:38:43 2018 -0400

    Swagger UI tests.
---
 .../main/java/org/apache/juneau/BeanSession.java   |   2 +
 .../main/java/org/apache/juneau/ObjectList.java    |  37 ++-
 .../src/main/java/org/apache/juneau/ObjectMap.java |  60 +++--
 .../apache/juneau/rest/BasicRestInfoProvider.java  |  50 ++--
 .../juneau/rest/annotation/MethodSwagger.java      |  10 +-
 .../juneau/rest/BasicRestInfoProviderTest.java     | 263 ++++++++++++++++++++-
 .../rest/BasicRestInfoProviderTest_swagger.json    | 161 ++++++++++++-
 7 files changed, 503 insertions(+), 80 deletions(-)

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
index 460f4cc..e518e49 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/BeanSession.java
@@ -534,6 +534,7 @@ public class BeanSession extends Session {
 						return (T)m;
 					} else if (!type.canCreateNewInstanceFromString(outer)) {
 						ObjectMap m = new ObjectMap(value.toString());
+						m.setBeanSession(this);
 						return convertToMemberType(outer, m, type);
 					}
 				} catch (Exception e) {
@@ -559,6 +560,7 @@ public class BeanSession extends Session {
 						String s = value.toString();
 						if (isObjectList(s, false)) {
 							ObjectList l2 = new ObjectList(s);
+							l2.setBeanSession(this);
 							for (Object o : l2)
 								l.add(elementType.isObject() ? o : convertToMemberType(l, o, elementType));
 						} else {
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java
index a7ebbd1..cec754d 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectList.java
@@ -141,11 +141,11 @@ public class ObjectList extends LinkedList<Object> {
 	 * @throws ParseException If the input contains a syntax error or is malformed.
 	 */
 	public ObjectList(CharSequence s, Parser p) throws ParseException {
-		this(p == null ? BeanContext.DEFAULT.createSession() : p.createBeanSession());
+		this(p == null ? null : p.createBeanSession());
 		if (p == null)
 			p = JsonParser.DEFAULT;
 		if (s != null)
-			p.parseIntoCollection(s, this, session.object());
+			p.parseIntoCollection(s, this, bs().object());
 	}
 
 	/**
@@ -169,7 +169,7 @@ public class ObjectList extends LinkedList<Object> {
 	 * @throws IOException If a problem occurred trying to read from the reader.
 	 */
 	public ObjectList(Reader r, Parser p) throws ParseException, IOException {
-		this(p == null ? BeanContext.DEFAULT.createSession() : p.createBeanSession());
+		this(p == null ? null : p.createBeanSession());
 		parseReader(r, p);
 	}
 
@@ -183,21 +183,19 @@ public class ObjectList extends LinkedList<Object> {
 	 * @throws IOException If a problem occurred trying to read from the reader.
 	 */
 	public ObjectList(Reader r) throws ParseException, IOException {
-		this(BeanContext.DEFAULT.createSession());
 		parseReader(r, JsonParser.DEFAULT);
 	}
 
 	private void parseReader(Reader r, Parser p) throws ParseException {
 		if (p == null)
 			p = JsonParser.DEFAULT;
-		p.parseIntoCollection(r, this, session.object());
+		p.parseIntoCollection(r, this, bs().object());
 	}
 
 	/**
 	 * Construct an empty JSON array. (i.e. an empty {@link LinkedList}).
 	 */
 	public ObjectList() {
-		this(BeanContext.DEFAULT.createSession());
 	}
 
 	/**
@@ -246,6 +244,15 @@ public class ObjectList extends LinkedList<Object> {
 	}
 
 	/**
+	 * Returns the {@link BeanSession} currently associated with this list.
+	 * 
+	 * @return The {@link BeanSession} currently associated with this list.
+	 */
+	public BeanSession getBeanSession() {
+		return session;
+	}
+
+	/**
 	 * Convenience method for adding multiple objects to this list.
 	 * 
 	 * @param o The objects to add to the list.
@@ -324,7 +331,7 @@ public class ObjectList extends LinkedList<Object> {
 	 * @return The converted entry.
 	 */
 	public <T> T get(int index, Class<T> type) {
-		return session.convertToType(get(index), type);
+		return bs().convertToType(get(index), type);
 	}
 
 	/**
@@ -372,7 +379,7 @@ public class ObjectList extends LinkedList<Object> {
 	 * @return The converted entry.
 	 */
 	public <T> T get(int index, Type type, Type...args) {
-		return session.convertToType(get(index), type, args);
+		return bs().convertToType(get(index), type, args);
 	}
 
 	/**
@@ -439,7 +446,7 @@ public class ObjectList extends LinkedList<Object> {
 	 * @throws InvalidDataConversionException If value cannot be converted.
 	 */
 	public <K,V> Map<K,V> getMap(int index, Class<K> keyType, Class<V> valType) {
-		return session.convertToType(get(index), Map.class, keyType, valType);
+		return bs().convertToType(get(index), Map.class, keyType, valType);
 	}
 
 	/**
@@ -462,7 +469,7 @@ public class ObjectList extends LinkedList<Object> {
 	 * @throws InvalidDataConversionException If value cannot be converted.
 	 */
 	public <E> List<E> getList(int index, Class<E> elementType) {
-		return session.convertToType(get(index), List.class, elementType);
+		return bs().convertToType(get(index), List.class, elementType);
 	}
 
 	/**
@@ -671,7 +678,7 @@ public class ObjectList extends LinkedList<Object> {
 
 					@Override /* Iterator */
 					public E next() {
-						return session.convertToType(i.next(), childType);
+						return bs().convertToType(i.next(), childType);
 					}
 
 					@Override /* Iterator */
@@ -691,7 +698,7 @@ public class ObjectList extends LinkedList<Object> {
 	 * @return The data type of the object at the specified index, or <jk>null</jk> if the value is null.
 	 */
 	public ClassMeta<?> getClassMeta(int index) {
-		return session.getClassMetaForObject(get(index));
+		return bs().getClassMetaForObject(get(index));
 	}
 
 	private PojoRest getPojoRest() {
@@ -751,4 +758,10 @@ public class ObjectList extends LinkedList<Object> {
 			throw new RuntimeException(e);
 		}
 	}
+	
+	BeanSession bs() {
+		if (session == null)
+			session = BeanContext.DEFAULT.createBeanSession();
+		return session;
+	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
index f0484dd..8670176 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ObjectMap.java
@@ -149,11 +149,11 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 	 * @throws ParseException If the input contains a syntax error or is malformed.
 	 */
 	public ObjectMap(CharSequence s, Parser p) throws ParseException {
-		this(p == null ? BeanContext.DEFAULT.createSession() : p.createBeanSession());
+		this(p == null ? null : p.createBeanSession());
 		if (p == null)
 			p = JsonParser.DEFAULT;
 		if (! StringUtils.isEmpty(s))
-			p.parseIntoMap(s, this, session.string(), session.object());
+			p.parseIntoMap(s, this, bs().string(), bs().object());
 	}
 
 	/**
@@ -175,7 +175,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 	 * @throws IOException If a problem occurred trying to read from the reader.
 	 */
 	public ObjectMap(Reader r, Parser p) throws ParseException, IOException {
-		this(p == null ? BeanContext.DEFAULT.createSession() : p.createBeanSession());
+		this(p == null ? null : p.createBeanSession());
 		parseReader(r, p);
 	}
 
@@ -187,21 +187,19 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 	 * @throws IOException If a problem occurred trying to read from the reader.
 	 */
 	public ObjectMap(Reader r) throws ParseException, IOException {
-		this(BeanContext.DEFAULT.createSession());
 		parseReader(r, JsonParser.DEFAULT);
 	}
 
 	private void parseReader(Reader r, Parser p) throws ParseException {
 		if (p == null)
 			p = JsonParser.DEFAULT;
-		p.parseIntoMap(r, this, session.string(), session.object());
+		p.parseIntoMap(r, this, bs().string(), bs().object());
 	}
 
 	/**
 	 * Construct an empty JSON object (i.e. an empty {@link LinkedHashMap}).
 	 */
 	public ObjectMap() {
-		this(BeanContext.DEFAULT.createSession());
 	}
 
 	/**
@@ -518,7 +516,10 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 	 * @return The value, or <jk>null</jk> if the entry doesn't exist.
 	 */
 	public <T> T getWithDefault(String key, T def, Type type, Type...args) {
-		T t = session.convertToType(get(key), type, args);
+		Object o = get(key);
+		if (o == null)
+			return def;
+		T t = bs().convertToType(o, type, args);
 		return t == null ? def : t;
 	}
 
@@ -540,7 +541,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 			if (o == null)
 				return null;
 			PojoSwap swap = pojoSwap;
-			return (T)swap.unswap(session, o, null);
+			return (T)swap.unswap(bs(), o, null);
 		} catch (ParseException e) {
 			throw e;
 		} catch (Exception e) {
@@ -925,7 +926,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 		Object o = get(key);
 		if (o == null)
 			return def;
-		return session.convertToType(o, Map.class, keyType, valType);
+		return bs().convertToType(o, Map.class, keyType, valType);
 	}
 
 	/**
@@ -970,7 +971,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 		Object o = get(key);
 		if (o == null)
 			return def;
-		return session.convertToType(o, List.class, elementType);
+		return bs().convertToType(o, List.class, elementType);
 	}
 
 	/**
@@ -1209,7 +1210,7 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 	 * 	The data type of the object at the specified key, or <jk>null</jk> if the value is null or does not exist.
 	 */
 	public ClassMeta<?> getClassMeta(String key) {
-		return session.getClassMetaForObject(get(key));
+		return bs().getClassMetaForObject(get(key));
 	}
 
 	/**
@@ -1375,9 +1376,10 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 	 */
 	@SuppressWarnings("unchecked")
 	public <T> T cast(Class<T> type) {
-		ClassMeta<?> c2 = session.getClassMeta(type);
-		String typePropertyName = session.getBeanTypePropertyName(c2);
-		ClassMeta<?> c1 = session.getBeanRegistry().getClassMeta((String)get(typePropertyName));
+		BeanSession bs = bs();
+		ClassMeta<?> c2 = bs.getClassMeta(type);
+		String typePropertyName = bs.getBeanTypePropertyName(c2);
+		ClassMeta<?> c1 = bs.getBeanRegistry().getClassMeta((String)get(typePropertyName));
 		ClassMeta<?> c = c1 == null ? c2 : narrowClassMeta(c1, c2);
 		if (c.isObject())
 			return (T)this;
@@ -1395,7 +1397,8 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 	 */
 	@SuppressWarnings({"unchecked"})
 	public <T> T cast(ClassMeta<T> cm) {
-		ClassMeta<?> c1 = session.getBeanRegistry().getClassMeta((String)get(session.getBeanTypePropertyName(cm)));
+		BeanSession bs = bs();
+		ClassMeta<?> c1 = bs.getBeanRegistry().getClassMeta((String)get(bs.getBeanTypePropertyName(cm)));
 		ClassMeta<?> c = narrowClassMeta(c1, cm);
 		return (T)cast2(c);
 	}
@@ -1413,11 +1416,11 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 		if (c1.isMap()) {
 			ClassMeta<?> k = getNarrowedClassMeta(c1.getKeyType(), c2.getKeyType());
 			ClassMeta<?> v = getNarrowedClassMeta(c1.getValueType(), c2.getValueType());
-			return session.getClassMeta(c.getInnerClass(), k, v);
+			return bs().getClassMeta(c.getInnerClass(), k, v);
 		}
 		if (c1.isCollection()) {
 			ClassMeta<?> e = getNarrowedClassMeta(c1.getElementType(), c2.getElementType());
-			return session.getClassMeta(c.getInnerClass(), e);
+			return bs().getClassMeta(c.getInnerClass(), e);
 		}
 		return c;
 	}
@@ -1438,23 +1441,24 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 	@SuppressWarnings({"unchecked","rawtypes"})
 	private <T> T cast2(ClassMeta<T> cm) {
 
+		BeanSession bs = bs();
 		try {
 			Object value = get("value");
 
 			if (cm.isMap()) {
-				Map m2 = (cm.canCreateNewInstance() ? (Map)cm.newInstance() : new ObjectMap(session));
+				Map m2 = (cm.canCreateNewInstance() ? (Map)cm.newInstance() : new ObjectMap(bs));
 				ClassMeta<?> kType = cm.getKeyType(), vType = cm.getValueType();
 				for (Map.Entry<String,Object> e : entrySet()) {
 					Object k = e.getKey();
 					Object v = e.getValue();
-					if (! k.equals(session.getBeanTypePropertyName(cm))) {
+					if (! k.equals(bs.getBeanTypePropertyName(cm))) {
 
 						// Attempt to recursively cast child maps.
 						if (v instanceof ObjectMap)
 							v = ((ObjectMap)v).cast(vType);
 
-						k = (kType.isString() ? k : session.convertToType(k, kType));
-						v = (vType.isObject() ? v : session.convertToType(v, vType));
+						k = (kType.isString() ? k : bs.convertToType(k, kType));
+						v = (vType.isObject() ? v : bs.convertToType(v, vType));
 
 						m2.put(k, v);
 					}
@@ -1462,13 +1466,13 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 				return (T)m2;
 
 			} else if (cm.isBean()) {
-				BeanMap<? extends T> bm = session.newBeanMap(cm.getInnerClass());
+				BeanMap<? extends T> bm = bs.newBeanMap(cm.getInnerClass());
 
 				// Iterate through all the entries in the map and set the individual field values.
 				for (Map.Entry<String,Object> e : entrySet()) {
 					String k = e.getKey();
 					Object v = e.getValue();
-					if (! k.equals(session.getBeanTypePropertyName(cm))) {
+					if (! k.equals(bs.getBeanTypePropertyName(cm))) {
 
 						// Attempt to recursively cast child maps.
 						if (v instanceof ObjectMap)
@@ -1482,10 +1486,10 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 
 			} else if (cm.isCollectionOrArray()) {
 				List items = (List)get("items");
-				return session.convertToType(items, cm);
+				return bs.convertToType(items, cm);
 
 			} else if (value != null) {
-				return session.convertToType(value, cm);
+				return bs.convertToType(value, cm);
 			}
 
 		} catch (Exception e) {
@@ -1606,4 +1610,10 @@ public class ObjectMap extends LinkedHashMap<String,Object> {
 			}
 		};
 	}
+	
+	private BeanSession bs() {
+		if (session == null)
+			session = BeanContext.DEFAULT.createBeanSession();
+		return session;
+	}
 }
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
index 80cc9d8..55ed686 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/BasicRestInfoProvider.java
@@ -277,20 +277,20 @@ public class BasicRestInfoProvider implements RestInfoProvider {
 			MethodSwagger ms = rm.swagger();
 
 			op.putAll(parseMap(joinnl(ms.value()), vr, true, false, "@MethodSwagger(value) on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "operationId", vr.resolve(ms.operationId()));
 			op.appendIf(true, true, true, "summary", vr.resolve(rm.summary()));
 			op.appendIf(true, true, true, "summary", vr.resolve(joinnl(ms.summary())));
 			op.appendIf(true, true, true, "description", vr.resolve(joinnl(rm.description())));
 			op.appendIf(true, true, true, "description", vr.resolve(joinnl(ms.description())));
 			op.appendIf(true, true, true, "deprecated", vr.resolve(ms.deprecated()));
 			op.appendIf(true, true, true, "externalDocs", parseMap(joinnl(ms.externalDocs()), vr, false, true, "@MethodSwagger(externalDocs) on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "tags", parseListOrCdl(joinnl(ms.tags()), vr, false, true, "@MethodSwagger(tags) on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "schemes", parseListOrCdl(joinnl(ms.schemes()), vr, false, true, "@MethodSwagger(schemes) on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "consumes", parseListOrCdl(joinnl(ms.consumes()), vr, false, true, "@MethodSwagger(consumes) on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "produces", parseListOrCdl(joinnl(ms.produces()), vr, false, true, "@MethodSwagger(produces) on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "parameters", parseList(joinnl(ms.parameters()), vr, false, true, "@MethodSwagger(parameters) on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "responses", parseMap(joinnl(ms.responses()), vr, false, true, "@MethodSwagger(responses) on class {0} method {1}", c, m));
 			
-			if (ms.parameters().length > 0)
-				op.getObjectList("parameters", true).addAll(parseList(joinnl(ms.parameters()), vr, false, false, "@MethodSwagger(parameters) on class {0} method {1}", c, m));
-			if (ms.responses().length > 0)
-				op.getObjectMap("responses", true).putAll(parseMap(joinnl(ms.responses()), vr, false, false, "@MethodSwagger(responses) on class {0} method {1}", c, m));
-			if (ms.tags().length > 0)
-				op.getObjectList("tags", true).addAll(parseListOrCdl(joinnl(ms.tags()), vr, false, false, "@MethodSwagger(tags) on class {0} method {1}", c, m));
-
 			op.putIfNotExists("operationId", mn);
 			
 			if (m.getAnnotation(Deprecated.class) != null)
@@ -298,27 +298,22 @@ public class BasicRestInfoProvider implements RestInfoProvider {
 
 			op.appendIf(true, true, true, "summary", vr.resolve(mb.findFirstString(locale, mn + ".summary")));
 			op.appendIf(true, true, true, "description", vr.resolve(mb.findFirstString(locale, mn + ".description")));
+			op.appendIf(true, true, true, "externalDocs", parseMap(mb.findFirstString(locale, mn + ".description"), vr, false, true, "Messages/externalDocs on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "tags", parseListOrCdl(mb.findFirstString(locale, mn + ".tags"), vr, false, true, "Messages/tags on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "schemes", parseListOrCdl(mb.findFirstString(locale, mn + ".schemes"), vr, false, true, "Messages/schemes on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "consumes", parseListOrCdl(mb.findFirstString(locale, mn + ".consumes"), vr, false, true, "Messages/consumes on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "produces", parseListOrCdl(mb.findFirstString(locale, mn + ".produces"), vr, false, true, "Messages/produces on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "parameters", parseList(mb.findFirstString(locale, mn + ".parameters"), vr, false, true, "Messages/parameters on class {0} method {1}", c, m));
+			op.appendIf(true, true, true, "responses", parseMap(mb.findFirstString(locale, mn + ".responses"), vr, false, true, "Messages/responses on class {0} method {1}", c, m));
 
-			s = mb.findFirstString(locale, mn + ".tags");
-			if (s != null) 
-				op.getObjectList("tags", true).addAll(parseListOrCdl(s, vr, false, false, "Messages/tags on class {0} method {1}", c, m));
-			
 			if (op.containsKey("tags"))
 				for (String tag : op.getObjectList("tags").elements(String.class)) 
 					if (! tagMap.containsKey(tag))
 						tagMap.put(tag, new ObjectMap().append("name", tag));
 			
-			op.appendIf(true, true, true, "externalDocs", parseMap(mb.findFirstString(locale, mn + ".description"), vr, false, true, "Messages/externalDocs on class {0} method {1}", c, m));
-			
-			s = mb.findFirstString(locale, mn + ".parameters");
-			if (s != null) 
-				op.getObjectList("parameters", true).addAll(parseList(s, vr, false, false, "Messages/parameters on class {0} method {1}", c, m));
-
 			ObjectMap paramMap = new ObjectMap();
-
-			ObjectList ol = op.getObjectList("parameters");
-			if (ol != null) 
-				for (ObjectMap param : ol.elements(ObjectMap.class)) 
+			if (op.containsKey("parameters"))
+				for (ObjectMap param : op.getObjectList("parameters").elements(ObjectMap.class)) 
 					paramMap.put(param.getString("in") + '.' + param.getString("name"), param);
 		
 			// Finally, look for parameters defined on method.
@@ -374,18 +369,7 @@ public class BasicRestInfoProvider implements RestInfoProvider {
 				op.put("parameters", paramMap.values());
 
 			ObjectMap responses = op.getObjectMap("responses", true);
-			
-			s = mb.findFirstString(locale, mn + ".responses");
-			if (s != null) {
-				for (Map.Entry<String,Object> e : jp.parse(vr.resolve(s), ObjectMap.class).entrySet()) {
-					String httpCode = e.getKey();
-					if (responses.containsKey(httpCode))
-						responses.getObjectMap(httpCode).putAll((ObjectMap)e.getValue());
-					else
-						responses.put(httpCode, e.getValue());
-				}
-			}
-			
+						
 			// Gather responses from @Response-annotated exceptions.
 			for (RestMethodThrown rt : context.getRestMethodThrowns(m)) {
 				int code = rt.getCode();
diff --git a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java
index 496a5c0..10efb2f 100644
--- a/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java
+++ b/juneau-rest/juneau-rest-server/src/main/java/org/apache/juneau/rest/annotation/MethodSwagger.java
@@ -84,6 +84,10 @@ public @interface MethodSwagger {
 	
 	String[] description() default {};
 
+	String operationId() default "";
+	
+	String[] schemes() default {};
+	
 	/**
 	 * Optional deprecated flag for the exposed API.
 	 * 
@@ -106,6 +110,10 @@ public @interface MethodSwagger {
 	 * </ul>
 	 */
 	String deprecated() default "";
+	
+	String[] consumes() default {};
+	
+	String[] produces() default {};
 
 	/**
 	 * Optional external documentation information for the exposed API.
@@ -241,5 +249,5 @@ public @interface MethodSwagger {
 	 * 		Corresponds to the swagger field <code>/paths/{path}/{method}/tags</code>.
 	 * </ul>
 	 */
-	String[] tags() default "";
+	String[] tags() default {};
 }
diff --git a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
index 626ae73..7fd0152 100644
--- a/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
+++ b/juneau-rest/juneau-rest-server/src/test/java/org/apache/juneau/rest/BasicRestInfoProviderTest.java
@@ -14,10 +14,12 @@ package org.apache.juneau.rest;
 
 import static org.junit.Assert.assertEquals;
 import static org.apache.juneau.rest.TestUtils.*;
+import static org.apache.juneau.http.HttpMethodName.*;
 
 import java.io.*;
 import java.util.*;
 
+import org.apache.juneau.annotation.*;
 import org.apache.juneau.dto.swagger.*;
 import org.apache.juneau.rest.annotation.*;
 import org.apache.juneau.rest.util.*;
@@ -713,7 +715,266 @@ public class BasicRestInfoProviderTest {
 	}
 	
 	//-----------------------------------------------------------------------------------------------------------------
-	// /paths/<path>/<method>/tags
+	// /paths/<path>/<method>
 	//-----------------------------------------------------------------------------------------------------------------
+
+	@RestResource()
+	public static class M01 {
+		
+		@RestMethod(name=GET,path="/path/{foo}")
+		public Foo doFoo() {
+			return null;
+		}
+	}
 	
+	@Test
+	public void m01_operation_default() throws Exception {
+		assertObjectEquals("{operationId:'doFoo',responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}}}", getSwagger(new M01()).getPaths().get("/path/{foo}").get("get"));
+		assertObjectEquals("{operationId:'s-operationId',summary:'s-summary',description:'s-description',tags:['s-tag'],externalDocs:{description:'s-description',url:'s-url'},consumes:['s-consumes'],produces:['s-produces'],responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}},schemes:['s-scheme'],deprecated:true}", getSwaggerWithFile(new M01()).getPaths().get("/path/{foo}").get("get"));
+	}
+
+	@RestResource(swagger=@ResourceSwagger("{paths:{'/path/{foo}':{get:{summary:'a-summary',description:'a-description',operationId:'a-operationId',deprecated:true,consumes:['a-consumes'],produces:['a-produces'],tags:['a-tag'],schemes:['a-scheme'],externalDocs:{description: 'a-description',url: 'a-url'}}}}}"))
+	public static class M02 {		
+		@RestMethod(name=GET,path="/path/{foo}")
+		public Foo doFoo() {
+			return null;
+		}
+	}
+	
+	@Test
+	public void m02_operation_swaggerOnClass() throws Exception {
+		assertObjectEquals("{operationId:'a-operationId',summary:'a-summary',description:'a-description',tags:['a-tag'],externalDocs:{description:'a-description',url:'a-url'},consumes:['a-consumes'],produces:['a-produces'],responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}},schemes:['a-scheme'],deprecated:true}", getSwagger(new M02()).getPaths().get("/path/{foo}").get("get"));
+		assertObjectEquals("{operationId:'a-operationId',summary:'a-summary',description:'a-description',tags:['a-tag'],externalDocs:{description:'a-description',url:'a-url'},consumes:['a-consumes'],produces:['a-produces'],responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}},schemes:['a-scheme'],deprecated:true}", getSwaggerWithFile(new M02()).getPaths().get("/path/{foo}").get("get"));
+	}
+
+	@RestResource(swagger=@ResourceSwagger("{paths:{'/path/{foo}':{get:{summary:'a-summary',description:'a-description',operationId:'a-operationId',deprecated:true,consumes:['a-consumes'],produces:['a-produces'],tags:['a-tag'],schemes:['a-scheme'],externalDocs:{description:'a-description',url:'a-url'}}}}}"))
+	public static class M03 {		
+		@RestMethod(name=GET,path="/path/{foo}",swagger=@MethodSwagger("summary:'b-summary',description:'b-description',operationId:'b-operationId',deprecated:false,consumes:['b-consumes'],produces:['b-produces'],tags:['b-tag'],schemes:['b-scheme'],externalDocs:{description:'b-description',url:'b-url'}}"))
+		public Foo doFoo() {
+			return null;
+		}
+	}
+	
+	@Test
+	public void m03_operation_swaggerOnMethod() throws Exception {
+		assertObjectEquals("{operationId:'b-operationId',summary:'b-summary',description:'b-description',tags:['b-tag'],externalDocs:{description:'b-description',url:'b-url'},consumes:['b-consumes'],produces:['b-produces'],responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}},schemes:['b-scheme'],deprecated:false}", getSwagger(new M03()).getPaths().get("/path/{foo}").get("get"));
+		assertObjectEquals("{operationId:'b-operationId',summary:'b-summary',description:'b-description',tags:['b-tag'],externalDocs:{description:'b-description',url:'b-url'},consumes:['b-consumes'],produces:['b-produces'],responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}},schemes:['b-scheme'],deprecated:false}", getSwaggerWithFile(new M03()).getPaths().get("/path/{foo}").get("get"));
+	}
+
+	@RestResource(swagger=@ResourceSwagger("{paths:{'/path/{foo}':{get:{summary:'a-summary',description:'a-description',operationId:'a-operationId',deprecated:true,consumes:['a-consumes'],produces:['a-produces'],tags:['a-tag'],schemes:['a-scheme'],externalDocs:{description:'a-description',url:'a-url'}}}}}"))
+	public static class M04 {		
+		@RestMethod(name=GET,path="/path/{foo}",swagger=@MethodSwagger(summary="b-summary",description="b-description",operationId="b-operationId",deprecated="false",consumes="b-consumes",produces="b-produces",tags="b-tag",schemes="b-scheme",externalDocs="description:'b-description',url:'b-url'"))
+		public Foo doFoo() {
+			return null;
+		}
+	}
+
+	@Test
+	public void m04_operation_swaggerOnAnnotation() throws Exception {
+		assertObjectEquals("{operationId:'b-operationId',summary:'b-summary',description:'b-description',tags:['b-tag'],externalDocs:{description:'b-description',url:'b-url'},consumes:['b-consumes'],produces:['b-produces'],responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}},schemes:['b-scheme'],deprecated:false}", getSwagger(new M04()).getPaths().get("/path/{foo}").get("get"));
+		assertObjectEquals("{operationId:'b-operationId',summary:'b-summary',description:'b-description',tags:['b-tag'],externalDocs:{description:'b-description',url:'b-url'},consumes:['b-consumes'],produces:['b-produces'],responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}},schemes:['b-scheme'],deprecated:false}", getSwaggerWithFile(new M04()).getPaths().get("/path/{foo}").get("get"));
+	}
+
+	@RestResource(messages="BasicRestInfoProviderTest", swagger=@ResourceSwagger("{paths:{'/path/{foo}':{get:{summary:'a-summary',description:'a-description',operationId:'a-operationId',deprecated:true,consumes:['a-consumes'],produces:['a-produces'],tags:['a-tag'],schemes:['a-scheme'],externalDocs:{description:'a-description',url:'a-url'}}}}}"))
+	public static class M05 {		
+		@RestMethod(name=GET,path="/path/{foo}",swagger=@MethodSwagger(summary="$L{foo}",description="$L{foo}",operationId="$L{foo}",deprecated="$L{foo}",consumes="$L{foo}",produces="$L{foo}",tags="$L{foo}",schemes="$L{foo}",externalDocs="description:'$L{foo}',url:'$L{foo}'"))
+		public Foo doFoo() {
+			return null;
+		}
+	}
+
+	@Test
+	public void m05_operation_swaggerOnAnnotation_localized() throws Exception {
+		assertObjectEquals("{operationId:'l-foo',summary:'l-foo',description:'l-foo',tags:['l-foo'],externalDocs:{description:'l-foo',url:'l-foo'},consumes:['l-foo'],produces:['l-foo'],responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}},schemes:['l-foo'],deprecated:false}", getSwagger(new M05()).getPaths().get("/path/{foo}").get("get"));
+		assertObjectEquals("{operationId:'l-foo',summary:'l-foo',description:'l-foo',tags:['l-foo'],externalDocs:{description:'l-foo',url:'l-foo'},consumes:['l-foo'],produces:['l-foo'],responses:{'200':{description:'OK',schema:{type:'object',properties:{id:{format:'int32',type:'integer'}}}}},schemes:['l-foo'],deprecated:false}", getSwaggerWithFile(new M05()).getPaths().get("/path/{foo}").get("get"));
+	}
+	
+	//-----------------------------------------------------------------------------------------------------------------
+	// /paths/<path>/<method>/description
+	//-----------------------------------------------------------------------------------------------------------------
+	
+	//	paths: {
+//		'/path/{foo}': {
+//			get: {
+//				summary: 's-summary',
+//				description: 's-description',
+//				operationId: 's-operationId',
+//				deprecated: true,
+//				consumes: ['s-consumes'],
+//				produces: ['s-produces'],
+//				tags: ['s-tag'],
+//				schemes: ['s-scheme'],
+//				externalDocs: {
+//					description: 's-description',
+//					url: 's-url'
+//				},
+//				parameters: [
+//					{
+//						name: 's-query-name',
+//						in: 'query',
+//						description: 's-description',
+//						type: 'string',
+//						format: 's-format',
+//						pattern: 's-pattern',
+//						collectionFormat: 's-collectionFormat',
+//						minimum: 1.0,
+//						maximum: 2.0,
+//						multipleOf: 3.0,
+//						minLength: 1,
+//						maxLength: 2,
+//						minItems: 3,
+//						maxItems: 4,
+//						required: true,
+//						allowEmptyValue: true,
+//						exclusiveMinimum: true,
+//						exclusiveMaximum: true,
+//						uniqueItems: true,						
+//						schemaInfo: {
+//						}						
+//					},
+//					{
+//						name: 's-header-name',
+//						in: 'header',
+//						description: 's-description',
+//						type: 'string',
+//						format: 's-format',
+//						pattern: 's-pattern',
+//						collectionFormat: 's-collectionFormat',
+//						minimum: 1.0,
+//						maximum: 2.0,
+//						multipleOf: 3.0,
+//						minLength: 1,
+//						maxLength: 2,
+//						minItems: 3,
+//						maxItems: 4,
+//						required: true,
+//						allowEmptyValue: true,
+//						exclusiveMinimum: true,
+//						exclusiveMaximum: true,
+//						uniqueItems: true,						
+//						schemaInfo: {
+//							$ref: '#/definitions/Foo'
+//						}						
+//					},
+//					{
+//						name: 's-path-name',
+//						in: 'path',
+//						description: 's-description',
+//						type: 'string',
+//						format: 's-format',
+//						pattern: 's-pattern',
+//						collectionFormat: 's-collectionFormat',
+//						minimum: 1.0,
+//						maximum: 2.0,
+//						multipleOf: 3.0,
+//						minLength: 1,
+//						maxLength: 2,
+//						minItems: 3,
+//						maxItems: 4,
+//						required: true,
+//						allowEmptyValue: true,
+//						exclusiveMinimum: true,
+//						exclusiveMaximum: true,
+//						uniqueItems: true,						
+//						schemaInfo: {
+//							$ref: '#/definitions/Foo'
+//						}						
+//					},
+//					{
+//						name: 's-formData-name',
+//						in: 'formData',
+//						description: 's-description',
+//						type: 'string',
+//						format: 's-format',
+//						pattern: 's-pattern',
+//						collectionFormat: 's-collectionFormat',
+//						minimum: 1.0,
+//						maximum: 2.0,
+//						multipleOf: 3.0,
+//						minLength: 1,
+//						maxLength: 2,
+//						minItems: 3,
+//						maxItems: 4,
+//						required: true,
+//						allowEmptyValue: true,
+//						exclusiveMinimum: true,
+//						exclusiveMaximum: true,
+//						uniqueItems: true,						
+//						schemaInfo: {
+//							$ref: '#/definitions/Foo'
+//						}						
+//					},
+//					{
+//						name: 's-body-name',
+//						in: 'body',
+//						description: 's-description',
+//						type: 'string',
+//						format: 's-format',
+//						pattern: 's-pattern',
+//						collectionFormat: 's-collectionFormat',
+//						minimum: 1.0,
+//						maximum: 2.0,
+//						multipleOf: 3.0,
+//						minLength: 1,
+//						maxLength: 2,
+//						minItems: 3,
+//						maxItems: 4,
+//						required: true,
+//						allowEmptyValue: true,
+//						exclusiveMinimum: true,
+//						exclusiveMaximum: true,
+//						uniqueItems: true,						
+//						schemaInfo: {
+//							$ref: '#/definitions/Foo'
+//						}						
+//					}
+//				],
+//				responses: {
+//					100: {
+//						description:'s-100-description',
+//						schema: {
+//							type: array,
+//							items: {
+//								$ref: '#/definitions/Foo'
+//							}
+//						},
+//						headers: {
+//							X-Foo: {
+//								type: 'integer',
+//								format: 'int32',
+//								description: 's-description'
+//							}
+//						},
+//						examples: {
+//							foo: {bar:123},
+//							bar: 'baz'
+//						}
+//					}
+//				},
+//				security: [
+//					{foo_auth:['read:foo','write-foo']}
+//				]
+//			} 
+//		}
+//	},
+//	definitions: {
+//		Foo: {
+//			type: 'object',
+//			properties: {
+//				id: {
+//					type: 'integer',
+//					format: 'int64'
+//				}
+//			},
+//			xml: {
+//				name: 'Foo'
+//			}
+//		}
+//	}
+	
+	@Bean(typeName="Foo")
+	public static class Foo {
+		public int id;
+	}
+
 }
diff --git a/juneau-rest/juneau-rest-server/src/test/resources/org/apache/juneau/rest/BasicRestInfoProviderTest_swagger.json b/juneau-rest/juneau-rest-server/src/test/resources/org/apache/juneau/rest/BasicRestInfoProviderTest_swagger.json
index 3e935ac..5047b03 100644
--- a/juneau-rest/juneau-rest-server/src/test/resources/org/apache/juneau/rest/BasicRestInfoProviderTest_swagger.json
+++ b/juneau-rest/juneau-rest-server/src/test/resources/org/apache/juneau/rest/BasicRestInfoProviderTest_swagger.json
@@ -61,10 +61,14 @@
 				externalDocs: {
 					description: 's-description',
 					url: 's-url'
-				},
+				}
+			}
+		},
+		'/path/{foo}/query': {
+			get: {
 				parameters: [
 					{
-						name: 's-name',
+						name: 's-query-name',
 						in: 'query',
 						description: 's-description',
 						type: 'string',
@@ -84,15 +88,156 @@
 						exclusiveMaximum: true,
 						uniqueItems: true,						
 						schemaInfo: {
+							$ref: '#/definitions/Foo'
+						}						
+					}
+				]
+			}
+		},
+		'/path/{foo}/header': {
+			get: {
+				parameters: [
+					{
+						name: 's-header-name',
+						in: 'header',
+						description: 's-description',
+						type: 'string',
+						format: 's-format',
+						pattern: 's-pattern',
+						collectionFormat: 's-collectionFormat',
+						minimum: 1.0,
+						maximum: 2.0,
+						multipleOf: 3.0,
+						minLength: 1,
+						maxLength: 2,
+						minItems: 3,
+						maxItems: 4,
+						required: true,
+						allowEmptyValue: true,
+						exclusiveMinimum: true,
+						exclusiveMaximum: true,
+						uniqueItems: true,						
+						schemaInfo: {
+							$ref: '#/definitions/Foo'
 						}						
 					}
-				],
-				responses: {
-					100: {description:'s-100-description'}
-				},
-				security: [
-					{foo_auth:['read:foo','write-foo']}
 				]
+			}
+		},
+		'/path/{foo}/path': {
+			get: {
+				parameters: [
+					{
+						name: 's-path-name',
+						in: 'path',
+						description: 's-description',
+						type: 'string',
+						format: 's-format',
+						pattern: 's-pattern',
+						collectionFormat: 's-collectionFormat',
+						minimum: 1.0,
+						maximum: 2.0,
+						multipleOf: 3.0,
+						minLength: 1,
+						maxLength: 2,
+						minItems: 3,
+						maxItems: 4,
+						required: true,
+						allowEmptyValue: true,
+						exclusiveMinimum: true,
+						exclusiveMaximum: true,
+						uniqueItems: true,						
+						schemaInfo: {
+							$ref: '#/definitions/Foo'
+						}						
+					}
+				]
+			}
+		},
+		'/path/{foo}/formData': {
+			get: {
+				parameters: [
+					{
+						name: 's-formData-name',
+						in: 'formData',
+						description: 's-description',
+						type: 'string',
+						format: 's-format',
+						pattern: 's-pattern',
+						collectionFormat: 's-collectionFormat',
+						minimum: 1.0,
+						maximum: 2.0,
+						multipleOf: 3.0,
+						minLength: 1,
+						maxLength: 2,
+						minItems: 3,
+						maxItems: 4,
+						required: true,
+						allowEmptyValue: true,
+						exclusiveMinimum: true,
+						exclusiveMaximum: true,
+						uniqueItems: true,						
+						schemaInfo: {
+							$ref: '#/definitions/Foo'
+						}						
+					}
+				]
+			}
+		},
+		'/path/{foo}/body': {
+			get: {
+				parameters: [
+					{
+						name: 's-body-name',
+						in: 'body',
+						description: 's-description',
+						type: 'string',
+						format: 's-format',
+						pattern: 's-pattern',
+						collectionFormat: 's-collectionFormat',
+						minimum: 1.0,
+						maximum: 2.0,
+						multipleOf: 3.0,
+						minLength: 1,
+						maxLength: 2,
+						minItems: 3,
+						maxItems: 4,
+						required: true,
+						allowEmptyValue: true,
+						exclusiveMinimum: true,
+						exclusiveMaximum: true,
+						uniqueItems: true,						
+						schemaInfo: {
+							$ref: '#/definitions/Foo'
+						}						
+					}
+				]
+			}
+		},
+		'/path/{foo}/responses/100': {
+			get: {
+				responses: {
+					100: {
+						description:'s-100-description',
+						schema: {
+							type: 'array',
+							items: {
+								$ref: '#/definitions/Foo'
+							}
+						},
+						headers: {
+							X-Foo: {
+								type: 'integer',
+								format: 'int32',
+								description: 's-description'
+							}
+						},
+						examples: {
+							foo: {bar:123},
+							bar: 'baz'
+						}
+					}
+				}
 			} 
 		}
 	},

-- 
To stop receiving notification emails like this one, please contact
jamesbognar@apache.org.