AnnotationApiConfigGenerator implements ApiConfigGenerator { @Override public Map<String, String> generateConfig(Class<?>... serviceClasses) throws ApiConfigException { return generateConfig(ServiceContext.create(), serviceClasses); } AnnotationApiConfigGenerator();  AnnotationApiConfigGenerator(ApiConfigWriter apiConfigWriter, ClassLoader classLoader,
      ApiConfig.Factory configFactory); @Override Map<String, String> generateConfig(Class<?>... serviceClasses); @Override Map<String, String> generateConfig(
      ServiceContext serviceContext, Class<?>... serviceClasses);  }
@Test public void testSimpleOverrideEndpoint() throws Exception { String apiConfigSource = g.generateConfig(SimpleOverrideEndpoint.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); assertEquals(1, methods.size()); }
@Test public void testSimpleOverloadEndpoint() throws Exception { try { g.generateConfig(SimpleOverloadEndpoint.class); fail("Config generation for endpoint with overloaded inherited method should have failed"); } catch (OverloadedMethodException expected) { } }
@Test public void testSimpleCovarianceEndpoint() throws Exception { try { g.generateConfig(SimpleCovarianceEndpoint.class); fail("Config generation for endpoint with covariant inherited method should have failed"); } catch (OverloadedMethodException expected) { } }
@Test public void testSimpleContravarianceEndpoint() throws Exception { try { g.generateConfig(SimpleContravarianceEndpoint.class); fail("Config generation for endpoint with contravariant inherited method should have failed"); } catch (OverloadedMethodException expected) { } }
@Test public void testCollectionCovarianceEndpoint() throws Exception { try { g.generateConfig(CollectionCovarianceEndpoint.class); fail("Config generation for endpoint with covariant inherited method should have failed"); } catch (OverloadedMethodException expected) { } }
@Test public void testCollectionContravarianceEndpoint() throws Exception { try { g.generateConfig(CollectionContravarianceEndpoint.class); fail("Config generation for endpoint with contravariant inherited method should have failed"); } catch (OverloadedMethodException expected) { } }
@Test public void testFullySpecializedDateEndpoint() throws Exception { String apiConfigSource = g.generateConfig( RestfulResourceEndpointBase.FullySpecializedEndpoint.class).get("fullApi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); assertEquals(6, methods.size()); }
@Test public void testGenericBasePartiallySpecializedEndpoint() throws Exception { String apiConfigSource = g.generateConfig( RestfulResourceEndpointBase.PartiallySpecializedEndpoint.class).get("partialApi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); assertEquals(6, methods.size()); JsonNode rootPartial = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methodsPartial = rootPartial.path("descriptor").path("methods"); JsonNode rootFull = objectMapper.readValue( g.generateConfig(RestfulResourceEndpointBase.FullySpecializedEndpoint.class) .get("fullApi-v1.api"), JsonNode.class); JsonNode methodsFull = rootFull.path("descriptor").path("methods"); String partialPrefix = RestfulResourceEndpointBase.PartiallySpecializedEndpoint.class.getName(); String fullPrefix = RestfulResourceEndpointBase.FullySpecializedEndpoint.class.getName(); JsonNode methodListPartial = methodsFull.path(partialPrefix + ".list"); JsonNode methodListFull = methodsFull.path(fullPrefix + ".list"); assertNotNull(methodListPartial); assertEquals( methodListPartial.path("request").asText(), methodListFull.path("request").asText()); JsonNode methodMiscPartial = methodsFull.path("GenericRestBasePartiallySpecialized.misc"); JsonNode methodMiscFull = methodsFull.path("GenericRestBaseFullySpecialized.misc"); assertEquals( methodMiscPartial.path("request").asText(), methodMiscFull.path("request").asText()); }
@Test public void testDeepGenericHierarchySuccessEndpoint() throws Exception { String apiConfigSource = g.generateConfig(DeepGenericHierarchySuccessEndpoint.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); assertEquals(1, methods.size()); }
@Test public void testBoundedGenericEndpoint() throws Exception { String apiConfigSource = g.generateConfig(BoundedGenericEndpoint.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); assertEquals(1, methods.size()); }
@Test public void testEndpointWithOnlyDefaultConfiguration() throws Exception { String apiConfigSource = g.generateConfig(Endpoint0.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifyApi(root, "thirdParty.api", "https: "myapi", "v1", "", "https: JsonNode methodGetFoo = root.path("methods").path("myapi.endpoint0.getFoo"); verifyMethod(methodGetFoo, "foo/{id}", HttpMethod.GET, Endpoint0.class.getName() + ".getFoo", "autoTemplate(backendResponse)"); verifyMethodRequest(methodGetFoo.path("request"), "empty", 1); verifyMethodRequestParameter(methodGetFoo.path("request"), "id", "string", true, false); verifyEndpoint0Schema(root, Endpoint0.class.getName()); }
@Test public void testWildcardParameterTypes() throws Exception { @Api final class WildcardEndpoint { @SuppressWarnings("unused") public void foo(Map<String, ? extends Integer> map) {} } try { g.generateConfig(WildcardEndpoint.class); fail("Config generation for service class with wildcard parameter type should have failed"); } catch (IllegalArgumentException e) { } }
@Test public void testDeepGenericHierarchyFailEndpoint() throws Exception { try { g.generateConfig(DeepGenericHierarchyFailEndpoint.class); fail("Config generation for endpoint with contravariant inherited method should have failed"); } catch (OverloadedMethodException expected) { } }
@Test public void testServiceWithMergedInheritance() throws Exception { String apiConfigSource = g.generateConfig(SubclassedEndpoint.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifyEndpoint1(SubclassedEndpoint.class, root); verifyEndpoint1Schema(SubclassedEndpoint.class, root); }
@Test public void testServiceWithOverridingInheritance() throws Exception { String apiConfigSource = g.generateConfig(SubclassedOverridingEndpoint.class).get("myapi-v2.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifySubclassedOverridingEndpoint(SubclassedOverridingEndpoint.class, root); verifySubclassedOverridingEndpointSchema(SubclassedOverridingEndpoint.class, root); }
@Test public void testServiceWithApiNameOverride() throws Exception { String apiConfigSource = g.generateConfig( ServiceContext.create("abc", "xyz"), Endpoint4.class).get("api4-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); assertEquals("api4", root.path("name").asText()); JsonNode methods = root.path("methods"); verifyEndpoint0Schema(root, Endpoint4.class.getName()); }
@Test public void testDefaultEndpointOnGooglePlex() throws Exception { String apiConfigSource = g.generateConfig( ServiceContext.create("google.com:myapp", "myapi"), Endpoint0.class) .get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); assertEquals("myapi", root.path("name").asText()); assertEquals("https: assertEquals("https: }
@Test public void testEndpointWithNestedBeans() throws Exception { String apiConfigSource = g.generateConfig(Endpoint5.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifyEndpoint5Schema(root); }
@Test public void testPrimitiveSchemas() throws Exception { String apiConfigSource = g.generateConfig(PrimitiveEndpoint.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifyPrimitiveSchemas(root); }
@Test public void testEndpointNoApiAnnotation() throws Exception { try { g.generateConfig(Object.class); fail("Config generation for service class with no @Api annotation should have failed"); } catch (ApiConfigException e) { } }
@Test public void testEndpointWithUnnamedParameterTypes() throws Exception { try { g.generateConfig(TestUnnamedParameterType.class); fail("Config generation for service class with unnamed parameter type should have failed"); } catch (MissingParameterNameException e) { } }
@Test public void testMultipleServiceClasses() throws Exception { String apiConfigSource = g.generateConfig(MultipleEndpoint1.class, MultipleEndpoint2.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); assertEquals("item", root.path("methods").path("myapi.add").path("path").asText()); assertEquals("item/{id}", root.path("methods").path("myapi.delete").path("path").asText()); }
@Test public void testSimpleLevelOverriding() throws Exception { String apiConfigSource = g.generateConfig(SimpleLevelOverridingApi.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); JsonNode noOverrides = methods.path("myapi.resource1.noOverrides"); assertFalse(noOverrides.isMissingNode()); verifyStrings(objectMapper.convertValue(noOverrides.path("scopes"), String[].class), new String[] {"s0a", "s1a"}); verifyStrings(objectMapper.convertValue(noOverrides.path("audiences"), String[].class), new String[] {"a0a", "a1a"}); verifyStrings(objectMapper.convertValue(noOverrides.path("clientIds"), String[].class), new String[] {"c0a", "c1a"}); assertEquals("resource1", objectMapper.convertValue(noOverrides.path("path"), String.class)); assertEquals(AuthLevel.REQUIRED, objectMapper.convertValue(noOverrides.path("authLevel"), AuthLevel.class)); JsonNode overrides = methods.path("myapi.resource1.overrides"); assertFalse(overrides.isMissingNode()); verifyStrings(objectMapper.convertValue(overrides.path("scopes"), String[].class), new String[] {"s0b", "s1b"}); verifyStrings(objectMapper.convertValue(overrides.path("audiences"), String[].class), new String[] {"a0b", "a1b"}); verifyStrings(objectMapper.convertValue(overrides.path("clientIds"), String[].class), new String[] {"c0b", "c1b"}); assertEquals("overridden", objectMapper.convertValue(overrides.path("path"), String.class)); assertEquals(AuthLevel.OPTIONAL, objectMapper.convertValue(overrides.path("authLevel"), AuthLevel.class)); }
@Test public void testRecursiveSchemas() throws Exception { String apiConfigSource = g.generateConfig(RecursiveEndpoint.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifyRecursiveSchemas(root); }
@Test public void testParentChildSchemas() throws Exception { String apiConfigSource = g.generateConfig(ParentChildEndpoint.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifyParentChildSchemas(root); }
@Test public void testEndpointWithMultidimensionalArrays() throws Exception { String apiConfigSource = g.generateConfig(ArrayEndpoint.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifyArrayEndpointSchema(root); }
@Test public void testValidDateInParameter() throws Exception { @Api class DateParameter { @SuppressWarnings("unused") public void foo(@Named("date") Date date) { } } String apiConfigSource = g.generateConfig(DateParameter.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode request = root.path("methods").path("myapi.dateParameter.foo").path("request"); verifyMethodRequestParameter(request, "date", "datetime", true, false); assertTrue(root.path("descriptor").path("schemas").path("Outcome").isMissingNode()); verifyEmptyMethodRequest(root, DateParameter.class.getName() + ".pick"); }
@Test public void testDateCollection() throws Exception { @Api class DateParameters { @SuppressWarnings("unused") public void foo(@Named("date") Date date, @Named("dates1") Collection<Date> dates1, @Named("dates2") Date[] dates2) { } } String apiConfigSource = g.generateConfig(DateParameters.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode request = root.path("methods").path("myapi.dateParameters.foo").path("request"); verifyMethodRequestParameter(request, "date", "datetime", true, false); verifyMethodRequestParameter(request, "dates1", "datetime", true, true); verifyMethodRequestParameter(request, "dates2", "datetime", true, true); verifyEmptyMethodRequest(root, DateParameters.class.getName() + ".foo"); }
@Test public void testInvalidDateInEndpointRequest() throws Exception { @Api class DateParameter { @SuppressWarnings("unused") public void foo(Date date) {} } try { g.generateConfig(DateParameter.class).get("myapi-v1.api"); fail("Dates should not be treated as resources"); } catch (MissingParameterNameException expected) { } }
@Test public void testValidEnumInParameter() throws Exception { @Api class EnumParameter { @SuppressWarnings("unused") public void pick(@Named("outcome") Outcome outcome) { } } String apiConfigSource = g.generateConfig(EnumParameter.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode request = root.path("methods").path("myapi.enumParameter.pick").path("request"); verifyMethodRequestParameter(request, "outcome", "string", true, false, "WON", "LOST", "TIE"); assertTrue(root.path("descriptor").path("schemas").path("Outcome").isMissingNode()); verifyEmptyMethodRequest(root, EnumParameter.class.getName() + ".pick"); }
@Test public void testValidEnumInParameterAndResource() throws Exception { @Api class EnumParameter { @SuppressWarnings("unused") public void pick(@Named("outcomeParam") Outcome outcome, EnumBean date) { } } String apiConfigSource = g.generateConfig(EnumParameter.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode outcomeSchema = root.path("descriptor").path("schemas").path("Outcome"); assertEquals("Outcome", outcomeSchema.path("id").asText()); assertEquals("string", outcomeSchema.path("type").asText()); JsonNode enumConfig = outcomeSchema.path("enum"); assertTrue(enumConfig.isArray()); assertEquals(3, enumConfig.size()); assertEquals(Outcome.WON.toString(), enumConfig.get(0).asText()); assertEquals(Outcome.LOST.toString(), enumConfig.get(1).asText()); assertEquals(Outcome.TIE.toString(), enumConfig.get(2).asText()); JsonNode enumSchema = root.path("descriptor").path("schemas").path("EnumBean"); assertEquals("EnumBean", enumSchema.path("id").asText()); assertEquals("object", enumSchema.path("type").asText()); assertNotNull(enumSchema.get("properties")); assertNotNull(enumSchema.path("properties").path("outcome").get("$ref")); assertEquals("Outcome", enumSchema.path("properties").path("outcome").path("$ref").asText()); JsonNode methodSchema = root.path("descriptor").path("methods").path( EnumParameter.class.getName() + ".pick"); assertNotNull(methodSchema.get("request")); assertNotNull(methodSchema.path("request").get("$ref")); assertEquals("EnumBean", methodSchema.path("request").get("$ref").asText()); }
@Test public void testSerializedPropertyInResourceSchema() throws Exception { class BazToDateSerializer extends DefaultValueSerializer<Baz, Date> { } @Api(transformers = {BazToDateSerializer.class, BarResourceSerializer.class}) class BarEndpoint { @SuppressWarnings("unused") public Bar getBar() { return null; } } String apiConfigSource = g.generateConfig(BarEndpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode bar = root.path("descriptor").path("schemas").path("Bar"); verifyObjectPropertySchema(bar, "someBaz", "string", "date-time"); }
@Test public void testFullyConfiguredEndpoint() throws Exception { String apiConfigSource = g.generateConfig(Endpoint1.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifyEndpoint1(Endpoint1.class, root); verifyEndpoint1Schema(Endpoint1.class, root); }
@Test public void testChainedSerializer() throws Exception { class BarToBazSerializer extends DefaultValueSerializer<Bar, Baz> { } class BazToDateSerializer extends DefaultValueSerializer<Baz, Date> { } class Qux { @SuppressWarnings("unused") public Bar getSomeBar() { return null; } } @Api(transformers = {BazToDateSerializer.class, BarToBazSerializer.class}) class QuxEndpoint { @SuppressWarnings("unused") public Qux getQux() { return null; } } String apiConfigSource = g.generateConfig(QuxEndpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode qux = root.path("descriptor").path("schemas").path("Qux"); verifyObjectPropertySchema(qux, "someBar", "string", "date-time"); }
@Test public void testSerializedEnum() throws Exception { class OutcomeToIntegerSerializer extends DefaultValueSerializer<Outcome, Integer> { } @Api(transformers = {OutcomeToIntegerSerializer.class}) class EnumParameter { @SuppressWarnings("unused") public void foo(@Named("outcome") Outcome outcome) { } } String apiConfigSource = g.generateConfig(EnumParameter.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode request = root.path("methods").path("myapi.enumParameter.foo").path("request"); verifyMethodRequestParameter(request, "outcome", "int32", true, false); }
@Test public void testNonSerializedEnumShouldAlwaysBeString() throws Exception { class StringToIntegerSerializer extends DefaultValueSerializer<String, Integer> { } @Api(transformers = {StringToIntegerSerializer.class}) class EnumParameter { @SuppressWarnings("unused") public void foo(@Named("outcome") Outcome outcome) { } } String apiConfigSource = g.generateConfig(EnumParameter.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode request = root.path("methods").path("myapi.enumParameter.foo").path("request"); verifyMethodRequestParameter(request, "outcome", "string", true, false, "WON", "LOST", "TIE"); assertTrue(root.path("descriptor").path("schemas").path("Outcome").isMissingNode()); verifyEmptyMethodRequest(root, EnumParameter.class.getName() + ".pick"); }
@Test public void testEnumCollection() throws Exception { @Api class EnumParameters { @SuppressWarnings("unused") public void foo(@Named("outcome") Outcome outcome, @Named("outcomes1") Collection<Outcome> outcomes1, @Named("outcomes2") Outcome[] outcomes2) { } } String apiConfigSource = g.generateConfig(EnumParameters.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode request = root.path("methods").path("myapi.enumParameters.foo").path("request"); verifyMethodRequestParameter(request, "outcome", "string", true, false, "WON", "LOST", "TIE"); verifyMethodRequestParameter(request, "outcomes1", "string", true, true, "WON", "LOST", "TIE"); verifyMethodRequestParameter(request, "outcomes2", "string", true, true, "WON", "LOST", "TIE"); assertTrue(root.path("descriptor").path("schemas").path("Outcome").isMissingNode()); verifyEmptyMethodRequest(root, EnumParameters.class.getName() + ".foo"); }
@Test public void testInvalidEnumInEndpointRequest() throws Exception { @Api class EnumParameter { @SuppressWarnings("unused") public void pick(Outcome outcome) {} } try { g.generateConfig(EnumParameter.class).get("myapi-v1.api"); fail("Enums should not be treated as resources"); } catch (MissingParameterNameException expected) { } }
@Test public void testBareRemoveAndDelete() throws Exception { String apiConfigSource = g.generateConfig(Endpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); assertEquals( "remove/{id}", root.path("methods").path("myapi.endpoint.remove").path("path").asText()); assertEquals( "delete/{id}", root.path("methods").path("myapi.endpoint.delete").path("path").asText()); }
@Test public void testOptionalParameters() throws Exception { String apiConfigSource = g.generateConfig(TestEndpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); assertEquals("foo/{required}", root.path("methods").path("myapi.testEndpoint.getItem").path("path").asText()); }
@Test public void testCollectionResponse() throws Exception { String apiConfigSource = g.generateConfig(TestEndpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); assertEquals("bar", root.path("methods").path("myapi.testEndpoint.list").path("path").asText()); assertNotNull(root.path("descriptor").path("schemas").path("Bar")); assertNotNull(root.path("descriptor").path("schemas").path("BarCollection")); assertEquals("array", root.path("descriptor").path("schemas").path("BarCollection") .path("properties").path("items").path("type").asText()); assertEquals("Bar", root.path("descriptor").path("schemas").path("BarCollection") .path("properties").path("items").path("items").path("$ref").asText()); String backendName = TestEndpoint.class.getName(); assertEquals("baz", root.path("methods").path("myapi.testEndpoint.listWithPagination") .path("path").asText()); assertEquals("CollectionResponse_Baz", root.path("descriptor").path("methods") .path(backendName + ".listWithPagination").path("response").path("$ref").asText()); assertNotNull(root.path("descriptor").path("schemas").path("Baz")); assertNotNull(root.path("descriptor").path("schemas").path("CollectionResponse_Baz")); assertEquals("array", root.path("descriptor").path("schemas").path("CollectionResponse_Baz") .path("properties").path("items").path("type").asText()); assertEquals("Baz", root.path("descriptor").path("schemas").path("CollectionResponse_Baz") .path("properties").path("items").path("items").path("$ref").asText()); assertEquals("string", root.path("descriptor").path("schemas").path("CollectionResponse_Baz") .path("properties").path("nextPageToken").path("type").asText()); assertEquals("CollectionResponse_Baz", root.path("descriptor").path("methods") .path(backendName + ".listWithPagination").path("response").path("$ref").asText()); assertEquals("foo", root.path("methods").path("myapi.testEndpoint.listWithMyCollectionResponse") .path("path").asText()); assertEquals("MyCollectionResponse_Foo", root.path("descriptor").path("methods") .path(backendName + ".listWithMyCollectionResponse").path("response") .path("$ref").asText()); assertNotNull(root.path("descriptor").path("schemas").path("Foo")); assertNotNull(root.path("descriptor").path("schemas").path("MyCollectionResponse_Foo")); assertEquals("array", root.path("descriptor").path("schemas").path("MyCollectionResponse_Foo") .path("properties").path("items").path("type").asText()); assertEquals("Foo", root.path("descriptor").path("schemas").path("MyCollectionResponse_Foo") .path("properties").path("items").path("items").path("$ref").asText()); assertEquals("string", root.path("descriptor").path("schemas").path("MyCollectionResponse_Foo") .path("properties").path("nextPageToken").path("type").asText()); assertEquals("MyCollectionResponse_Foo", root.path("descriptor").path("methods") .path(backendName + ".listWithMyCollectionResponse").path("response") .path("$ref").asText()); }
@Test public void testAbstract() throws Exception { @Api(isAbstract = AnnotationBoolean.TRUE) class Test {} String apiConfigSource = g.generateConfig(Test.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); assertTrue(root.get("abstract").asBoolean()); }
@Test public void testRepeatedParameterName() throws Exception { @Api class Test { @ApiMethod(path = "path") public void foo(@Named("id") String id1, @Named("id") Long id2) {} } try { g.generateConfig(Test.class).get("myapi-v1.api"); fail("Config generation for endpoint with two parameters named the same should have failed."); } catch (DuplicateParameterNameException expected) { } }
@Test public void testEmptyRequestBodies() throws Exception { String apiConfigSource = g.generateConfig(Endpoint1.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); JsonNode methodGetFoo = methods.path("myapi.foos.get"); assertTrue(methodGetFoo.path("request").path("body").asText().equals("empty")); assertFalse(root.path("descriptor").path("methods") .path(methodGetFoo.path("rosyMethod").asText()).has("request")); JsonNode methodInsertFoo = methods.path("myapi.foos.insert"); assertFalse(methodInsertFoo.path("request").path("body").asText().equals("empty")); assertTrue(root.path("descriptor").path("methods") .path(methodInsertFoo.path("rosyMethod").asText()).has("request")); JsonNode methodExecute0 = methods.path("myapi.foos.execute0"); assertTrue(methodExecute0.path("request").path("body").asText().equals("empty")); assertFalse(root.path("descriptor").path("methods") .path(methodExecute0.path("rosyMethod").asText()).has("request")); }
@Test public void testNullablePathParameter() throws Exception { @Api class Test { @ApiMethod(path = "path/{id}") public void foo(@Named("id") @Nullable String id) {} } try { g.generateConfig(Test.class); fail("Config generation for endpoint with nullable path parameter should have failed."); } catch (InvalidParameterAnnotationsException expected) { } }
@Test public void testDefaultValuedPathParameterBoolean() throws Exception { final class Test extends DefaultValuedPathParameterEndpoint<Boolean> {} try { g.generateConfig(Test.class); fail("Config generation for endpoint with default-valued path parameter should have failed."); } catch (InvalidParameterAnnotationsException expected) { } }
@Test public void testDefaultValuedPathParameterInteger() throws Exception { final class Test extends DefaultValuedPathParameterEndpoint<Integer> {} try { g.generateConfig(Test.class); fail("Config generation for endpoint with default-valued path parameter should have failed."); } catch (InvalidParameterAnnotationsException expected) { } }
@Test public void testDefaultValuedPathParameterLong() throws Exception { final class Test extends DefaultValuedPathParameterEndpoint<Long> {} try { g.generateConfig(Test.class); fail("Config generation for endpoint with default-valued path parameter should have failed."); } catch (InvalidParameterAnnotationsException expected) { } }
@Test public void testDefaultValuedPathParameterString() throws Exception { final class Test extends DefaultValuedPathParameterEndpoint<String> {} try { g.generateConfig(Test.class); fail("Config generation for endpoint with default-valued path parameter should have failed."); } catch (InvalidParameterAnnotationsException expected) { } }
@Test public void testMethodPaths_apiAtCustomPath() throws Exception { @SuppressWarnings("unused") @Api(resource = "foos") class Foos { public List<Foo> list() { return null; } public Foo get(@Named("id") Long id) { return null; } } String apiConfigSource = g.generateConfig(Foos.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); verifyMethodPathAndHttpMethod(root, "myapi.foos.list", "foos", "GET"); verifyMethodPathAndHttpMethod(root, "myapi.foos.get", "foos/{id}", "GET"); }
@Test public void testMethodPaths_apiAndMethodsAtCustomPaths() throws Exception { @Api(resource = "foo") class AcmeCo { @ApiMethod(path = "foos") public List<Foo> list() { return null; } @SuppressWarnings("unused") public List<Foo> listAllTheThings() { return null; } @ApiMethod(path = "give") public Foo giveMeOne() { return null; } } String apiConfigSource = g.generateConfig(AcmeCo.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); verifyMethodPathAndHttpMethod(root, "myapi.foo.list", "foos", "GET"); verifyMethodPathAndHttpMethod(root, "myapi.foo.listAllTheThings", "foo", "GET"); verifyMethodPathAndHttpMethod(root, "myapi.foo.giveMeOne", "give", "POST"); }
@Test public void testMethodPaths_inheritanceResourceSpecified() throws Exception { @Api(resource = "foos") class Foos extends GenFoos<Foo> {} String apiConfigSource = g.generateConfig(Foos.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); GenFoos.verifyMethodPathsAndHttpMethods(Foos.class, "foos", root, "foos"); }
@Test public void testMethodPaths_inheritanceResourceInheritedAndSpecified() throws Exception { @Api(resource = "foos") class Foos extends GenBars<Foo> {} String apiConfigSource = g.generateConfig(Foos.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); GenFoos.verifyMethodPathsAndHttpMethods(Foos.class, "foos", root, "foos"); }
@Test public void testMethodPaths_inheritanceResourceUnspecified() throws Exception { @Api class Foos extends GenFoos<Foo> {} String apiConfigSource = g.generateConfig(Foos.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); verifyMethodPathAndHttpMethod(root, "myapi.foos.insert", "foo", "POST"); verifyMethodPathAndHttpMethod(root, "myapi.foos.update", "foo", "PUT"); verifyMethodPathAndHttpMethod(root, "myapi.foos.list", "foo", "GET"); verifyMethodPathAndHttpMethod(root, "myapi.foos.remove", "remove/{id}", "DELETE"); verifyMethodPathAndHttpMethod(root, "myapi.foos.get", "foo/{id}", "GET"); }
@Test public void testEmptyParameterBodies() throws Exception { String apiConfigSource = g.generateConfig(Endpoint1.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); JsonNode methodListFoo = methods.path("myapi.foos.list"); assertFalse(methodListFoo.path("request").has("parameters")); JsonNode methodGetFoo = methods.path("myapi.foos.get"); assertTrue(methodGetFoo.path("request").has("parameters")); assertFalse(0 == methodGetFoo.path("request").path("parameters").size()); JsonNode methodInsertFoo = methods.path("myapi.foos.insert"); assertFalse(methodInsertFoo.path("request").has("parameters")); }
@Test public void testMethodPaths_inheritanceResourceInheritedAndUnspecified() throws Exception { @Api class Foos extends GenBars<Foos> {} String apiConfigSource = g.generateConfig(Foos.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); GenBars.verifyMethodPathsAndHttpMethods(Foos.class, root); }
@Test public void testMultipleGenericClasses() throws Exception { @Api class GenFoo extends GenBars<Foo> {} @Api @ApiClass(resource = "resource2") class GenBar extends GenBars<Bar> {} String apiConfigSource = g.generateConfig(GenFoo.class, GenBar.class).get("myapi-v1.api"); }
@Test public void testCustomSerialization_renamedProperty() throws Exception { String apiConfigSource = g.generateConfig(SerializerTestEndpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode schemas = root.path("descriptor").path("schemas"); verifyObjectPropertySchema(schemas.path("SerializerTestBean"), "baz", "string"); }
@Test public void testCustomSerialization_ignoredProperty() throws Exception { String apiConfigSource = g.generateConfig(SerializerTestEndpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode schemas = root.path("descriptor").path("schemas"); assertTrue(schemas.path("SerializerTestBean").path("properties").path("bar").isMissingNode()); }
@Test public void testCustomSerialization_customizedProperty() throws Exception { String apiConfigSource = g.generateConfig(SerializerTestEndpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode schemas = root.path("descriptor").path("schemas"); verifyObjectPropertyRef( schemas.path("SerializerTestBean"), "converted", "SerializerTestConvertedToBean"); }
@Test public void testCustomSerialization_customizedPropertyFromApi() throws Exception { String apiConfigSource = g.generateConfig(SerializerTestEndpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); verifyMethodResponseRef( root, SerializerTestEndpoint.class.getName() + ".getBaz", "SerializerTestConvertedToBean"); }
@Test public void testCustomSerialization_overridenProperty() throws Exception { String apiConfigSource = g.generateConfig(SerializerTestEndpoint.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode schemas = root.path("descriptor").path("schemas"); verifyObjectPropertySchema( schemas.path("SerializerTestOverridingPropertyBean"), "bar", "string"); }
@Test public void testCustomSerialization_requestBody() throws Exception { Class<?> clazz = SerializerTestEndpoint.class; String apiConfigSource = g.generateConfig(clazz).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); verifyMethodRequestRef( root, clazz.getName() + ".testMethodRequestBody", "SerializerTestConvertedToBean"); }
@Test public void testCustomSerialization_badRequestBodyAfterSerialization() throws Exception { try { g.generateConfig(BadMethodRequestEndpoint.class).get("myapi-v1.api"); fail("Type serialized to string should not be usable as a method request body"); } catch (MissingParameterNameException e) { } }
@Test public void testStringRequestThrowsException() throws Exception { try { String apiConfigSource = g.generateConfig(StringRequestEndpoint.class).get("myapi-v1.api"); fail("String request body didn't fail in config generation"); } catch (MissingParameterNameException e) { } }
@Test public void testEndpointWithNoPublicMethods() throws Exception { String apiConfigSource = g.generateConfig(Endpoint2.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); assertNull(methods.findValue("api2.foos.invisible0")); assertNull(methods.findValue("api2.foos.invisible1")); }
@Test public void testStringResponseThrowsException() throws Exception { try { String apiConfigSource = g.generateConfig(StringResponseEndpoint.class).get("myapi-v1.api"); fail("String response body didn't fail in config generation"); } catch (InvalidReturnTypeException e) { } }
@Test public void testArrayRequest() throws Exception { @Api class StringArray { @SuppressWarnings("unused") public void foo(@Named("collection") String[] s) {} } String apiConfigSource = g.generateConfig(StringArray.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode methodNode = root.path("methods").path("myapi.stringArray.foo"); assertFalse(methodNode.isMissingNode()); verifyMethodRequestParameter(methodNode.get("request"), "collection", "string", true, true); }
@Test public void testGenericArrayRequest() throws Exception { @Api abstract class GenericArray<T> { @SuppressWarnings("unused") public void foo(@Named("collection") T[] t) {} } GenericArray<Integer> dummy = new GenericArray<Integer>() {}; class Int32Array extends GenericArray<Integer> {} String apiConfigSource = g.generateConfig(Int32Array.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode methodNode = root.path("methods").path("myapi.int32Array.foo"); assertFalse(methodNode.isMissingNode()); verifyMethodRequestParameter(methodNode.get("request"), "collection", "int32", true, true); }
@Test public void testCollectionRequest() throws Exception { @Api class BooleanCollection { @SuppressWarnings("unused") public void foo(@Named("collection") Collection<Boolean> b) {} } String apiConfigSource = g.generateConfig(BooleanCollection.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode methodNode = root.path("methods").path("myapi.booleanCollection.foo"); assertFalse(methodNode.isMissingNode()); verifyMethodRequestParameter(methodNode.get("request"), "collection", "boolean", true, true); }
@Test public void testGenericCollectionRequest() throws Exception { @Api abstract class GenericCollection<T> { @SuppressWarnings("unused") public void foo(@Named("collection") Collection<T> t) {} } GenericCollection<Long> dummy = new GenericCollection<Long>() {}; class Int64Collection extends GenericCollection<Long> {} String apiConfigSource = g.generateConfig(Int64Collection.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode methodNode = root.path("methods").path("myapi.int64Collection.foo"); assertFalse(methodNode.isMissingNode()); verifyMethodRequestParameter(methodNode.get("request"), "collection", "int64", true, true); }
@Test public void testComplexTypeArrayRequest() throws Exception { @Api class ComplexArray { @SuppressWarnings("unused") public void foo(@Named("collection") Bean[] beans) {} } try { g.generateConfig(ComplexArray.class).get("myapi-v1.api"); fail("Non-primitive array should have failed"); } catch (CollectionResourceException expected) { } }
@Test public void testComplexTypeCollectionRequest() throws Exception { @Api class ComplexCollection { @SuppressWarnings("unused") public void foo(@Named("collection") Collection<Object> dates){} } try { g.generateConfig(ComplexCollection.class).get("myapi-v1.api"); fail("Non-primitive array should have failed"); } catch (CollectionResourceException expected) { } }
@Test public void testUnnamedTypeArrayRequest() throws Exception { @Api class UnnamedArray { @SuppressWarnings("unused") public void foo(Long[] dates) {} } try { g.generateConfig(UnnamedArray.class).get("myapi-v1.api"); fail("Non-primitive array should have failed"); } catch (MissingParameterNameException expected) { } }
@Test public void testUnnamedTypeCollectionRequest() throws Exception { @Api class UnnamedCollection { @SuppressWarnings("unused") public void foo(Collection<String> dates){} } try { g.generateConfig(UnnamedCollection.class).get("myapi-v1.api"); fail("Non-primitive array should have failed"); } catch (MissingParameterNameException expected) { } }
@Test public void testMultipleCollectionRequests() throws Exception { @Api class MultipleCollections { @SuppressWarnings("unused") public void foo(@Named("ids") Long[] ids, @Named("authors") List<String> authors) {} } String apiConfigSource = g.generateConfig(MultipleCollections.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode methodNode = root.path("methods").path("myapi.multipleCollections.foo"); assertFalse(methodNode.isMissingNode()); verifyMethodRequestParameter(methodNode.get("request"), "ids", "int64", true, true); verifyMethodRequestParameter(methodNode.get("request"), "authors", "string", true, true); }
@Test public void testEndpointWithInheritance() throws Exception { String apiConfigSource = g.generateConfig(Endpoint3.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); verifyEndpoint1(Endpoint3.class, root); verifyEndpoint3(root); verifyEndpoint1Schema(Endpoint3.class, root); verifyEndpoint3Schema(root); }
@Test public void testResourceAndCollection() throws Exception { @Api class ResourceAndCollection { @SuppressWarnings("unused") public void foo(Bean resource, @Named("authors") List<String> authors) {} } String apiConfigSource = g.generateConfig(ResourceAndCollection.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode methodNode = root.path("methods").path("myapi.resourceAndCollection.foo"); assertFalse(methodNode.isMissingNode()); verifyMethodRequestParameter(methodNode.get("request"), "authors", "string", true, true); assertEquals(1, methodNode.path("request").path("parameters").size()); verifyMethodRequestRef(root, ResourceAndCollection.class.getName() + ".foo", "Bean"); }
@Test public void testCollectionOfArrays() throws Exception { @Api class NestedCollections { @SuppressWarnings("unused") public void foo(@Named("authors") List<String[]> authors) {} } try { g.generateConfig(NestedCollections.class).get("myapi-v1.api"); fail("Nested collections should fail"); } catch (NestedCollectionException expected) { } }
@Test public void testArraysOfCollections() throws Exception { @Api class NestedCollections { @SuppressWarnings("unused") public void foo(@Named("authors") List<String>[] authors) {} } try { g.generateConfig(NestedCollections.class).get("myapi-v1.api"); fail("Nested collections should fail"); } catch (NestedCollectionException expected) { } }
@Test public void testCollectionOfCollections() throws Exception { @Api class NestedCollections { @SuppressWarnings("unused") public void foo(@Named("authors") List<List<String>> authors) {} } try { g.generateConfig(NestedCollections.class).get("myapi-v1.api"); fail("Nested collections should fail"); } catch (NestedCollectionException expected) { } }
@Test public void testArrayOfArrays() throws Exception { @Api class NestedCollections { @SuppressWarnings("unused") public void foo(@Named("authors") String[][] authors) {} } try { g.generateConfig(NestedCollections.class).get("myapi-v1.api"); fail("Nested collections should fail"); } catch (NestedCollectionException expected) { } }
@Test public void testArrayOfResources() throws Exception { @Api class ResourceCollection { @SuppressWarnings("unused") public void foo(List<Bean> beans) {} } try { g.generateConfig(ResourceCollection.class).get("myapi-v1.api"); fail("Array of resources should fail"); } catch (CollectionResourceException expected) { } }
@Test public void testArrayOfSerializedResources() throws Exception { @Api class ResourceCollection { @SuppressWarnings("unused") public void foo(@Named("beans") List<StringBean> beans) {} } String apiConfigSource = g.generateConfig(ResourceCollection.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); JsonNode methodNode = root.path("methods").path("myapi.resourceCollection.foo"); assertFalse(methodNode.isMissingNode()); verifyMethodRequestParameter(methodNode.get("request"), "beans", "string", true, true); }
@Test public void testCanonicalName() throws Exception { @Api(name = "myapi", canonicalName = "My API") class CanonicalApi { } String apiConfigSource = g.generateConfig(CanonicalApi.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); assertEquals("myapi", root.path("name").asText()); assertEquals("My API", root.path("canonicalName").asText()); }
@Test public void testTitle() throws Exception { @Api(name = "myapi", title = "My API Title") class ApiWithTitle { } String apiConfigSource = g.generateConfig(ApiWithTitle.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); assertEquals("myapi", root.path("name").asText()); assertEquals("My API Title", root.path("title").asText()); }
@Test public void testDocumentationLink() throws Exception { @Api(name = "myapi", documentationLink = "http: class ApiWithDocs { } String apiConfigSource = g.generateConfig(ApiWithDocs.class).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); assertEquals("myapi", root.path("name").asText()); assertEquals("http: }
@Test public void testEndpointWithBridgeMethods() throws Exception { String apiConfigSource = g.generateConfig(BridgeInheritanceEndpoint.class).get("myapi-v1.api"); JsonNode root = objectMapper.readValue(apiConfigSource, JsonNode.class); JsonNode methods = root.path("methods"); assertEquals(2, methods.size()); assertNull(methods.findValue("myapi.fn1")); assertNotNull(methods.findValue("myapi.api6.foos.fn1")); assertNotNull(methods.findValue("myapi.api6.foos.fn2")); }
@Test public void testGenericParameterTypes() throws Exception { @Api final class Test <T> { @SuppressWarnings("unused") public void setT(T t) {} } try { g.generateConfig(Test.class).get("myapi-v1.api"); fail(); } catch (GenericTypeException e) { } }
@Test public void testConsistentApiWideConfig() throws Exception { @Api(scopes = { "scopes" }) @ApiClass(scopes = { "foo" }) final class Test1 {} @Api(scopes = { "scopes" }) @ApiClass(scopes = { "bar" }) final class Test2 {} g.generateConfig(Test1.class, Test2.class); }
@Test public void testInconsistentApiWideConfig() throws Exception { @Api(scopes = { "foo" }) @ApiClass(scopes = { "scopes" }) final class Test1 {} @Api(scopes = { "bar" }) @ApiClass(scopes = { "scopes" }) final class Test2 {} try { g.generateConfig(Test1.class, Test2.class); fail(); } catch (InconsistentApiConfigurationException e) { } }
@Test public void testAnonymousClass() throws Exception { @Api() class Test { @SuppressWarnings("unused") public void test() { } } String apiConfigSource = g.generateConfig(new Test() {}.getClass()).get("myapi-v1.api"); ObjectNode root = objectMapper.readValue(apiConfigSource, ObjectNode.class); assertEquals("test", root.path("methods").path("myapi.test").path("path").asText()); }
@Test public void testGenerateConfigRetainsOrder() throws Exception { @Api(name = "onetoday", description = "OneToday API") final class OneToday { } @Api(name = "onetodayadmin", description = "One Today Admin API") final class OneTodayAdmin { } Map<String, String> configs = g.generateConfig(OneToday.class, OneTodayAdmin.class); Iterator<String> iterator = configs.keySet().iterator(); assertEquals("onetoday-v1.api", iterator.next()); assertEquals("onetodayadmin-v1.api", iterator.next()); }
@Test public void testDuplicateMethodEndpoint() throws Exception { try { g.generateConfig(DuplicateMethodEndpoint.class); fail("Config generation for endpoint with overloaded method should have failed."); } catch (OverloadedMethodException expected) { } }