/*
* Copyright © 2015 Cask Data, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package co.cask.cdap.client;
import co.cask.cdap.client.config.ClientConfig;
import co.cask.cdap.client.util.RESTClient;
import co.cask.cdap.common.BadRequestException;
import co.cask.cdap.common.NotFoundException;
import co.cask.cdap.common.UnauthenticatedException;
import co.cask.cdap.proto.Id;
import co.cask.cdap.proto.codec.NamespacedIdCodec;
import co.cask.cdap.proto.metadata.lineage.CollapseType;
import co.cask.cdap.proto.metadata.lineage.LineageRecord;
import co.cask.common.http.HttpRequest;
import co.cask.common.http.HttpResponse;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
/**
* Provides ways to interact with CDAP Lineage.
*/
public class LineageClient {
private static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(Id.NamespacedId.class, new NamespacedIdCodec()).create();
private final RESTClient restClient;
private final ClientConfig config;
@Inject
public LineageClient(ClientConfig config, RESTClient restClient) {
this.config = config;
this.restClient = restClient;
}
public LineageClient(ClientConfig config) {
this(config, new RESTClient());
}
/**
* Retrieves Lineage for a given dataset.
*
* @param datasetInstance the dataset for which to retrieve lineage
* @param startTime start time for the query, in seconds
* @param endTime end time for the query, in seconds
* @param levels number of levels to compute lineage for, or {@code null} to use the LineageHandler's default value
* @return {@link LineageRecord} for the specified dataset.
*/
public LineageRecord getLineage(Id.DatasetInstance datasetInstance, long startTime, long endTime,
@Nullable Integer levels)
throws IOException, UnauthenticatedException, NotFoundException, BadRequestException {
return getLineage(datasetInstance, Long.toString(startTime), Long.toString(endTime), levels);
}
/**
* Retrieves Lineage for a given dataset.
*
* @param datasetInstance the dataset for which to retrieve lineage
* @param startTime start time for the query, in seconds, or in 'now - xs' format
* @param endTime end time for the query, in seconds, or in 'now - xs' format
* @param levels number of levels to compute lineage for, or {@code null} to use the LineageHandler's default value
* @return {@link LineageRecord} for the specified dataset.
*/
public LineageRecord getLineage(Id.DatasetInstance datasetInstance, String startTime, String endTime,
@Nullable Integer levels)
throws IOException, UnauthenticatedException, NotFoundException, BadRequestException {
return getLineage(datasetInstance, startTime, endTime, Collections.<CollapseType>emptySet(), levels);
}
/**
* Retrieves Lineage for a given dataset.
*
* @param datasetInstance the dataset for which to retrieve lineage
* @param startTime start time for the query, in seconds
* @param endTime end time for the query, in seconds
* @param collapseTypes fields on which lineage relations can be collapsed on
* @param levels number of levels to compute lineage for, or {@code null} to use the LineageHandler's default value
* @return {@link LineageRecord} for the specified dataset.
*/
public LineageRecord getLineage(Id.DatasetInstance datasetInstance, long startTime, long endTime,
Set<CollapseType> collapseTypes, @Nullable Integer levels)
throws IOException, UnauthenticatedException, NotFoundException, BadRequestException {
return getLineage(datasetInstance, Long.toString(startTime), Long.toString(endTime), collapseTypes, levels);
}
/**
* Retrieves Lineage for a given dataset.
*
* @param datasetInstance the dataset for which to retrieve lineage
* @param startTime start time for the query, in seconds, or in 'now - xs' format
* @param endTime end time for the query, in seconds, or in 'now - xs' format
* @param collapseTypes fields on which lineage relations can be collapsed on
* @param levels number of levels to compute lineage for, or {@code null} to use the LineageHandler's default value
* @return {@link LineageRecord} for the specified dataset.
*/
public LineageRecord getLineage(Id.DatasetInstance datasetInstance, String startTime, String endTime,
Set<CollapseType> collapseTypes, @Nullable Integer levels)
throws IOException, UnauthenticatedException, NotFoundException, BadRequestException {
String path = String.format("datasets/%s/lineage?start=%s&end=%s", datasetInstance.getId(),
URLEncoder.encode(startTime, "UTF-8"), URLEncoder.encode(endTime, "UTF-8"));
for (CollapseType collapseType : collapseTypes) {
path = String.format("%s&collapse=%s", path, collapseType);
}
if (levels != null) {
path = String.format("%s&levels=%d", path, levels);
}
return getLineage(datasetInstance, path);
}
/**
* Retrieves Lineage for a given stream.
*
* @param streamId the stream for which to retrieve lineage
* @param startTime start time for the query, in seconds
* @param endTime end time for the query, in seconds
* @param levels number of levels to compute lineage for, or {@code null} to use the LineageHandler's default value
* @return {@link LineageRecord} for the specified stream.
*/
public LineageRecord getLineage(Id.Stream streamId, long startTime, long endTime, @Nullable Integer levels)
throws IOException, UnauthenticatedException, NotFoundException, BadRequestException {
return getLineage(streamId, Long.toString(startTime), Long.toString(endTime), levels);
}
/**
* Retrieves Lineage for a given stream.
*
* @param streamId the stream for which to retrieve lineage
* @param startTime start time for the query, in seconds
* @param endTime end time for the query, in seconds
* @param collapseTypes fields on which lineage relations can be collapsed on
* @param levels number of levels to compute lineage for, or {@code null} to use the LineageHandler's default value
* @return {@link LineageRecord} for the specified stream.
*/
public LineageRecord getLineage(Id.Stream streamId, long startTime, long endTime,
Set<CollapseType> collapseTypes, @Nullable Integer levels)
throws IOException, UnauthenticatedException, NotFoundException, BadRequestException {
return getLineage(streamId, Long.toString(startTime), Long.toString(endTime), collapseTypes, levels);
}
/**
* Retrieves Lineage for a given stream.
*
* @param streamId the stream for which to retrieve lineage
* @param startTime start time for the query, in seconds, or in 'now - xs' format
* @param endTime end time for the query, in seconds, or in 'now - xs' format
* @param levels number of levels to compute lineage for, or {@code null} to use the LineageHandler's default value
* @return {@link LineageRecord} for the specified stream.
*/
public LineageRecord getLineage(Id.Stream streamId, String startTime, String endTime, @Nullable Integer levels)
throws IOException, UnauthenticatedException, NotFoundException, BadRequestException {
return getLineage(streamId, startTime, endTime, Collections.<CollapseType>emptySet(), levels);
}
/**
* Retrieves Lineage for a given stream.
*
* @param streamId the stream for which to retrieve lineage
* @param startTime start time for the query, in seconds, or in 'now - xs' format
* @param endTime end time for the query, in seconds, or in 'now - xs' format
* @param collapseTypes fields on which lineage relations can be collapsed on
* @param levels number of levels to compute lineage for, or {@code null} to use the LineageHandler's default value
* @return {@link LineageRecord} for the specified stream.
*/
public LineageRecord getLineage(Id.Stream streamId, String startTime, String endTime,
Set<CollapseType> collapseTypes, @Nullable Integer levels)
throws IOException, UnauthenticatedException, NotFoundException, BadRequestException {
String path = String.format("streams/%s/lineage?start=%s&end=%s", streamId.getId(),
URLEncoder.encode(startTime, "UTF-8"), URLEncoder.encode(endTime, "UTF-8"));
for (CollapseType collapseType : collapseTypes) {
path = String.format("%s&collapse=%s", path, collapseType);
}
if (levels != null) {
path = String.format("%s&levels=%d", path, levels);
}
return getLineage(streamId, path);
}
private LineageRecord getLineage(Id.NamespacedId namespacedId, String path)
throws IOException, UnauthenticatedException, NotFoundException, BadRequestException {
URL lineageURL = config.resolveNamespacedURLV3(namespacedId.getNamespace(), path);
HttpResponse response = restClient.execute(HttpRequest.get(lineageURL).build(),
config.getAccessToken(),
HttpURLConnection.HTTP_BAD_REQUEST, HttpURLConnection.HTTP_NOT_FOUND);
if (response.getResponseCode() == HttpURLConnection.HTTP_BAD_REQUEST) {
throw new BadRequestException(response.getResponseBodyAsString());
}
if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) {
throw new NotFoundException(namespacedId);
}
return GSON.fromJson(response.getResponseBodyAsString(), LineageRecord.class);
}
}