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

dev.sixpack.client.Sixpack Maven / Gradle / Ivy

The newest version!
package dev.sixpack.client;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import dev.sixpack.api.data.Configuration;
import dev.sixpack.api.data.Dataset;
import dev.sixpack.api.data.SixpackError;
import dev.sixpack.api.exception.SixpackClientException;
import dev.sixpack.api.exception.SixpackException;
import dev.sixpack.api.exception.SixpackServerException;
import lombok.Getter;
import okhttp3.*;

import javax.crypto.Mac;
import java.io.IOException;
import java.util.UUID;

import static dev.sixpack.client.HmacHelper.bytesToHex;
import static dev.sixpack.client.HmacHelper.macFactory;
import static dev.sixpack.utils.FormatUtils.mFormat;
import static java.lang.System.getenv;
import static java.nio.charset.StandardCharsets.UTF_8;

public class Sixpack {

    public static final String SX_ACCOUNT = "SX-Account";
    public static final String SX_USER = "SX-User";
    public static final String SX_TIMESTAMP = "SX-Timestamp";
    public static final String SX_NONCE = "SX-Nonce";
    public static final String SX_SIGNATURE = "SX-Signature";

    public static final String SIXPACK_URL = "SIXPACK_URL";
    public static final String SIXPACK_ACCOUNT = "SIXPACK_ACCOUNT";
    public static final String SIXPACK_USER_ID = "SIXPACK_USER_ID";
    public static final String SIXPACK_API_KEY = "SIXPACK_API_KEY";

    @Getter
    protected static final Sixpack instance;

    static {
        instance = new Sixpack();
    }

    /**
     * Creates a new dataset request builder chain.
     *
     * @return A builder chain to construct a new dataset request.
     */
    public static EnvironmentSetter newDataset() {
        return new EnvironmentSetter();
    }

    public static void refreshDataset(Dataset dataset, long waitUntil) {
        Dataset freshDataset = Sixpack.instance.getDataset(dataset.getId(), waitUntil);
        dataset.updateFrom(freshDataset);
    }

    public static class Utils {
        public static  T mapConfigurationToObject(Configuration output, Class clazz) {
            return objectMapper.convertValue(output, clazz);
        }
    }

    private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");
    private static OkHttpClient client = new OkHttpClient();
    private static final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule());

    static void setClient(OkHttpClient client) {
        Sixpack.client = client;
    }

    private String url;
    private String account;
    private String userId;
    private String apiKey;

    Sixpack() {
        url = getenv(SIXPACK_URL);
        account = getenv(SIXPACK_ACCOUNT);
        userId = getenv(SIXPACK_USER_ID);
        apiKey = getenv(SIXPACK_API_KEY);
    }

    /**
     * Sets the URL of the Sixpack instance to communicate with. Before calling that method, the url is loaded from the environment variable SIXPACK_URL.
     *
     * @param url The URL of the Sixpack instance.
     * @return The same instance of the Sixpack client.
     */
    @SuppressWarnings("UnusedReturnValue")
    public Sixpack setUrl(String url) {
        instance.url = url;
        return instance;
    }

    /**
     * Sets the account to use for the requests. Before calling that method, the account is loaded from the environment variable SIXPACK_ACCOUNT.
     *
     * @param account The account to use for the requests.
     * @return The same instance of the Sixpack client.
     */
    @SuppressWarnings("UnusedReturnValue")
    public Sixpack setAccount(String account) {
        instance.account = account;
        return instance;
    }

    /**
     * Sets the user ID to use for the requests. Before calling that method, the user ID is loaded from the environment variable SIXPACK_USER.
     *
     * @param userId The user ID to use for the requests.
     * @return The same instance of the Sixpack client.
     */
    @SuppressWarnings("UnusedReturnValue")
    public Sixpack setUserId(String userId) {
        instance.userId = userId;
        return instance;
    }

    /**
     * Sets the API key to use for the requests. Before calling that method, the API key is loaded from the environment variable SIXPACK_API_KEY.
     *
     * @param apiKey The API key to use for the requests.
     * @return The same instance of the Sixpack client.
     */
    @SuppressWarnings("UnusedReturnValue")
    public Sixpack setApiKey(String apiKey) {
        instance.apiKey = apiKey;
        return instance;
    }

    private void validateSettings() {
        validateParameter(url, SIXPACK_URL, "withUrl");
        validateParameter(account, SIXPACK_ACCOUNT, "withAccount");
        validateParameter(userId, SIXPACK_USER_ID, "withUserId");
        validateParameter(apiKey, SIXPACK_API_KEY, "withApiKey");
    }

    private void validateParameter(String parameter,
                                   String parameterName,
                                   String parameterSetter) {
        if (parameter == null) {
            throw new SixpackClientException(mFormat(
                    "Parameter {} is not set, either set it as environment variable or initialise it with the respective setter Sixpack::{}",
                    parameterName, parameterSetter));
        }
    }

    public Dataset requestDataset(Dataset requestedDataset) {
        validateSettings();
        String requestBodyString;
        try {
            requestBodyString = objectMapper.writeValueAsString(requestedDataset);
        } catch (JsonProcessingException e) {
            throw new SixpackClientException("Failed to serialize the requested dataset", e);
        }
        RequestBody body = RequestBody.create(requestBodyString, JSON);
        Request.Builder httpRequestBuilder = new Request.Builder()
                .url(mFormat("{}/1/datasets",
                        url))
                .header(SX_ACCOUNT, account)
                .header(SX_USER, userId)
                .post(body);
        sign(httpRequestBuilder);
        Request httpRequest = httpRequestBuilder.build();
        return getDataset(httpRequest);
    }

    public Dataset getDataset(String datasetId, long waitUntil) {
        Request.Builder httpRequestBuilder = new Request.Builder()
                .url(mFormat("{}/1/datasets/{}?waitUntil={}",
                        url,
                        datasetId,
                        waitUntil))
                .header(SX_ACCOUNT, account)
                .header(SX_USER, userId);
        sign(httpRequestBuilder);
        Request httpRequest = httpRequestBuilder.build();
        return getDataset(httpRequest);
    }

    private Dataset getDataset(Request httpRequest) {
        try (Response response = client.newCall(httpRequest).execute()) {
            String responseBody;
            try {
                responseBody = response.body().string();
            } catch (IOException e) {
                throw new SixpackClientException("Failed to read the response body from Sixpack", e);
            }
            try {
                return objectMapper.readValue(responseBody, Dataset.class);
            } catch (JsonProcessingException e) {
                try {
                    SixpackError error = objectMapper.readValue(responseBody, SixpackError.class);
                    throw new SixpackServerException(error.getMessage(), error.getHttpCode());
                } catch (JsonProcessingException e2) {
                    throw new SixpackClientException(mFormat("Failed to deserialize the response body from Sixpack: {}",
                            responseBody),
                            e);
                }
            }
        } catch (IOException e) {
            throw new SixpackClientException("Failed to communicate with Sixpack", e);
        }
    }

    private void sign(Request.Builder requestBuilder) {
        assert requestBuilder.getUrl$okhttp() != null;
        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
        String nonce = UUID.randomUUID().toString();
        String toSign = timestamp + "|" + nonce;
        Mac mac = macFactory(apiKey);
        String signature = bytesToHex(mac.doFinal(toSign.getBytes(UTF_8)));
        requestBuilder.addHeader(SX_TIMESTAMP, timestamp);
        requestBuilder.addHeader(SX_NONCE, nonce);
        requestBuilder.addHeader(SX_SIGNATURE, signature);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy