package org.zalando.riptide.httpclient; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.github.restdriver.clientdriver.ClientDriverRequest.Method; import com.github.restdriver.clientdriver.ClientDriverRule; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.client.AsyncClientHttpRequest; import org.springframework.http.client.AsyncClientHttpRequestFactory; import org.springframework.http.client.ClientHttpResponse; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.scheduling.concurrent.ConcurrentTaskExecutor; import org.springframework.web.client.RestTemplate; import org.zalando.riptide.Rest; import org.zalando.riptide.capture.Capture; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.List; import java.util.concurrent.ExecutionException; import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NON_PRIVATE; import static com.github.restdriver.clientdriver.RestClientDriver.giveResponseAsBytes; import static com.github.restdriver.clientdriver.RestClientDriver.onRequestTo; import static com.google.common.io.Resources.getResource; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.singletonList; import static java.util.stream.Collectors.toList; import static org.hamcrest.Matchers.anEmptyMap; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasItems; import static org.hamcrest.Matchers.hasToString; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; import static org.springframework.http.HttpMethod.GET; import static org.springframework.http.HttpMethod.POST; import static org.springframework.http.HttpStatus.Series.SUCCESSFUL; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.zalando.riptide.Bindings.on; import static org.zalando.riptide.Navigators.series; import static org.zalando.riptide.Route.listOf; public final class RestAsyncClientHttpRequestFactoryTest { @Rule public final ClientDriverRule driver = new ClientDriverRule(); @JsonAutoDetect(fieldVisibility = NON_PRIVATE) static class User { String login; public String getLogin() { return login; } } private final CloseableHttpClient client = HttpClientBuilder.create().build(); private final AsyncListenableTaskExecutor executor = new ConcurrentTaskExecutor(); private final RestAsyncClientHttpRequestFactory factory = new RestAsyncClientHttpRequestFactory(client, executor); private final Rest rest = Rest.builder() .baseUrl(driver.getBaseUrl()) .requestFactory(factory) .converter(createJsonConverter()) .build(); private static MappingJackson2HttpMessageConverter createJsonConverter() { final MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); converter.setObjectMapper(createObjectMapper()); return converter; } private static ObjectMapper createObjectMapper() { return new ObjectMapper().findAndRegisterModules() .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); } @After public void tearDown() throws IOException { client.close(); } @Test public void shouldReadContributors() throws IOException { driver.addExpectation(onRequestTo("/repos/zalando/riptide/contributors"), giveResponseAsBytes(getResource("contributors.json").openStream(), "application/json")); final RestTemplate template = new RestTemplate(factory); template.setMessageConverters(singletonList(createJsonConverter())); final List<User> users = template.exchange(driver.getBaseUrl() + "/repos/zalando/riptide/contributors", GET, HttpEntity.EMPTY, new ParameterizedTypeReference<List<User>>() { }).getBody(); final List<String> names = users.stream() .map(User::getLogin) .collect(toList()); assertThat(names, hasItems("jhorstmann", "lukasniemeier-zalando", "whiskeysierra")); } @Test public void shouldReadContributorsAsync() throws IOException { driver.addExpectation(onRequestTo("/repos/zalando/riptide/contributors"), giveResponseAsBytes(getResource("contributors.json").openStream(), "application/json")); final Capture<List<User>> capture = Capture.empty(); final List<User> users = rest.get("/repos/{org}/{repo}/contributors", "zalando", "riptide") .dispatch(series(), on(SUCCESSFUL).call(listOf(User.class), capture)) .thenApply(capture).join(); final List<String> names = users.stream() .map(User::getLogin) .collect(toList()); assertThat(names, hasItems("jhorstmann", "lukasniemeier-zalando", "whiskeysierra")); } @Test public void shouldReadContributorsManually() throws IOException, ExecutionException, InterruptedException { driver.addExpectation(onRequestTo("/repos/zalando/riptide/contributors").withMethod(Method.POST), giveResponseAsBytes(getResource("contributors.json").openStream(), "application/json")); final URI uri = URI.create(driver.getBaseUrl()).resolve("/repos/zalando/riptide/contributors"); final AsyncClientHttpRequest request = factory.createAsyncRequest(uri, POST); request.getHeaders().setAccept(singletonList(APPLICATION_JSON)); request.getBody().write("{}".getBytes(UTF_8)); assertThat(request.getMethod(), is(POST)); assertThat(request.getURI(), hasToString(endsWith("/repos/zalando/riptide/contributors"))); assertThat(request.getHeaders().getAccept(), hasItem(APPLICATION_JSON)); final ClientHttpResponse response = request.executeAsync().get(); assertThat(response.getStatusCode(), is(HttpStatus.OK)); assertThat(response.getRawStatusCode(), is(200)); assertThat(response.getStatusText(), is("OK")); assertThat(response.getHeaders(), is(not(anEmptyMap()))); final InputStream stream = response.getBody(); final ObjectMapper mapper = createObjectMapper(); final List<User> users = mapper.readValue(stream, new TypeReference<List<User>>() { }); final List<String> names = users.stream() .map(User::getLogin) .collect(toList()); assertThat(names, hasItems("jhorstmann", "lukasniemeier-zalando", "whiskeysierra")); } }