Skip to content

Commit

Permalink
Merge pull request Azure#218 from daschult/InvalidSwaggerMethod
Browse files Browse the repository at this point in the history
Add MissingRequiredAnnotationException
  • Loading branch information
Dan Schulte authored Sep 13, 2017
2 parents 280735a + fd07146 commit 32f5005
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for
* license information.
*/

package com.microsoft.rest.v2;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.List;

/**
* An exception that is thrown when a Swagger interface is parsed and it is missing required
* annotations.
*/
public class MissingRequiredAnnotationException extends RuntimeException {
/**
* Create a new MissingRequiredAnnotationException for the provided missing required annotation
* on the provided swaggerInterface.
* @param requiredAnnotation The annotation that is required.
* @param swaggerInterface The swagger interface that is missing the required annotation.
*/
public MissingRequiredAnnotationException(Class<? extends Annotation> requiredAnnotation, Class<?> swaggerInterface) {
super("A " + getAnnotationName(requiredAnnotation) + " annotation must be defined on " + swaggerInterface.getName() + ".");
}

/**
* Create a new MissingRequiredAnnotationException for the provided missing required annotation
* on the provided swaggerInterface method.
* @param requiredAnnotation The annotation that is required.
* @param swaggerInterfaceMethod The swagger interface method that is missing the required annotation.
*/
public MissingRequiredAnnotationException(Class<? extends Annotation> requiredAnnotation, Method swaggerInterfaceMethod) {
super("A " + getAnnotationName(requiredAnnotation) + " annotation must be defined on the method " + methodFullName(swaggerInterfaceMethod) + ".");
}

/**
* Create a new MissingRequiredAnnotationException for the provided missing required annotation
* options on the provided swaggerInterface method.
* @param requiredAnnotationOptions The options for the annotation that is required.
* @param swaggerInterfaceMethod The swagger interface method that is missing the required annotation.
*/
public MissingRequiredAnnotationException(List<Class<? extends Annotation>> requiredAnnotationOptions, Method swaggerInterfaceMethod) {
super("Either " + optionsToString(requiredAnnotationOptions) + " annotation must be defined on the method " + methodFullName(swaggerInterfaceMethod) + ".");
}

private static String getAnnotationName(Class<? extends Annotation> annotation) {
return annotation.getSimpleName();
}

private static String optionsToString(List<Class<? extends Annotation>> requiredAnnotationOptions) {
final StringBuilder result = new StringBuilder();

final int optionCount = requiredAnnotationOptions.size();
for (int i = 0; i < optionCount; ++i) {
if (1 <= i) {
result.append(", ");
}
if (i == optionCount - 1) {
result.append("or ");
}
result.append(getAnnotationName(requiredAnnotationOptions.get(i)));
}

return result.toString();
}

private static String methodFullName(Method swaggerInterfaceMethod) {
return swaggerInterfaceMethod.getDeclaringClass().getName() + "." + swaggerInterfaceMethod.getName() + "()";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,24 @@
*/
class SwaggerInterfaceParser {
private final Class<?> swaggerInterface;
private final String host;
private final Map<Method, SwaggerMethodParser> methodParsers = new HashMap<>();

private String host;

/**
* Create a new SwaggerInterfaceParser object with the provided fully qualified interface
* name.
* @param swaggerInterface The interface that will be parsed.
*/
SwaggerInterfaceParser(Class<?> swaggerInterface) {
this.swaggerInterface = swaggerInterface;

final Host hostAnnotation = swaggerInterface.getAnnotation(Host.class);
if (hostAnnotation == null) {
throw new MissingRequiredAnnotationException(Host.class, swaggerInterface);
}
else {
host = hostAnnotation.value();
}
}

/**
Expand All @@ -47,18 +54,11 @@ public SwaggerMethodParser methodParser(Method swaggerMethod) {
}

/**
* Parse the desired host that the provided Swagger interface will target with its REST API
* calls. This value is retrieved from the @Host annotation placed on the Swagger interface. If
* no @Host annotation exists on the Swagger interface, then null will be returned.
* @return The value of the @Host annotation, or null if no @Host annotation exists.
* Get the desired host that the provided Swagger interface will target with its REST API
* calls. This value is retrieved from the @Host annotation placed on the Swagger interface.
* @return The value of the @Host annotation.
*/
protected String host() {
if (host == null) {
final Host hostAnnotation = swaggerInterface.getAnnotation(Host.class);
if (hostAnnotation != null) {
host = hostAnnotation.value();
}
}
String host() {
return host;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ else if (swaggerMethod.isAnnotationPresent(POST.class)) {
else if (swaggerMethod.isAnnotationPresent(PATCH.class)) {
setHttpMethodAndRelativePath("PATCH", swaggerMethod.getAnnotation(PATCH.class).value());
}
else {
final ArrayList<Class<? extends Annotation>> requiredAnnotationOptions = new ArrayList<>();
requiredAnnotationOptions.add(GET.class);
requiredAnnotationOptions.add(PUT.class);
requiredAnnotationOptions.add(HEAD.class);
requiredAnnotationOptions.add(DELETE.class);
requiredAnnotationOptions.add(POST.class);
requiredAnnotationOptions.add(PATCH.class);
throw new MissingRequiredAnnotationException(requiredAnnotationOptions, swaggerMethod);
}

returnType = swaggerMethod.getGenericReturnType();

Expand All @@ -104,14 +114,21 @@ else if (swaggerMethod.isAnnotationPresent(PATCH.class)) {
}
}

expectedStatusCodes = swaggerMethod.getAnnotation(ExpectedResponses.class).value();

if (swaggerMethod.isAnnotationPresent(UnexpectedResponseExceptionType.class)) {
exceptionType = swaggerMethod.getAnnotation(UnexpectedResponseExceptionType.class).value();
final ExpectedResponses expectedResponses = swaggerMethod.getAnnotation(ExpectedResponses.class);
if (expectedResponses == null) {
throw new MissingRequiredAnnotationException(ExpectedResponses.class, swaggerMethod);
}
else {
expectedStatusCodes = expectedResponses.value();
}

final UnexpectedResponseExceptionType unexpectedResponseExceptionType = swaggerMethod.getAnnotation(UnexpectedResponseExceptionType.class);
if (unexpectedResponseExceptionType == null) {
exceptionType = RestException.class;
}
else {
exceptionType = unexpectedResponseExceptionType.value();
}

try {
final Method exceptionBodyMethod = exceptionType.getDeclaredMethod("body");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.microsoft.rest.v2;

import com.microsoft.rest.v2.annotations.ExpectedResponses;
import com.microsoft.rest.v2.annotations.GET;
import com.microsoft.rest.v2.annotations.Host;
import org.junit.Test;

Expand All @@ -18,10 +19,9 @@ interface TestInterface1 {
interface TestInterface2 {
}

@Test
@Test(expected = MissingRequiredAnnotationException.class)
public void hostWithNoHostAnnotation() {
final SwaggerInterfaceParser interfaceParser = new SwaggerInterfaceParser(TestInterface1.class);
assertEquals(null, interfaceParser.host());
new SwaggerInterfaceParser(TestInterface1.class);
}

@Test
Expand All @@ -30,7 +30,9 @@ public void hostWithHostAnnotation() {
assertEquals("https://management.azure.com", interfaceParser.host());
}

@Host("https://azure.com")
interface TestInterface3 {
@GET("my/url/path")
@ExpectedResponses({200})
void testMethod3();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.microsoft.rest.RestException;
import com.microsoft.rest.v2.annotations.ExpectedResponses;
import com.microsoft.rest.v2.annotations.PATCH;
import com.microsoft.rest.v2.annotations.UnexpectedResponseExceptionType;
import org.junit.Test;

Expand All @@ -15,7 +16,7 @@ interface TestInterface1 {
void testMethod1();
}

@Test(expected = NullPointerException.class)
@Test(expected = MissingRequiredAnnotationException.class)
public void withNoAnnotations() {
final Method testMethod1 = TestInterface1.class.getDeclaredMethods()[0];
assertEquals("testMethod1", testMethod1.getName());
Expand All @@ -24,6 +25,7 @@ public void withNoAnnotations() {
}

interface TestInterface2 {
@PATCH("my/rest/api/path")
@ExpectedResponses({200})
void testMethod2();
}
Expand All @@ -35,7 +37,7 @@ public void withOnlyExpectedResponse() {

final SwaggerMethodParser methodParser = new SwaggerMethodParser(testMethod2, "https://raw.host.com");
assertEquals("com.microsoft.rest.v2.SwaggerMethodParserTests.TestInterface2.testMethod2", methodParser.fullyQualifiedMethodName());
assertEquals(null, methodParser.httpMethod());
assertEquals("PATCH", methodParser.httpMethod());
assertArrayEquals(new int[] { 200 }, methodParser.expectedStatusCodes());
assertEquals(RestException.class, methodParser.exceptionType());
assertEquals(Object.class, methodParser.exceptionBodyType());
Expand All @@ -45,6 +47,7 @@ public void withOnlyExpectedResponse() {
}

interface TestInterface3 {
@PATCH("my/rest/api/path")
@ExpectedResponses({200})
@UnexpectedResponseExceptionType(MyRestException.class)
void testMethod3();
Expand All @@ -57,7 +60,7 @@ public void withExpectedResponseAndUnexpectedResponseExceptionType() {

final SwaggerMethodParser methodParser = new SwaggerMethodParser(testMethod3, "https://raw.host.com");
assertEquals("com.microsoft.rest.v2.SwaggerMethodParserTests.TestInterface3.testMethod3", methodParser.fullyQualifiedMethodName());
assertEquals(null, methodParser.httpMethod());
assertEquals("PATCH", methodParser.httpMethod());
assertArrayEquals(new int[] { 200 }, methodParser.expectedStatusCodes());
assertEquals(MyRestException.class, methodParser.exceptionType());
assertEquals(HttpBinJSON.class, methodParser.exceptionBodyType());
Expand Down

0 comments on commit 32f5005

Please sign in to comment.