YahooFinanceRestTemplateConfiguration.java

package com.tdmconsult.ete.services.yahoo;

import static org.springframework.http.HttpStatus.FORBIDDEN;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;

import java.io.IOException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.MediaType;
import org.springframework.http.client.BufferingClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.http.client.support.HttpRequestWrapper;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriComponentsBuilder;

@Configuration
@EnableConfigurationProperties(YahooFinanceProperties.class)
@Slf4j
public class YahooFinanceRestTemplateConfiguration {

    @Bean
    RestTemplate yahooFinanceRestTemplate(
            final RestTemplateBuilder builder,
            final YahooFinanceProperties yahooFinanceProperties,
            final YahooFinanceSessionService authenticationService) {

        ClientHttpRequestInterceptor interceptor = (request, body, execution) -> {

            final var response = execute(authenticationService, request, body, execution);
            if (response.getStatusCode() == UNAUTHORIZED || response.getStatusCode() == FORBIDDEN) {
                // 2nd try after session invalidation
                authenticationService.invalidateSession();
                return execute(authenticationService, request, body, execution);
            }

            return response;
        };

        return builder
                .uriTemplateHandler(new DefaultUriBuilderFactory(yahooFinanceProperties.baseurl() + "/v10/finance/"))
                .requestFactory(() ->
                        new BufferingClientHttpRequestFactory(
                                new SimpleClientHttpRequestFactory()))

                .additionalInterceptors(interceptor, new JsonLoggingInterceptor())
                .build();
    }

    private static ClientHttpResponse execute(
            final YahooFinanceSessionService authenticationService,
            final HttpRequest request,
            final byte[] body,
            final ClientHttpRequestExecution execution) throws IOException {

        final var session = authenticationService.getSession();

        request.getHeaders().set(
                HttpHeaders.USER_AGENT,
                "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");

        request.getHeaders().set(
                HttpHeaders.COOKIE,
                session.cookie());

        final var uri = UriComponentsBuilder
                .fromUri(request.getURI())
                .queryParam("crumb", session.crumb())
                .build(true)
                .toUri();

        final var wrapper = new HttpRequestWrapper(request) {
            @Override
            public URI getURI() {
                return uri;
            }
        };

        return execution.execute(wrapper, body);
    }

    public static class JsonLoggingInterceptor implements ClientHttpRequestInterceptor {

        @Override
        public ClientHttpResponse intercept(
                final HttpRequest request,
                final byte[] body,
                final ClientHttpRequestExecution execution) throws IOException {

            ClientHttpResponse response = execution.execute(request, body);

            MediaType contentType = response.getHeaders().getContentType();

            if (contentType != null
                    && MediaType.APPLICATION_JSON.includes(contentType)) {

                String json = StreamUtils.copyToString(
                        response.getBody(),
                        StandardCharsets.UTF_8);

                log.trace(request.getMethod() + ": " + request.getURI());
                log.trace("Status: " + response.getStatusCode());
                log.trace(json);
            }

            return response;
        }
    }
}