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

io.kiota.http.jdk.JDKRequestAdapter Maven / Gradle / Ivy

The newest version!
package io.kiota.http.jdk;

import com.microsoft.kiota.ApiClientBuilder;
import com.microsoft.kiota.ApiException;
import com.microsoft.kiota.ApiExceptionBuilder;
import com.microsoft.kiota.PeriodAndDuration;
import com.microsoft.kiota.RequestAdapter;
import com.microsoft.kiota.RequestInformation;
import com.microsoft.kiota.RequestOption;
import com.microsoft.kiota.ResponseHandler;
import com.microsoft.kiota.ResponseHandlerOption;
import com.microsoft.kiota.ResponseHeaders;
import com.microsoft.kiota.serialization.Parsable;
import com.microsoft.kiota.serialization.ParsableFactory;
import com.microsoft.kiota.serialization.ParseNode;
import com.microsoft.kiota.serialization.ParseNodeFactory;
import com.microsoft.kiota.serialization.ParseNodeFactoryRegistry;
import com.microsoft.kiota.serialization.SerializationWriterFactory;
import com.microsoft.kiota.serialization.SerializationWriterFactoryRegistry;
import com.microsoft.kiota.serialization.ValuedEnumParser;
import com.microsoft.kiota.store.BackingStoreFactory;
import com.microsoft.kiota.store.BackingStoreFactorySingleton;
import jakarta.annotation.Nonnull;
import jakarta.annotation.Nullable;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

/** RequestAdapter implementation for VertX */
public class JDKRequestAdapter implements RequestAdapter {
    private static final String contentTypeHeaderKey = "Content-Type";
    @Nonnull private final HttpClient client;
    @Nonnull private ParseNodeFactory pNodeFactory;
    @Nonnull private SerializationWriterFactory sWriterFactory;
    @Nonnull private String baseUrl = "";

    public void setBaseUrl(@Nonnull final String baseUrl) {
        this.baseUrl = Objects.requireNonNull(baseUrl);
    }

    @Nonnull
    public String getBaseUrl() {
        return baseUrl;
    }

    public JDKRequestAdapter() {
        this(null, null, null);
    }

    public JDKRequestAdapter(@Nonnull final HttpClient client) {
        this(client, null, null);
    }

    public JDKRequestAdapter(
            @Nullable final HttpClient client, @Nullable final ParseNodeFactory parseNodeFactory) {
        this(client, parseNodeFactory, null);
    }

    public JDKRequestAdapter(
            @Nullable final HttpClient client,
            @Nullable final ParseNodeFactory parseNodeFactory,
            @Nullable final SerializationWriterFactory serializationWriterFactory) {
        if (client == null) {
            this.client = HttpClient.newHttpClient();
        } else {
            this.client = client;
        }
        if (parseNodeFactory == null) {
            pNodeFactory = ParseNodeFactoryRegistry.defaultInstance;
        } else {
            pNodeFactory = parseNodeFactory;
        }

        if (serializationWriterFactory == null) {
            sWriterFactory = SerializationWriterFactoryRegistry.defaultInstance;
        } else {
            sWriterFactory = serializationWriterFactory;
        }
    }

    @Nonnull
    public SerializationWriterFactory getSerializationWriterFactory() {
        return sWriterFactory;
    }

    public void enableBackingStore(@Nullable final BackingStoreFactory backingStoreFactory) {
        this.pNodeFactory =
                Objects.requireNonNull(
                        ApiClientBuilder.enableBackingStoreForParseNodeFactory(pNodeFactory));
        this.sWriterFactory =
                Objects.requireNonNull(
                        ApiClientBuilder.enableBackingStoreForSerializationWriterFactory(
                                sWriterFactory));
        if (backingStoreFactory != null) {
            BackingStoreFactorySingleton.instance = backingStoreFactory;
        }
    }

    private static final String nullRequestInfoParameter = "parameter requestInfo cannot be null";
    private static final String nullEnumParserParameter = "parameter enumParser cannot be null";
    private static final String nullFactoryParameter = "parameter factory cannot be null";

    @Nullable
    public  List sendCollection(
            @Nonnull final RequestInformation requestInfo,
            @Nullable final HashMap> errorMappings,
            @Nonnull final ParsableFactory factory) {
        Objects.requireNonNull(requestInfo, nullRequestInfoParameter);
        Objects.requireNonNull(factory, nullFactoryParameter);

        HttpResponse response = this.getHttpResponseMessage(requestInfo);
        final ResponseHandler responseHandler = getResponseHandler(requestInfo);
        if (responseHandler == null) {
            this.throwIfFailedResponse(response, errorMappings);
            if (this.shouldReturnNull(response)) {
                return null;
            }
            final ParseNode rootNode = getRootParseNode(response);
            if (rootNode == null) {
                return null;
            }
            final List result = rootNode.getCollectionOfObjectValues(factory);
            return result;
        } else {
            return responseHandler.handleResponse(response, errorMappings);
        }
    }

    private ResponseHandler getResponseHandler(final RequestInformation requestInfo) {
        final Collection requestOptions = requestInfo.getRequestOptions();
        for (final RequestOption rOption : requestOptions) {
            if (rOption instanceof ResponseHandlerOption) {
                final ResponseHandlerOption option = (ResponseHandlerOption) rOption;
                return option.getResponseHandler();
            }
        }
        return null;
    }

    @Nullable
    public  ModelType send(
            @Nonnull final RequestInformation requestInfo,
            @Nullable final HashMap> errorMappings,
            @Nonnull final ParsableFactory factory) {
        Objects.requireNonNull(requestInfo, nullRequestInfoParameter);
        Objects.requireNonNull(factory, nullFactoryParameter);

        HttpResponse response = this.getHttpResponseMessage(requestInfo);
        final ResponseHandler responseHandler = getResponseHandler(requestInfo);
        if (responseHandler == null) {
            this.throwIfFailedResponse(response, errorMappings);
            if (this.shouldReturnNull(response)) {
                return null;
            }
            final ParseNode rootNode = getRootParseNode(response);
            if (rootNode == null) {
                return null;
            }
            final ModelType result = rootNode.getObjectValue(factory);
            return result;
        } else {
            return responseHandler.handleResponse(response, errorMappings);
        }
    }

    @Nullable
    public  ModelType sendPrimitive(
            @Nonnull final RequestInformation requestInfo,
            @Nullable final HashMap> errorMappings,
            @Nonnull final Class targetClass) {
        Objects.requireNonNull(requestInfo, nullRequestInfoParameter);
        Objects.requireNonNull(targetClass, "parameter targetClass cannot be null");

        HttpResponse response = this.getHttpResponseMessage(requestInfo);
        final ResponseHandler responseHandler = getResponseHandler(requestInfo);
        if (responseHandler == null) {
            this.throwIfFailedResponse(response, errorMappings);
            if (this.shouldReturnNull(response)) {
                return null;
            }
            if (targetClass == Void.class) {
                return null;
            } else {
                if (targetClass == InputStream.class) {
                    // TODO: verify streaming responses
                    return (ModelType) response.body();
                }
                final ParseNode rootNode = getRootParseNode(response);
                if (rootNode == null) {
                    return null;
                }
                Object result;
                if (targetClass == Boolean.class) {
                    result = rootNode.getBooleanValue();
                } else if (targetClass == Byte.class) {
                    result = rootNode.getByteValue();
                } else if (targetClass == String.class) {
                    result = rootNode.getStringValue();
                } else if (targetClass == Short.class) {
                    result = rootNode.getShortValue();
                } else if (targetClass == BigDecimal.class) {
                    result = rootNode.getBigDecimalValue();
                } else if (targetClass == Double.class) {
                    result = rootNode.getDoubleValue();
                } else if (targetClass == Integer.class) {
                    result = rootNode.getIntegerValue();
                } else if (targetClass == Float.class) {
                    result = rootNode.getFloatValue();
                } else if (targetClass == Long.class) {
                    result = rootNode.getLongValue();
                } else if (targetClass == UUID.class) {
                    result = rootNode.getUUIDValue();
                } else if (targetClass == OffsetDateTime.class) {
                    result = rootNode.getOffsetDateTimeValue();
                } else if (targetClass == LocalDate.class) {
                    result = rootNode.getLocalDateValue();
                } else if (targetClass == LocalTime.class) {
                    result = rootNode.getLocalTimeValue();
                } else if (targetClass == PeriodAndDuration.class) {
                    result = rootNode.getPeriodAndDurationValue();
                } else if (targetClass == byte[].class) {
                    result = rootNode.getByteArrayValue();
                } else {
                    throw new RuntimeException("unexpected payload type " + targetClass.getName());
                }
                return (ModelType) result;
            }
        } else {
            return responseHandler.handleResponse(response, errorMappings);
        }
    }

    @Nullable
    public > ModelType sendEnum(
            @Nonnull final RequestInformation requestInfo,
            @Nullable final HashMap> errorMappings,
            @Nonnull final ValuedEnumParser enumParser) {
        Objects.requireNonNull(requestInfo, nullRequestInfoParameter);
        Objects.requireNonNull(enumParser, nullEnumParserParameter);

        HttpResponse response = this.getHttpResponseMessage(requestInfo);
        final ResponseHandler responseHandler = getResponseHandler(requestInfo);
        if (responseHandler == null) {
            this.throwIfFailedResponse(response, errorMappings);
            if (this.shouldReturnNull(response)) {
                return null;
            }
            final ParseNode rootNode = getRootParseNode(response);
            if (rootNode == null) {
                return null;
            }
            final Object result = rootNode.getEnumValue(enumParser);
            return (ModelType) result;
        } else {
            return responseHandler.handleResponse(response, errorMappings);
        }
    }

    @Nullable
    public > List sendEnumCollection(
            @Nonnull final RequestInformation requestInfo,
            @Nullable final HashMap> errorMappings,
            @Nonnull final ValuedEnumParser enumParser) {
        Objects.requireNonNull(requestInfo, nullRequestInfoParameter);
        Objects.requireNonNull(enumParser, nullEnumParserParameter);

        HttpResponse response = this.getHttpResponseMessage(requestInfo);
        final ResponseHandler responseHandler = getResponseHandler(requestInfo);
        if (responseHandler == null) {
            this.throwIfFailedResponse(response, errorMappings);
            if (this.shouldReturnNull(response)) {
                return null;
            }
            final ParseNode rootNode = getRootParseNode(response);
            if (rootNode == null) {
                return null;
            }
            final Object result = rootNode.getCollectionOfEnumValues(enumParser);
            return (List) result;
        } else {
            return responseHandler.handleResponse(response, errorMappings);
        }
    }

    @Nullable
    public  List sendPrimitiveCollection(
            @Nonnull final RequestInformation requestInfo,
            @Nullable final HashMap> errorMappings,
            @Nonnull final Class targetClass) {
        Objects.requireNonNull(requestInfo, nullRequestInfoParameter);

        HttpResponse response = getHttpResponseMessage(requestInfo);
        final ResponseHandler responseHandler = getResponseHandler(requestInfo);
        if (responseHandler == null) {
            this.throwIfFailedResponse(response, errorMappings);
            if (this.shouldReturnNull(response)) {
                return null;
            }
            final ParseNode rootNode = getRootParseNode(response);
            if (rootNode == null) {
                return null;
            }
            final List result = rootNode.getCollectionOfPrimitiveValues(targetClass);
            return result;
        } else {
            return responseHandler.handleResponse(response, errorMappings);
        }
    }

    @Nullable
    private ParseNode getRootParseNode(final HttpResponse response) {
        final InputStream rawInputStream = response.body();
        if (rawInputStream == null) {
            return null;
        }

        final String contentType = response.headers().firstValue(contentTypeHeaderKey).orElse(null);
        if (contentType == null) {
            return null;
        }
        return pNodeFactory.getParseNode(contentType, rawInputStream);
    }

    private boolean shouldReturnNull(final HttpResponse response) {
        return response.statusCode() == 204;
    }

    private HttpResponse throwIfFailedResponse(
            @Nonnull final HttpResponse response,
            @Nullable final HashMap> errorMappings) {
        if (response.statusCode() >= 200 && response.statusCode() < 300) return response;

        final String statusCodeAsString = Integer.toString(response.statusCode());
        final int statusCode = response.statusCode();
        final ResponseHeaders responseHeaders =
                HeadersCompatibility.getResponseHeaders(response.headers());
        if (errorMappings == null
                || !errorMappings.containsKey(statusCodeAsString)
                        && !(statusCode >= 400
                                && statusCode < 500
                                && errorMappings.containsKey("4XX"))
                        && !(statusCode >= 500
                                && statusCode < 600
                                && errorMappings.containsKey("5XX"))) {
            final ApiException result =
                    new ApiExceptionBuilder()
                            .withMessage(
                                    "the server returned an unexpected status code and no error"
                                            + " class is registered for this code "
                                            + statusCode)
                            .withResponseStatusCode(statusCode)
                            .withResponseHeaders(responseHeaders)
                            .build();
            throw result;
        }

        final ParsableFactory errorClass =
                errorMappings.containsKey(statusCodeAsString)
                        ? errorMappings.get(statusCodeAsString)
                        : (statusCode >= 400 && statusCode < 500
                                ? errorMappings.get("4XX")
                                : errorMappings.get("5XX"));
        final ParseNode rootNode = getRootParseNode(response);
        if (rootNode == null) {
            final ApiException result =
                    new ApiExceptionBuilder()
                            .withMessage(
                                    "service returned status code"
                                            + statusCode
                                            + " but no response body was found")
                            .withResponseStatusCode(statusCode)
                            .withResponseHeaders(responseHeaders)
                            .build();
            throw result;
        }
        ApiException result =
                new ApiExceptionBuilder(() -> rootNode.getObjectValue(errorClass))
                        .withResponseStatusCode(statusCode)
                        .withResponseHeaders(responseHeaders)
                        .build();
        throw result;
    }

    private HttpResponse getHttpResponseMessage(
            @Nonnull final RequestInformation requestInfo) {
        Objects.requireNonNull(requestInfo, nullRequestInfoParameter);
        this.setBaseUrlForRequestInformation(requestInfo);
        try {
            return this.client.send(
                    HttpRequestCompatibility.convert(requestInfo),
                    HttpResponse.BodyHandlers.ofInputStream());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    private void setBaseUrlForRequestInformation(@Nonnull final RequestInformation requestInfo) {
        Objects.requireNonNull(requestInfo);
        requestInfo.pathParameters.put("baseurl", getBaseUrl());
    }

    @Nonnull
    public  T convertToNativeRequest(@Nonnull final RequestInformation requestInfo) {
        Objects.requireNonNull(requestInfo, nullRequestInfoParameter);
        return (T) getRequestFromRequestInformation(requestInfo);
    }

    protected @Nonnull HttpRequest getRequestFromRequestInformation(
            @Nonnull final RequestInformation requestInfo) {
        return HttpRequestCompatibility.convert(requestInfo);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy