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

com.gooddata.warehouse.WarehouseService Maven / Gradle / Ivy

/*
 * Copyright (C) 2004-2017, GoodData(R) Corporation. All rights reserved.
 * This source code is licensed under the BSD-style license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */
package com.gooddata.warehouse;

import com.gooddata.AbstractPollHandler;
import com.gooddata.AbstractService;
import com.gooddata.FutureResult;
import com.gooddata.GoodDataException;
import com.gooddata.GoodDataRestException;
import com.gooddata.GoodDataSettings;
import com.gooddata.PollResult;
import com.gooddata.collections.MultiPageList;
import com.gooddata.collections.Page;
import com.gooddata.collections.PageRequest;
import com.gooddata.collections.PageableList;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriTemplate;

import java.io.IOException;
import java.net.URI;

import static com.gooddata.util.Validate.notEmpty;
import static com.gooddata.util.Validate.notNull;
import static java.lang.String.format;

/**
 * Provide access to warehouse API - create, update, list and delete warehouses.
 */
public class WarehouseService extends AbstractService {

    private static final String DEFAULT_SCHEMA_NAME = "default";

    /**
     * Sets RESTful HTTP Spring template. Should be called from constructor of concrete service extending
     * this abstract one.
     *
     * @param restTemplate RESTful HTTP Spring template
     * @param settings settings
     */
    public WarehouseService(final RestTemplate restTemplate, final GoodDataSettings settings) {
        super(restTemplate, settings);
    }

    /**
     * Sets RESTful HTTP Spring template. Should be called from constructor of concrete service extending
     * this abstract one.
     *
     * @param restTemplate RESTful HTTP Spring template
     * @deprecated use WarehouseService(RestTemplate, GoodDataSettings) constructor instead
     */
    @Deprecated
    public WarehouseService(final RestTemplate restTemplate) {
        super(restTemplate);
    }

    /**
     * Create new warehouse.
     *
     * @param warehouse warehouse to create
     *
     * @return created warehouse
     */
    public FutureResult createWarehouse(final Warehouse warehouse) {
        notNull(warehouse, "warehouse");
        final WarehouseTask task;
        try {
            task = restTemplate.postForObject(Warehouses.URI, warehouse, WarehouseTask.class);
        } catch (GoodDataException | RestClientException e) {
            throw new GoodDataException("Unable to create Warehouse", e);
        }

        if (task == null) {
            throw new GoodDataException("Empty response when Warehouse POSTed to API");
        }

        return new PollResult<>(this, new AbstractPollHandler(task.getPollUri(), WarehouseTask.class, Warehouse.class) {

            @Override
            public boolean isFinished(ClientHttpResponse response) throws IOException {
                return HttpStatus.CREATED.equals(response.getStatusCode());
            }

            @Override
            protected void onFinish() {
                if (!getResult().isEnabled()) {
                    throw new GoodDataException("Created warehouse, uri: " + getResult().getUri() + " is not enabled!");
                }
            }

            @Override
            public void handlePollResult(WarehouseTask pollResult) {
                try {
                    final Warehouse warehouse = restTemplate.getForObject(pollResult.getWarehouseUri(), Warehouse.class);
                    setResult(warehouse);
                } catch (GoodDataException | RestClientException e) {
                    throw new GoodDataException("Warehouse creation finished, but can't get created warehouse, uri: "
                            + pollResult.getWarehouseUri(), e);
                }
            }

            @Override
            public void handlePollException(final GoodDataRestException e) {
                throw new GoodDataException("Unable to create warehouse", e);
            }
        });
    }

    /**
     * Delete Warehouse.
     * @param warehouse to delete
     */
    public void removeWarehouse(final Warehouse warehouse) {
        notNull(warehouse, "warehouse");
        notNull(warehouse.getUri(), "warehouse.uri");
        try {
            restTemplate.delete(warehouse.getUri());
        } catch (GoodDataException | RestClientException e) {
            throw new GoodDataException("Unable to delete Warehouse, uri: " + warehouse.getUri(), e);
        }
    }

    /**
     * Get Warehouse identified by given uri.
     * @param uri warehouse uri
     * @return Warehouse
     * @throws com.gooddata.GoodDataException when Warehouse can't be accessed
     */
    public Warehouse getWarehouseByUri(final String uri) {
        notEmpty(uri, "uri");
        try {
            return restTemplate.getForObject(uri, Warehouse.class);
        } catch (GoodDataRestException e) {
            if (HttpStatus.NOT_FOUND.value() == e.getStatusCode()) {
                throw new WarehouseNotFoundException(uri, e);
            } else {
                throw e;
            }
        } catch (RestClientException e) {
            throw new GoodDataException("Unable to get Warehouse instance " + uri, e);
        }
    }

    /**
     * Get Warehouse identified by given id.
     * @param id warehouse id
     * @return Warehouse
     * @throws com.gooddata.GoodDataException when Warehouse can't be accessed
     */
    public Warehouse getWarehouseById(String id) {
        notEmpty(id, "id");
        return getWarehouseByUri(uriFromId(id));
    }

    private static String uriFromId(String id) {
        return Warehouse.TEMPLATE.expand(id).toString();
    }

    /**
     * Lists Warehouses. Returns empty list in case there are no warehouses.
     * Returns only first page if there's more instances than page limit. Use {@link PageableList#stream()} to iterate
     * over all pages, or {@link MultiPageList#collectAll()} to load the entire list.
     *
     * @return MultiPageList first page of list of warehouse instances or empty list
     */
    public PageableList listWarehouses() {
        return listWarehouses(new PageRequest());
    }

    /**
     * Lists Warehouses. Returns empty list in case there are no warehouses.
     * Returns requested page (by page limit and offset). Use {@link #listWarehouses()} to get first page with default setting.
     *
     * @param startPage page to be listed
     * @return MultiPageList requested page of list of instances or empty list
     */
    public PageableList listWarehouses(final Page startPage) {
        notNull(startPage, "startPage");
        return new MultiPageList<>(startPage, page -> listWarehouses(getWarehousesUri(page)));
    }

    private URI getWarehousesUri() {
        return URI.create(Warehouses.URI);
    }

    private URI getWarehousesUri(final Page page) {
        return page.getPageUri(UriComponentsBuilder.fromUri(getWarehousesUri()));
    }

    private PageableList listWarehouses(final URI uri) {
        try {
            final Warehouses result = restTemplate.getForObject(uri, Warehouses.class);
            if (result == null) {
                return new PageableList<>();
            }
            return result;
        } catch (GoodDataException | RestClientException e) {
            throw new GoodDataException("Unable to list Warehouses", e);
        }
    }

    /**
     * Lists warehouse users. Returns empty list in case there are no users.
     * Use {@link PageableList#stream()} to iterate over all pages,
     * or {@link MultiPageList#collectAll()} to load the entire list.
     *
     * @param warehouse warehouse
     * @return MultiPageList requested page of list of instances or empty list
     */
    public PageableList listWarehouseUsers(final Warehouse warehouse) {
        return listWarehouseUsers(warehouse, new PageRequest());
    }

    /**
     * Lists warehouse users, starting with specified page. Returns empty list in case there are no users.
     * Use {@link PageableList#stream()} to iterate over all pages,
     * or {@link MultiPageList#collectAll()} to load the entire list.
     *
     * @param warehouse warehouse
     * @param startPage page to start with
     * @return MultiPageList requested page of list of instances starting with startPage or empty list
     */
    public PageableList listWarehouseUsers(final Warehouse warehouse, final Page startPage) {
        notNull(warehouse, "warehouse");
        notNull(warehouse.getId(), "warehouse.id");
        notNull(startPage, "startPage");

        return new MultiPageList<>(startPage,
                page -> listWarehouseUsers(warehouse, getWarehouseUsersUri(warehouse, page)));
    }

    private URI getWarehouseUsersUri(final Warehouse warehouse) {
        return WarehouseUsers.TEMPLATE.expand(warehouse.getId());
    }

    private URI getWarehouseUsersUri(final Warehouse warehouse, final Page page) {
        return page.getPageUri(UriComponentsBuilder.fromUri(getWarehouseUsersUri(warehouse)));
    }

    private PageableList listWarehouseUsers(final Warehouse warehouse, final URI uri) {
        try {
            final WarehouseUsers result = restTemplate.getForObject(uri, WarehouseUsers.class);
            return result == null ? new PageableList<>() : result;
        } catch (GoodDataException | RestClientException e) {
            throw new GoodDataException("Unable to list users of warehouse " + warehouse.getId(), e);
        }
    }

    /**
     * Add given user to given warehouse.
     *
     * @param warehouse warehouse the user should be added to
     * @param user user to be added
     * @return added user in warehouse
     */
    public FutureResult addUserToWarehouse(final Warehouse warehouse, final WarehouseUser user) {
        notNull(user, "user");
        notNull(warehouse, "warehouse");
        notNull(warehouse.getId(), "warehouse.id");

        final WarehouseTask task;
        try {
            task = restTemplate.postForObject(WarehouseUsers.URI, user, WarehouseTask.class, warehouse.getId());
        } catch (GoodDataException | RestClientException e) {
            throw new GoodDataException("Unable add user to warehouse " + warehouse.getId(), e);
        }
        if (task == null) {
            throw new GoodDataException("Empty response when user POSTed to API");
        }

        return new PollResult<>(this,
                new AbstractPollHandler
                        (task.getPollUri(), WarehouseTask.class, WarehouseUser.class) {

            @Override
            public boolean isFinished(ClientHttpResponse response) throws IOException {
                return HttpStatus.CREATED.equals(response.getStatusCode());
            }

            @Override
            public void handlePollResult(WarehouseTask pollResult) {
                try {
                    final WarehouseUser newUser = restTemplate.getForObject(pollResult.getWarehouseUserUri(), WarehouseUser.class);
                    setResult(newUser);
                } catch (GoodDataException | RestClientException e) {
                    throw new GoodDataException("User added to warehouse, but can't get it back, uri: "
                            + pollResult.getWarehouseUserUri(), e);
                }
            }

            @Override
            public void handlePollException(final GoodDataRestException e) {
                throw new GoodDataException("Unable to add user to warehouse", e);
            }
        });
    }

    /**
     * Remove given user from warehouse instance
     * @param user to remove from warehouse
     * @return empty future result
     * @throws WarehouseUserNotFoundException when user for removal can't be found
     * @throws GoodDataException any other reason
     */
    public FutureResult removeUserFromWarehouse(final WarehouseUser user) {
        notNull(user, "user");
        notNull(user.getUri(), "user.uri");

        final WarehouseTask task;
        try {
            task = restTemplate.exchange(user.getUri(), HttpMethod.DELETE, null, WarehouseTask.class).getBody();
        } catch (GoodDataRestException e) {
            if (HttpStatus.NOT_FOUND.value() == e.getStatusCode()) {
                throw new WarehouseUserNotFoundException(user.getUri(), e);
            } else {
                throw e;
            }
        } catch (RestClientException e) {
            throw new GoodDataException("Unable to remove Warehouse user from instance " + user.getUri(), e);
        }
        if (task == null) {
            throw new GoodDataException("Empty response when user removed");
        }

        return new PollResult<>(this,
                new AbstractPollHandler
                        (task.getPollUri(), WarehouseTask.class, Void.class) {

                @Override
                public boolean isFinished(ClientHttpResponse response) throws IOException {
                    return HttpStatus.CREATED.equals(response.getStatusCode());
                }

                @Override
                public void handlePollResult(WarehouseTask pollResult) {
                    setResult(null);
                }

                @Override
                public void handlePollException(final GoodDataRestException e) {
                    throw new GoodDataException("Unable to remove user from warehouse", e);
                }
        });
    }

    /**
     * Updates given Warehouse.
     *
     * @param toUpdate warehouse to be updated
     * @return updated warehouse
     * @throws com.gooddata.GoodDataException when update fails
     */
    public Warehouse updateWarehouse(final Warehouse toUpdate) {
        notNull(toUpdate, "warehouse");
        notNull(toUpdate.getUri(), "warehouse.uri");
        try {
            restTemplate.put(toUpdate.getUri(), toUpdate);
        } catch (GoodDataRestException | RestClientException e) {
            throw new GoodDataException("Unable to update Warehouse, uri: " + toUpdate.getUri());
        }

        return getWarehouseByUri(toUpdate.getUri());
    }

    /**
     * list schemas for Warehouse
     *
     * @param warehouse to list schemas for
     * @return MultiPageList pageable list of warehouse schemas
     */
    public PageableList listWarehouseSchemas(final Warehouse warehouse) {
        return listWarehouseSchemas(warehouse, new PageRequest());
    }

    /**
     * list schemas for Warehouse
     *
     * @param warehouse to list schemas for
     * @param startPage page to be listed
     * @return MultiPageList pageable list of warehouse schemas
     */
    public PageableList listWarehouseSchemas(final Warehouse warehouse, final Page startPage) {
        return new MultiPageList<>(startPage,
                page -> listWarehouseSchemas(getWarehouseSchemasUri(warehouse, page))
        );
    }

    private URI getWarehouseSchemasUri(final Warehouse warehouse) {
        notNull(warehouse, "warehouse");
        notNull(warehouse.getId(), "warehouse.id");
        return WarehouseSchemas.TEMPLATE.expand(warehouse.getId());
    }

    private URI getWarehouseSchemasUri(final Warehouse warehouse, final Page page) {
        notNull(page, "page");
        return page.getPageUri(UriComponentsBuilder.fromUri(getWarehouseSchemasUri(warehouse)));
    }

    private PageableList listWarehouseSchemas(final URI uri) {
        try {
            final WarehouseSchemas result = restTemplate.getForObject(uri, WarehouseSchemas.class);
            if (result == null) {
                return new PageableList<>();
            }
            return result;
        } catch (GoodDataException | RestClientException e) {
            throw new GoodDataException("Unable to list Warehouse schemas", e);
        }
    }

    /**
     * get warehouse schema by name
     *
     * @param warehouse to get schema for
     * @param name of schema
     * @return warehouse schema
     */
    public WarehouseSchema getWarehouseSchemaByName(final Warehouse warehouse, final String name) {
        notNull(warehouse, "warehouse");
        notNull(warehouse.getId(), "warehouse.id");
        notEmpty(name, "name");
        final String uri = WarehouseSchema.TEMPLATE.expand(warehouse.getId(), name).toString();
        return getWarehouseSchemaByUri(uri);
    }

    /**
     * get warehouse schema by uri
     *
     * @param uri of schema
     * @return warehouse schema
     */
    public WarehouseSchema getWarehouseSchemaByUri(final String uri) {
        notEmpty(uri, "uri");
        try {
            return restTemplate.getForObject(uri, WarehouseSchema.class);
        } catch (GoodDataRestException e) {
            if (HttpStatus.NOT_FOUND.value() == e.getStatusCode()) {
                throw new WarehouseSchemaNotFoundException(uri, e);
            } else {
                throw e;
            }
        } catch (RestClientException e) {
            throw new GoodDataException("Unable to get Warehouse instance " + uri, e);
        }
    }

    /**
     * get default warehouse schema
     *
     * @param warehouse to get default schema for
     * @return default warehouse schema
     */
    public WarehouseSchema getDefaultWarehouseSchema(final Warehouse warehouse) {
        return getWarehouseSchemaByName(warehouse, DEFAULT_SCHEMA_NAME);
    }

    /**
     * List S3 credentials for the Warehouse. Returns empty list if no credentials are found.
     *
     * @param warehouse warehouse to get S3 credentials for
     * @return PageableList with all S3 credentials belonging to the Warehouse (not null)
     * @throws WarehouseS3CredentialsException in case of failure during the REST operation
     */
    public PageableList listWarehouseS3Credentials(final Warehouse warehouse) {
        notNull(warehouse, "warehouse");

        final String uri = getWarehouseS3CredentialsListUri(warehouse).toString();
        try {
            final WarehouseS3CredentialsList result = restTemplate.getForObject(uri, WarehouseS3CredentialsList.class);
            if (result == null) {
                return new PageableList<>();
            }
            return result;
        } catch (GoodDataException | RestClientException e) {
            throw new WarehouseS3CredentialsException(uri, "Unable to list Warehouse S3 credentials at " + uri, e);
        }
    }

    /**
     * Get S3 credentials for the Warehouse based on {@code region} and {@code accessKey}.
     *
     * @param warehouse warehouse to get S3 credentials for
     * @return single S3 credentials record (not null)
     * @throws WarehouseS3CredentialsNotFoundException if no S3 credentials for the given parameters were found
     * @throws WarehouseS3CredentialsException in case of failure during the REST operation
     */
    public WarehouseS3Credentials getWarehouseS3Credentials(final Warehouse warehouse,
                                                            final String region,
                                                            final String accessKey) {
        notNull(warehouse, "warehouse");
        notEmpty(region, "region");
        notEmpty(accessKey, "accessKey");

        final String uri = getWarehouseS3CredentialsUri(warehouse, region, accessKey).toString();

        try {
            return restTemplate.getForObject(uri, WarehouseS3Credentials.class);
        } catch (GoodDataRestException e) {
            if (HttpStatus.NOT_FOUND.value() == e.getStatusCode()) {
                throw new WarehouseS3CredentialsNotFoundException(uri, e);
            } else {
                throw e;
            }
        } catch (RestClientException e) {
            throw new WarehouseS3CredentialsException(uri, "Unable to get Warehouse S3 credentials " + uri, e);
        }
    }

    /**
     * add new S3 credentials to the Warehouse
     *
     * @param warehouse     warehouse the S3 credentials should be added to
     * @param s3Credentials the credentials to store
     * @return added credentials (not null)
     * @throws WarehouseS3CredentialsException in case of failure during the REST operation
     */
    public FutureResult addS3Credentials(final Warehouse warehouse,
                                                                 final WarehouseS3Credentials s3Credentials) {
        notNull(warehouse, "warehouse");
        notEmpty(warehouse.getId(), "warehouse.id");
        notNull(s3Credentials, "s3Credentials");

        final WarehouseTask task = createWarehouseTask(WarehouseS3CredentialsList.URI, HttpMethod.POST,
                s3Credentials, createUpdateHttpEntity(s3Credentials), warehouse.getId());
        final String newCredentialsUri = WarehouseS3Credentials.TEMPLATE.expand(warehouse.getId(),
                s3Credentials.getRegion(), s3Credentials.getAccessKey()).toString();
        return new PollResult<>(this, createS3PollHandler(newCredentialsUri, task, "add"));
    }

    /**
     * update S3 credentials in the Warehouse
     *
     * @param s3Credentials the credentials to update
     * @return updated credentials (not null)
     * @throws WarehouseS3CredentialsException in case of failure during the REST operation
     */
    public FutureResult updateS3Credentials(final WarehouseS3Credentials s3Credentials) {
        notNull(s3Credentials, "s3Credentials");
        notNull(s3Credentials.getUri(), "s3Credentials.links.self");

        final WarehouseTask task = createWarehouseTask(s3Credentials.getUri(), HttpMethod.PUT,
                s3Credentials, createUpdateHttpEntity(s3Credentials));
        return new PollResult<>(this, createS3PollHandler(s3Credentials.getUri(), task, "update"));
    }

    /**
     * delete S3 credentials in the Warehouse
     *
     * @param s3Credentials the credentials to delete
     * @return nothing (Void)
     * @throws WarehouseS3CredentialsException in case of failure during the REST operation
     */
    public FutureResult removeS3Credentials(final WarehouseS3Credentials s3Credentials) {
        notNull(s3Credentials, "s3Credentials");
        notNull(s3Credentials.getUri(), "s3Credentials.links.self");

        final HttpEntity emptyRequestEntity = new HttpEntity<>(new HttpHeaders());
        final WarehouseTask task = createWarehouseTask(s3Credentials.getUri(), HttpMethod.DELETE,
                s3Credentials, emptyRequestEntity);
        return new PollResult<>(this, createS3PollHandler(s3Credentials.getUri(), task, Void.class, "delete"));
    }

    private WarehouseTask createWarehouseTask(final String targetUri,
                                              final HttpMethod httpMethod,
                                              final WarehouseS3Credentials s3Credentials,
                                              final HttpEntity requestEntity,
                                              final Object... args) {
        try {
            final HttpEntity taskHttpEntity = restTemplate.exchange(targetUri, httpMethod,
                    requestEntity, WarehouseTask.class, args);

            if (taskHttpEntity == null || taskHttpEntity.getBody() == null) {
                throw new WarehouseS3CredentialsException(targetUri,
                        format("Empty response when trying to %s S3 credentials via API", httpMethod.name()));
            }
            return taskHttpEntity.getBody();
        } catch (GoodDataException | RestClientException e) {
            final String expandedTargetUri = new UriTemplate(targetUri).expand(args).toString();
            throw new WarehouseS3CredentialsException(targetUri, format("Unable to %s S3 credentials %s with region: %s, access key: %s",
                    httpMethod.name(), expandedTargetUri, s3Credentials.getRegion(), s3Credentials.getAccessKey()), e);
        }
    }

    private HttpEntity createUpdateHttpEntity(final WarehouseS3Credentials s3Credentials) {
        final MappingJacksonValue jacksonValue = new MappingJacksonValue(s3Credentials);
        jacksonValue.setSerializationView(WarehouseS3Credentials.UpdateView.class);
        return new HttpEntity<>(jacksonValue);
    }

    private AbstractPollHandler createS3PollHandler(final String credentialsUri,
                                                                                           final WarehouseTask task,
                                                                                           final String action) {
        return createS3PollHandler(credentialsUri, task, WarehouseS3Credentials.class, action);
    }

    private  AbstractPollHandler createS3PollHandler(final String credentialsUri,
                                                                          final WarehouseTask task,
                                                                          final Class resultClass,
                                                                          final String action) {
        return new AbstractPollHandler(task.getPollUri(), WarehouseTask.class, resultClass) {

            @Override
            public boolean isFinished(ClientHttpResponse response) throws IOException {
                final HttpStatus expectedStatus = "add".equals(action) ? HttpStatus.CREATED : HttpStatus.OK;
                return response.getStatusCode() == expectedStatus;
            }

            @Override
            public void handlePollResult(WarehouseTask pollResult) {
                final String uri = pollResult.getWarehouseS3CredentialsUri();
                try {
                    final R result = restTemplate.getForObject(uri, resultClass);
                    setResult(result);
                } catch (GoodDataException | RestClientException e) {
                    throw new WarehouseS3CredentialsException(uri,
                            format("Attempt to %s S3 credentials in warehouse failed, can't get the result, uri: %s",
                                    action, uri), e);
                }
            }

            @Override
            public void handlePollException(final GoodDataRestException e) {
                throw new WarehouseS3CredentialsException(credentialsUri,
                        format("Unable to %s S3 credentials in warehouse, uri: %s", action, credentialsUri), e);
            }
        };
    }

    private URI getWarehouseS3CredentialsListUri(final Warehouse warehouse) {
        notEmpty(warehouse.getId(), "warehouse.id");

        return WarehouseS3CredentialsList.TEMPLATE.expand(warehouse.getId());
    }

    private URI getWarehouseS3CredentialsUri(final Warehouse warehouse, final String region, final String accessKey) {
        notEmpty(warehouse.getId(), "warehouse.id");

        return WarehouseS3Credentials.TEMPLATE.expand(warehouse.getId(), region, accessKey);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy