package cz.habarta.typescript.generator;
import com.fasterxml.jackson.core.type.*;
import cz.habarta.typescript.generator.parser.*;
import cz.habarta.typescript.generator.util.Predicate;
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import io.swagger.annotations.Api;
import java.io.*;
import java.lang.reflect.*;
import java.net.URI;
import java.util.*;
import javax.activation.*;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.xml.bind.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import org.glassfish.jersey.jdkhttp.JdkHttpServerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.*;
public class JaxrsApplicationTest {
@Test
public void testReturnedTypesFromApplication() {
final List<SourceType<Type>> sourceTypes = JaxrsApplicationScanner.scanJaxrsApplication(TestApplication.class, null);
List<Type> types = getTypes(sourceTypes);
final List<Type> expectedTypes = Arrays.<Type>asList(
TestApplication.class,
TestResource1.class
);
assertHasSameItems(expectedTypes, types);
}
@Test
public void testReturnedTypesFromResource() {
final JaxrsApplicationParser.Result result = new JaxrsApplicationParser(TestUtils.settings()).tryParse(new SourceType<>(TestResource1.class));
Assert.assertNotNull(result);
List<Type> types = getTypes(result.discoveredTypes);
final List<Type> expectedTypes = Arrays.asList(
A.class,
new TypeReference<List<B>>(){}.getType(),
C.class,
new TypeReference<List<D>>(){}.getType(),
List.class,
E.class,
new TypeReference<List<F>>(){}.getType(),
G.class,
new TypeReference<Map<String, H>>(){}.getType(),
I.class,
J[].class
);
assertHasSameItems(expectedTypes, types);
}
@Test
public void testWithParsingWithExplicitApplication() {
final List<SourceType<Type>> sourceTypes = JaxrsApplicationScanner.scanJaxrsApplication(TestApplication.class, null);
testWithParsing(sourceTypes, true);
}
@Test
public void testWithParsingWithDefaultApplication() {
final List<SourceType<Type>> sourceTypes = JaxrsApplicationScanner.scanAutomaticJaxrsApplication(new FastClasspathScanner().scan(), null);
testWithParsing(sourceTypes, false);
}
private void testWithParsing(List<SourceType<Type>> types, boolean exactMatch) {
final Model model = new TypeScriptGenerator(TestUtils.settings()).getModelParser().parseModel(types);
final ArrayList<Class<?>> classes = new ArrayList<>();
for (BeanModel beanModel : model.getBeans()) {
classes.add(beanModel.getOrigin());
}
final List<Class<?>> expectedClasses = Arrays.asList(
A.class,
B.class,
C.class,
D.class,
E.class,
F.class,
G.class,
H.class,
I.class,
J.class
);
if (exactMatch) {
assertHasSameItems(expectedClasses, classes);
} else {
Assert.assertTrue(classes.containsAll(expectedClasses));
}
}
@Test
public void testExcludedResource() {
final Predicate<String> excludeFilter = Settings.createExcludeFilter(Arrays.asList(
TestResource1.class.getName()
), null);
final List<SourceType<Type>> sourceTypes = JaxrsApplicationScanner.scanJaxrsApplication(TestApplication.class, excludeFilter);
final List<Type> types = getTypes(sourceTypes);
Assert.assertEquals(1, types.size());
Assert.assertTrue(getTypes(sourceTypes).contains(TestApplication.class));
}
@Test
public void testExcludedType() {
final Settings settings = TestUtils.settings();
settings.setExcludeFilter(Arrays.asList(
A.class.getName(),
J.class.getName()
), null);
final JaxrsApplicationParser jaxrsApplicationParser = new JaxrsApplicationParser(settings);
final JaxrsApplicationParser.Result result = jaxrsApplicationParser.tryParse(new SourceType<>(TestResource1.class));
Assert.assertNotNull(result);
Assert.assertTrue(!getTypes(result.discoveredTypes).contains(A.class));
Assert.assertTrue(getTypes(result.discoveredTypes).contains(J[].class));
}
private List<Type> getTypes(final List<? extends SourceType<? extends Type>> sourceTypes) {
final List<Type> types = new ArrayList<>();
for (SourceType<? extends Type> sourceType : sourceTypes) {
types.add(sourceType.type);
}
return types;
}
private static <T> void assertHasSameItems(Collection<? extends T> expected, Collection<? extends T> actual) {
for (T value : expected) {
Assert.assertTrue("Value '" + value + "' is missing in " + actual, actual.contains(value));
}
for (T value : actual) {
Assert.assertTrue("Value '" + value + "' not expected.", expected.contains(value));
}
}
private static class TestApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
return new LinkedHashSet<Class<?>>(Arrays.asList(
TestResource1.class
));
}
}
@Path("test")
static class TestResource1 {
@GET
public void getVoid() {
}
@GET
public Response getResponse() {
return null;
}
@GET
@Path("a")
public GenericEntity<A> getA() {
return null;
}
@GET
public GenericEntity<List<B>> getB() {
return null;
}
@GET
public C getC() {
return null;
}
@GET
public List<D> getD() {
return null;
}
@SuppressWarnings("rawtypes")
@GET
public List getRawList() {
return null;
}
@GET
@Path("e")
public E getE() {
return null;
}
@Path("f")
public SubResource1 getSubResource1() {
return null;
}
@POST
public void setG(G g) {
}
@POST
public void setHs(Map<String, H> hs) {
}
@POST
public void setI(
@MatrixParam("") String matrixParam,
@QueryParam("") String queryParam,
@PathParam("") String pathParam,
@CookieParam("") String cookieParam,
@HeaderParam("") String headerParam,
@Context String context,
@FormParam("") String formParam,
I entityI) {
}
@POST
public void setJs(J[] js) {
}
@POST
public void setStandardEntity(byte[] value) {}
@POST
public void setStandardEntity(String value) {}
@POST
public void setStandardEntity(InputStream value) {}
@POST
public void setStandardEntity(Reader value) {}
@POST
public void setStandardEntity(File value) {}
@POST
public void setStandardEntity(DataSource value) {}
@POST
public void setStandardEntity(Source value) {}
@POST
public void setStandardEntity(DOMSource value) {}
@POST
public void setStandardEntity(JAXBElement<?> value) {}
@POST
public void setStandardEntity(MultivaluedMap<String,String> value) {}
@POST
public void setStandardEntity(StreamingOutput value) {}
@POST
public void setStandardEntity(Boolean value) {}
@POST
public void setStandardEntity(Character value) {}
@POST
public void setStandardEntity(Number value) {}
@POST
public void setStandardEntity(Integer value) {}
@POST
public void setStandardEntity(int value) {}
}
private static class SubResource1 {
@GET
public List<F> getFs() {
return null;
}
}
private static class A {}
private static class B {}
private static class C {}
private static class D {}
private static class E {}
private static class F {}
private static class G {}
private static class H {}
private static class I {}
private static class J {}
@Test
public void basicInterfaceTest() {
final Settings settings = TestUtils.settings();
settings.generateJaxrsApplicationInterface = true;
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(TestResource1.class));
final String errorMessage = "Unexpected output: " + output;
Assert.assertTrue(errorMessage, output.contains("interface RestApplication"));
Assert.assertTrue(errorMessage, output.contains("getA(): RestResponse<A>;"));
Assert.assertTrue(errorMessage, output.contains("type RestResponse<R> = Promise<R>;"));
Assert.assertTrue(errorMessage, !output.contains("function uriEncoding"));
}
@Test
public void complexInterfaceTest() {
final Settings settings = TestUtils.settings();
settings.generateJaxrsApplicationInterface = true;
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(OrganizationApplication.class));
final String errorMessage = "Unexpected output: " + output;
Assert.assertTrue(errorMessage, output.contains("type RestResponse<R> = Promise<R>;"));
Assert.assertTrue(errorMessage, output.contains("interface Organization"));
Assert.assertTrue(errorMessage, output.contains("interface OrganizationApplication"));
Assert.assertTrue(errorMessage, output.contains("HTTP GET /api/organizations/{ organizationCode : [a-z]+ }/{organizationId}"));
Assert.assertTrue(errorMessage, output.contains("getOrganization(organizationCode: string, organizationId: number): RestResponse<Organization>;"));
Assert.assertTrue(errorMessage, output.contains("searchOrganizations(queryParams?: { name?: string; \"search-limit\"?: number; }): RestResponse<Organization[]>;"));
Assert.assertTrue(errorMessage, output.replace("arg1", "organization").contains("setOrganization(organizationCode: string, organizationId: number, organization: Organization): RestResponse<void>;"));
Assert.assertTrue(errorMessage, output.contains("HTTP GET /api/people/{personId}/address/{addressId}"));
Assert.assertTrue(errorMessage, output.contains("getAddress(personId: number, addressId: number): RestResponse<Address>;"));
Assert.assertTrue(errorMessage, output.contains("HTTP GET /api/people/{personId}"));
Assert.assertTrue(errorMessage, output.contains("getPerson(personId: number): RestResponse<Person>;"));
}
@Test
public void methodNameConflictTest() {
final Settings settings = TestUtils.settings();
settings.generateJaxrsApplicationInterface = true;
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(NameConflictResource.class));
final String errorMessage = "Unexpected output: " + output;
Assert.assertTrue(errorMessage, output.contains("interface RestApplication"));
Assert.assertTrue(errorMessage, output.replace("arg0", "person").contains("person$POST$conflict(person: Person): RestResponse<Person>;"));
Assert.assertTrue(errorMessage, output.contains("person$GET$conflict(): RestResponse<Person>;"));
Assert.assertTrue(errorMessage, output.contains("person$GET$conflict_search(queryParams?: { search?: string; }): RestResponse<Person>;"));
Assert.assertTrue(errorMessage, output.contains("person$GET$conflict_personId(personId: number): RestResponse<Person>;"));
}
@Test
public void customizationTest() {
final Settings settings = TestUtils.settings();
settings.generateJaxrsApplicationInterface = true;
settings.restResponseType = "AxiosPromise";
settings.restOptionsType = "AxiosRequestConfig";
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(OrganizationApplication.class));
final String errorMessage = "Unexpected output: " + output;
Assert.assertTrue(errorMessage, output.contains("type RestResponse<R> = AxiosPromise;"));
Assert.assertTrue(errorMessage, output.contains("searchOrganizations(queryParams?: { name?: string; \"search-limit\"?: number; }, options?: AxiosRequestConfig): RestResponse<Organization[]>;"));
}
@Test
public void basicClientTest() {
final Settings settings = TestUtils.settings();
settings.outputFileType = TypeScriptFileType.implementationFile;
settings.generateJaxrsApplicationClient = true;
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(OrganizationApplication.class));
final String errorMessage = "Unexpected output: " + output;
// HttpClient
Assert.assertTrue(errorMessage, output.contains("interface HttpClient"));
Assert.assertTrue(errorMessage, output.contains("request(requestConfig: { method: string; url: string; queryParams?: any; data?: any; }): RestResponse<any>;"));
// application client
Assert.assertTrue(errorMessage, output.contains("class OrganizationApplicationClient"));
Assert.assertTrue(errorMessage, output.contains("getPerson(personId: number): RestResponse<Person>"));
Assert.assertTrue(errorMessage, output.contains("return this.httpClient.request({ method: \"GET\", url: uriEncoding`api/people/${personId}` });"));
Assert.assertTrue(errorMessage, output.contains("type RestResponse<R> = Promise<R>;"));
// helper
Assert.assertTrue(errorMessage, output.contains("function uriEncoding"));
}
@Test
public void clientCustomizationTest() {
final Settings settings = TestUtils.settings();
settings.outputFileType = TypeScriptFileType.implementationFile;
settings.generateJaxrsApplicationClient = true;
settings.restResponseType = "AxiosPromise";
settings.restOptionsType = "AxiosRequestConfig";
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(OrganizationApplication.class));
final String errorMessage = "Unexpected output: " + output;
// HttpClient
Assert.assertTrue(errorMessage, output.contains("request(requestConfig: { method: string; url: string; queryParams?: any; data?: any; options?: AxiosRequestConfig; }): RestResponse<any>;"));
// application client
Assert.assertTrue(errorMessage, output.contains("class OrganizationApplicationClient"));
Assert.assertTrue(errorMessage, output.contains("getPerson(personId: number, options?: AxiosRequestConfig): RestResponse<Person>"));
Assert.assertTrue(errorMessage, output.contains("return this.httpClient.request({ method: \"GET\", url: uriEncoding`api/people/${personId}`, options: options });"));
Assert.assertTrue(errorMessage, output.contains("type RestResponse<R> = AxiosPromise;"));
}
@Test
public void testNamespacingPerResource() {
final Settings settings = TestUtils.settings();
settings.outputFileType = TypeScriptFileType.implementationFile;
settings.generateJaxrsApplicationInterface = true;
settings.generateJaxrsApplicationClient = true;
settings.jaxrsNamespacing = JaxrsNamespacing.perResource;
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(OrganizationApplication.class));
final String errorMessage = "Unexpected output: " + output;
Assert.assertTrue(errorMessage, !output.contains("class OrganizationApplicationClient"));
Assert.assertTrue(errorMessage, output.contains("class OrganizationsResourceClient implements OrganizationsResource "));
Assert.assertTrue(errorMessage, !output.contains("class OrganizationResourceClient"));
Assert.assertTrue(errorMessage, output.contains("class PersonResourceClient implements PersonResource "));
}
@Test
public void testNamespacingByAnnotation() {
final Settings settings = TestUtils.settings();
settings.outputFileType = TypeScriptFileType.implementationFile;
settings.generateJaxrsApplicationInterface = true;
settings.generateJaxrsApplicationClient = true;
settings.jaxrsNamespacing = JaxrsNamespacing.byAnnotation;
settings.jaxrsNamespacingAnnotation = Api.class;
final String output = new TypeScriptGenerator(settings).generateTypeScript(Input.from(OrganizationApplication.class));
final String errorMessage = "Unexpected output: " + output;
Assert.assertTrue(errorMessage, output.contains("class OrgApiClient implements OrgApi "));
Assert.assertTrue(errorMessage, output.contains("class OrganizationApplicationClient implements OrganizationApplication "));
Assert.assertTrue(errorMessage, !output.contains("class OrganizationsResourceClient"));
Assert.assertTrue(errorMessage, !output.contains("class OrganizationResourceClient"));
Assert.assertTrue(errorMessage, !output.contains("class PersonResourceClient"));
}
@ApplicationPath("api")
public static class OrganizationApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
return new LinkedHashSet<>(Arrays.asList(
OrganizationsResource.class,
PersonResource.class
));
}
}
@Api("OrgApi")
@Path("organizations")
public static class OrganizationsResource {
@PathParam("organizationId")
protected long organizationId;
@GET
public List<Organization> searchOrganizations(@QueryParam("name") String oranizationName, @QueryParam("search-limit") int searchLimit) {
return null;
}
@Path("{ organizationCode : [a-z]+ }/{organizationId}")
public OrganizationResource getOrganizationResource() {
return null;
}
}
public static class OrganizationResource {
@GET
public Organization getOrganization() {
return null;
}
@PUT
public void setOrganization(@PathParam("organizationCode") String organizationCode, Organization organization) {
}
}
public static class Organization {
public String name;
}
@Path("people/{personId}")
public static class PersonResource {
@PathParam("personId")
protected long personId;
@GET
public Person getPerson() {
return null;
}
@GET
@Path("address/{addressId}")
public Address getAddress(@PathParam("addressId") long addressId) {
return null;
}
}
public static class Person {
public String name;
public Person(String name) {
this.name = name;
}
}
public static class Address {
public String name;
}
@Path("conflict")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public static class NameConflictResource {
@POST
public Person person(Person person) {
return new Person("POST");
}
@GET
public Person person() {
return new Person("A");
}
@GET
@Path("search")
public Person person(@QueryParam("search") String search) {
return new Person("B");
}
@GET
@Path("{personId:.+}")
public Person person(@PathParam("personId") long personId) {
return new Person("C");
}
}
public static void main(String[] args) {
final ResourceConfig config = new ResourceConfig(NameConflictResource.class);
JdkHttpServerFactory.createHttpServer(URI.create("http://localhost:9998/"), config);
System.out.println("Jersey started.");
}
}