All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.stewseo.client.transport.restclient.YelpRestClientTransport Maven / Gradle / Ivy

package io.github.stewseo.client.transport.restclient;


import io.github.stewseo.client._types.ErrorResponse;
import io.github.stewseo.client._types.YelpFusionException;
import io.github.stewseo.client.json.JsonpDeserializer;
import io.github.stewseo.client.json.JsonpMapper;
import io.github.stewseo.client.json.NdJsonpSerializable;
import io.github.stewseo.client.transport.Endpoint;
import io.github.stewseo.client.transport.JsonEndpoint;
import io.github.stewseo.client.transport.TransportException;
import io.github.stewseo.client.transport.TransportOptions;
import io.github.stewseo.client.transport.YelpFusionTransport;
import io.github.stewseo.client.util.ApiTypeHelper;
import io.github.stewseo.client.util.MissingRequiredPropertyException;

import io.github.stewseo.client.transport.endpoints.BinaryEndpoint;
import io.github.stewseo.client.transport.endpoints.BooleanEndpoint;
import io.github.stewseo.client.transport.endpoints.BooleanResponse;

import io.github.stewseo.lowlevel.restclient.Cancellable;
import io.github.stewseo.lowlevel.restclient.Request;
import io.github.stewseo.lowlevel.restclient.RequestOptions;
import io.github.stewseo.lowlevel.restclient.Response;
import io.github.stewseo.lowlevel.restclient.ResponseException;
import io.github.stewseo.lowlevel.restclient.ResponseListener;
import io.github.stewseo.lowlevel.restclient.RestClient;
import jakarta.json.stream.JsonGenerator;
import jakarta.json.stream.JsonParser;
import org.apache.http.HttpEntity;
import org.apache.http.entity.BufferedHttpEntity;
import org.apache.http.util.EntityUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CompletableFuture;


public class YelpRestClientTransport implements YelpFusionTransport {

    private static final Logger logger = LoggerFactory.getLogger(YelpRestClientTransport.class);

    @Override
    public void close() throws IOException {
        this.restClient.close();
    }

    private static class RequestFuture extends CompletableFuture {
        private volatile Cancellable cancellable;

        @Override
        public boolean cancel(boolean mayInterruptIfRunning) {
            boolean cancelled = super.cancel(mayInterruptIfRunning);
            if (cancelled && cancellable != null) {
                cancellable.cancel();
            }
            return cancelled;
        }
    }

    private final RestClient restClient;
    private final JsonpMapper mapper;

    private co.elastic.clients.json.JsonpMapper esMapper;

    private final YelpRestTransportOptions transportOptions;

    public YelpRestClientTransport(RestClient restClient, JsonpMapper mapper, TransportOptions options) { // TransportOptions
        this.restClient = restClient;
        this.mapper = mapper;
        String optionsString = null;
        if (options == null) {
            transportOptions = YelpRestTransportOptions.initialOptions();
        } else {
            transportOptions = YelpRestTransportOptions.of(options);
        }

    }
    public YelpRestClientTransport(RestClient restClient, JsonpMapper mapper) throws IOException {
        this(restClient, mapper, null);
    }

    public RestClient restClient() {
        return restClient;
    }


    public  ResponseT performRequest(
            RequestT request,
            Endpoint endpoint,
            TransportOptions transportOptions) throws IOException {

        Request clientRequest = prepareLowLevelRequest(request, endpoint, transportOptions);

        Response clientResponse = restClient.performRequest(clientRequest);

        return getHighLevelResponse(clientResponse, endpoint);
    }

    public  CompletableFuture performRequestAsync(
            RequestT request,
            Endpoint endpoint,
            @Nullable TransportOptions options
    ) {
        Request clientReq = prepareLowLevelRequest(request, endpoint, options);

        RequestFuture future = new RequestFuture<>();

        boolean disableRequiredChecks = ApiTypeHelper.requiredPropertiesCheckDisabled();

        future.cancellable = restClient.performRequestAsync(clientReq, new ResponseListener() {

            @Override
            public void onSuccess(Response clientResp) {
                try (ApiTypeHelper.DisabledChecksHandle h =
                             ApiTypeHelper.DANGEROUS_disableRequiredPropertiesCheck(disableRequiredChecks)) {
                    ResponseT response = getHighLevelResponse(clientResp, endpoint);

                    future.complete(response);
                } catch (Exception e) {
                    future.completeExceptionally(e);
                }
            }
            @Override
            public void onFailure(Exception e) {
                future.completeExceptionally(e);
            }
        });
        return future;
    }


    private  Request prepareLowLevelRequest(
            RequestT request,
            Endpoint endpoint,
            TransportOptions options) {

        String method = endpoint.method(request);
        
        String path = endpoint.requestUrl(request);
        
        Request clientReq = new Request(method, path);

        Map params = endpoint.queryParameters(request);
        
        if(options != null) {
            RequestOptions restOptions =
                    YelpRestTransportOptions.of(options).restClientRequestOptions();
            clientReq.setOptions(restOptions);
        }

        clientReq.addParameters(params);

        return clientReq;
    }

    private  ResponseT getHighLevelResponse(
            Response clientResp,
            Endpoint endpoint
    ) throws IOException {

        int statusCode = clientResp.getStatusLine().getStatusCode();
        try {

            if (statusCode == 200) {
                checkHeaders();
            }

            if (endpoint.isError(statusCode)) {
                JsonpDeserializer errorDeserializer = endpoint.errorDeserializer(statusCode);
                if (errorDeserializer == null) {
                    throw new TransportException(
                            "Request failed with status code '" + statusCode + "'",
                            endpoint.id(), new ResponseException(clientResp)
                    );
                }

                HttpEntity entity = clientResp.getEntity();
                if (entity == null) {
                    throw new TransportException(
                            "Expecting a response body, but none was sent",
                            endpoint.id(), new ResponseException(clientResp)
                    );
                }

                // We may have to replay it.
                entity = new BufferedHttpEntity(entity);
                try {
                    InputStream content = entity.getContent();
                    try (JsonParser parser = mapper.jsonProvider().createParser(content)) {
                        ErrorT error = errorDeserializer.deserialize(parser, mapper);
                        // TODO: have the endpoint provide the exception constructor
                        throw new YelpFusionException(endpoint.id(), (ErrorResponse) error);
                    }
                } catch (MissingRequiredPropertyException errorEx) {
                    // Could not decode exception, try the response type
                    try {
                        ResponseT response = decodeResponse(statusCode, entity, clientResp, endpoint);
                        return response;
                    } catch (Exception respEx) {
                        // No better luck: throw the original error decoding exception
                        throw new TransportException("Failed to decode error response", endpoint.id(), new ResponseException(clientResp));
                    }
                }
            } else {
                return decodeResponse(statusCode, clientResp.getEntity(), clientResp, endpoint);
            }
            
        } finally {
            // Consume the entity unless this is a successful binary endpoint, where the user must consume the entity
            if (!(endpoint instanceof BinaryEndpoint && !endpoint.isError(statusCode))) {
                EntityUtils.consume(clientResp.getEntity());
            }

        }
    }

    private  ResponseT decodeResponse(
            int statusCode, @Nullable HttpEntity entity, Response clientResp, Endpoint endpoint
    ) throws IOException {

        if (endpoint instanceof JsonEndpoint) {
            @SuppressWarnings("unchecked")
            JsonEndpoint jsonEndpoint = (JsonEndpoint) endpoint;
            // Successful response
            ResponseT response = null;
            JsonpDeserializer responseParser = jsonEndpoint.responseDeserializer();

            if (responseParser != null) {
                if (entity == null) {
                    throw new TransportException(
                            "Expecting a response body, but none was sent",
                            endpoint.id(), new ResponseException(clientResp)
                    );
                }
//                logger.info("entity = new BufferedHttpEntity(entity): " + EntityUtils.toString(entity));
                InputStream content = entity.getContent();

                JsonParser parser = mapper.jsonProvider().createParser(content);

                response = responseParser.deserialize(parser, mapper);
            }
            return response;
        }
        else if(endpoint instanceof BooleanEndpoint bep) {

            @SuppressWarnings("unchecked")
            ResponseT response = (ResponseT) new BooleanResponse(bep.getResult(statusCode));
            return response;


        } else if (endpoint instanceof BinaryEndpoint bep) {

            @SuppressWarnings("unchecked")
            ResponseT response = (ResponseT) new HttpClientBinaryResponse(entity);
            return response;

        } else {
            throw new TransportException("Unhandled endpoint type: '" + endpoint.getClass().getName() + "'", endpoint.id());
        }
    }

    private void writeNdJson(NdJsonpSerializable value, ByteArrayOutputStream baos) {
        Iterator values = value._serializables();
        while (values.hasNext()) {
            Object item = values.next();
            if (item instanceof NdJsonpSerializable && item != value) { // do not recurse on the item itself
                writeNdJson((NdJsonpSerializable) item, baos);
            } else {
                JsonGenerator generator = mapper.jsonProvider().createGenerator(baos);
                mapper.serialize(item, generator);
                generator.close();
                baos.write('\n');
            }
        }
    }

    @Override
    public JsonpMapper jsonpMapper() {
        return this.mapper;
    }

    @Override
    public TransportOptions options() {
        return transportOptions;
    }

    @SuppressWarnings("EmptyMethod")
    private void checkHeaders() {

    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy