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

com.checkout.ApiClientImpl Maven / Gradle / Ivy

There is a newer version: 6.4.2
Show newest version
package com.checkout;

import com.checkout.common.AbstractFileRequest;
import com.checkout.common.CheckoutUtils;
import com.google.gson.reflect.TypeToken;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

import static com.checkout.ClientOperation.DELETE;
import static com.checkout.ClientOperation.GET;
import static com.checkout.ClientOperation.PATCH;
import static com.checkout.ClientOperation.POST;
import static com.checkout.ClientOperation.PUT;
import static com.checkout.ClientOperation.QUERY;
import static com.checkout.common.CheckoutUtils.validateParams;

public class ApiClientImpl implements ApiClient {

    private static final String AUTHORIZATION = "authorization";
    private static final String PATH = "path";
    private final Serializer serializer;
    private final Transport transport;

    public ApiClientImpl(final CheckoutConfiguration configuration, final UriStrategy uriStrategy) {
        this.serializer = new GsonSerializer();
        this.transport = new ApacheHttpClientTransport(uriStrategy.getUri(), configuration.getHttpClientBuilder(), configuration.getExecutor());
    }

    @Override
    public  CompletableFuture getAsync(final String path, final SdkAuthorization authorization, final Class responseType) {
        validateParams(PATH, path, AUTHORIZATION, authorization);
        return sendRequestAsync(GET, path, authorization, null, null, responseType);
    }

    @Override
    public  CompletableFuture getAsync(final String path, final SdkAuthorization authorization, final Type responseType) {
        validateParams(PATH, path, AUTHORIZATION, authorization);
        return sendRequestAsync(GET, path, authorization, null, null, responseType);
    }

    @Override
    public  CompletableFuture putAsync(final String path, final SdkAuthorization authorization, final Class responseType, final Object request) {
        validateParams(PATH, path, AUTHORIZATION, authorization);
        return sendRequestAsync(PUT, path, authorization, request, null, responseType);
    }

    @Override
    public  CompletableFuture patchAsync(final String path, final SdkAuthorization authorization, final Class responseType, final Object request, final String idempotencyKey) {
        validateParams(PATH, path, AUTHORIZATION, authorization);
        return sendRequestAsync(PATCH, path, authorization, request, idempotencyKey, responseType);
    }

    @Override
    public  CompletableFuture patchAsync(final String path, final SdkAuthorization authorization, final Type type, final Object request, final String idempotencyKey) {
        validateParams(PATH, path, AUTHORIZATION, authorization, "type", type, "request", request);
        return sendRequestAsync(PATCH, path, authorization, request, idempotencyKey, type);
    }

    @Override
    public  CompletableFuture postAsync(final String path, final SdkAuthorization authorization, final Class responseType, final Object request, final String idempotencyKey) {
        validateParams(PATH, path, AUTHORIZATION, authorization);
        return sendRequestAsync(POST, path, authorization, request, idempotencyKey, responseType);
    }

    @Override
    public  CompletableFuture postAsync(final String path, final SdkAuthorization authorization, final Type responseType, final Object request, final String idempotencyKey) {
        validateParams(PATH, path, AUTHORIZATION, authorization);
        return sendRequestAsync(POST, path, authorization, request, idempotencyKey, responseType);
    }

    @Override
    public CompletableFuture deleteAsync(final String path, final SdkAuthorization authorization) {
        validateParams(PATH, path, AUTHORIZATION, authorization);
        return sendRequestAsync(DELETE, path, authorization, null, null, EmptyResponse.class);
    }

    @Override
    public  CompletableFuture deleteAsync(String path, SdkAuthorization authorization, Class responseType) {
        validateParams(PATH, path, AUTHORIZATION, authorization);
        return sendRequestAsync(DELETE, path, authorization, null, null, responseType);
    }

    @Override
    public CompletableFuture postAsync(final String path, final SdkAuthorization authorization, final Map> resultTypeMappings, final Object request, final String idempotencyKey) {
        validateParams(PATH, path, AUTHORIZATION, authorization, "resultTypeMappings", resultTypeMappings);
        return transport.invoke(POST, path, authorization, serializer.toJson(request), idempotencyKey, null)
                .thenApply(this::errorCheck)
                .thenApply(response -> {
                    final Class responseType = resultTypeMappings.get(response.getStatusCode());
                    if (responseType == null) {
                        throw new IllegalStateException("The status code " + response.getStatusCode() + " is not mapped to a result type");
                    }
                    return deserialize(response, responseType);
                });
    }

    @Override
    public  CompletableFuture queryAsync(final String path,
                                                                    final SdkAuthorization authorization,
                                                                    final Object filter,
                                                                    final Class responseType) {
        validateParams(PATH, path, AUTHORIZATION, authorization, "filter", filter);
        final Map params = serializer.fromJson(serializer.toJson(filter),
                new TypeToken>() {
                }.getType());
        return transport.invoke(QUERY, path, authorization, null, null, params)
                .thenApply(this::errorCheck)
                .thenApply(response -> deserialize(response, responseType));
    }

    @Override
    public CompletableFuture queryCsvContentAsync(final String path,
                                                                   final SdkAuthorization authorization,
                                                                   final Object filter,
                                                                   final String targetFile) {
        validateParams(PATH, path, AUTHORIZATION, authorization);
        Map params = new HashMap<>();
        if (filter != null) {
            params = serializer.fromJson(serializer.toJson(filter),
                    new TypeToken>() {
                    }.getType());
        }
        return transport.invoke(QUERY, path, authorization, null, null, params)
                .thenApply(this::errorCheck)
                .thenApply(body -> transform(processAndGetContent(targetFile, body), body));
    }

    @SuppressWarnings("squid:S3516")
    private ContentResponse processAndGetContent(final String targetFile, final Response response) {
        final String content = response.getBody();
        if (StringUtils.isBlank(targetFile) || StringUtils.isBlank(content)) {
            return new ContentResponse(content);
        }
        final File file = new File(targetFile);
        if (!file.exists()) {
            try {
                final boolean ignore = file.createNewFile(); //NOSONAR
            } catch (final IOException e) {
                throw new CheckoutException(String.format("Failed creating file %s", targetFile));
            }
        }
        try {
            Files.copy(
                    new ByteArrayInputStream(content.getBytes()),
                    file.toPath(),
                    StandardCopyOption.REPLACE_EXISTING);
        } catch (final IOException e) {
            throw new CheckoutException(String.format("Failed writing file %s", targetFile), e);
        }
        return new ContentResponse(content);
    }

    @Override
    public  CompletableFuture submitFileAsync(final String path, final SdkAuthorization authorization,
                                                                         final AbstractFileRequest request, final Class responseType) {
        validateParams(PATH, path, AUTHORIZATION, authorization, "fileRequest", request);
        return transport.submitFile(path, authorization, request)
                .thenApply(this::errorCheck)
                .thenApply(response -> deserialize(response, responseType));
    }

    private  CompletableFuture sendRequestAsync(final ClientOperation clientOperation, final String path, final SdkAuthorization authorization, final Object request, final String idempotencyKey, final Type responseType) {
        return transport.invoke(clientOperation, path, authorization, request == null ? null : serializer.toJson(request), idempotencyKey, null)
                .thenApply(this::errorCheck)
                .thenApply(response -> deserialize(response, responseType));
    }

    private Response errorCheck(final Response response) {
        if (!CheckoutUtils.isSuccessHttpStatusCode(response.getStatusCode())) {
            final Map errorDetails = serializer.fromJson(response.getBody());
            throw new CheckoutApiException(response.getStatusCode(), response.getHeaders(), errorDetails);
        }
        return response;
    }

    private  T deserialize(final Response response, final Class responseType) {
        T result = serializer.fromJson(response.getBody(), responseType);
        //Unfortunate but GSON returns null if body is null, we need to instantiate the class type parameter for adding metadata
        if (result == null) {
            result = getInstanceFromT(responseType);
        }
        return transform(result, response);
    }

    private  T deserialize(final Response response, final Type responseType) {
        T result = serializer.fromJson(response.getBody(), responseType);
        //Unfortunate but GSON returns null if body is null, we need to instantiate type parameter for adding metadata
        if (result == null) {
            result = getInstanceFromT(responseType);
        }
        return transform(result, response);

    }

    private  T getInstanceFromT(final Type responseType) {
        try {
            final Class aClass;
            if (responseType instanceof ParameterizedType) {
                final ParameterizedType type = (ParameterizedType) responseType;
                aClass = Class.forName(type.getRawType().getTypeName());
            } else {
                aClass = Class.forName(responseType.getTypeName());
            }
            return (T) aClass.newInstance();
        } catch (final ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            throw new CheckoutException(e);
        }
    }

    private  T transform(final T result, final Response response) {
        result.setBody(response.getBody());
        result.setHttpStatusCode(response.getStatusCode());
        result.setResponseHeaders(response.getHeaders());
        return result;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy