/* * Copyright © 2015-2016 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.api.workflow.WorkflowToken; import co.cask.cdap.client.config.ClientConfig; import co.cask.cdap.client.util.RESTClient; import co.cask.cdap.common.NotFoundException; import co.cask.cdap.common.UnauthenticatedException; import co.cask.cdap.proto.DatasetSpecificationSummary; import co.cask.cdap.proto.Id; import co.cask.cdap.proto.WorkflowNodeStateDetail; import co.cask.cdap.proto.WorkflowTokenDetail; import co.cask.cdap.proto.WorkflowTokenNodeDetail; import co.cask.cdap.proto.codec.WorkflowTokenDetailCodec; import co.cask.cdap.proto.codec.WorkflowTokenNodeDetailCodec; import co.cask.cdap.proto.id.NamespaceId; import co.cask.cdap.proto.id.ProgramRunId; import co.cask.common.http.HttpMethod; import co.cask.common.http.HttpResponse; import co.cask.common.http.ObjectResponse; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.inject.Inject; import java.io.IOException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.Map; import javax.annotation.Nullable; /** * Client to interact with workflows */ public class WorkflowClient { private static final Gson GSON = new GsonBuilder() .registerTypeAdapter(WorkflowTokenDetail.class, new WorkflowTokenDetailCodec()) .registerTypeAdapter(WorkflowTokenNodeDetail.class, new WorkflowTokenNodeDetailCodec()) .create(); private final ClientConfig config; private final RESTClient restClient; @Inject public WorkflowClient(ClientConfig config, RESTClient restClient) { this.config = config; this.restClient = restClient; } WorkflowClient(ClientConfig config) { this(config, new RESTClient(config)); } /** * Retrieve the entire {@link WorkflowToken} for the specified workflow run. * * @param workflowRunId the {@link Id.Run} of the workflow * @return {@link WorkflowTokenDetail} for the specified workflow run */ public WorkflowTokenDetail getWorkflowToken(Id.Run workflowRunId) throws UnauthenticatedException, IOException, NotFoundException { return getWorkflowToken(workflowRunId, null, null); } /** * Retrieve all the keys in the {@link WorkflowToken} with the specified scope for the specified workflow run. * * @param workflowRunId the {@link Id.Run} of the workflow * @param scope the specified {@link WorkflowToken.Scope} * @return {@link WorkflowTokenDetail} containing all the keys in the specified {@link WorkflowToken.Scope} * for the specified workflow run */ public WorkflowTokenDetail getWorkflowToken(Id.Run workflowRunId, WorkflowToken.Scope scope) throws UnauthenticatedException, IOException, NotFoundException { return getWorkflowToken(workflowRunId, scope, null); } /** * Retrieve the specified key from the {@link WorkflowToken} for the specified workflow run. * * @param workflowRunId the {@link Id.Run} of the workflow * @param key the specified key * @return {@link WorkflowTokenDetail} containing all the values for the specified key */ public WorkflowTokenDetail getWorkflowToken(Id.Run workflowRunId, String key) throws UnauthenticatedException, IOException, NotFoundException { return getWorkflowToken(workflowRunId, null, key); } /** * Retrieve the {@link WorkflowToken} for the specified workflow run filtered by the specified * {@link WorkflowToken.Scope} and key. * * @param workflowRunId the {@link Id.Run} of the workflow * @param scope the specified {@link WorkflowToken.Scope}. If null, it returns keys for * {@link WorkflowToken.Scope#USER} * @param key the specified key. If null, it returns all keys in the specified {@link WorkflowToken.Scope} * @return {@link WorkflowTokenDetail} with the specified filters */ public WorkflowTokenDetail getWorkflowToken(Id.Run workflowRunId, @Nullable WorkflowToken.Scope scope, @Nullable String key) throws IOException, UnauthenticatedException, NotFoundException { String path = String.format("apps/%s/workflows/%s/runs/%s/token", workflowRunId.getProgram().getApplicationId(), workflowRunId.getProgram().getId(), workflowRunId.getId()); URL url = config.resolveNamespacedURLV3(workflowRunId.getNamespace(), appendScopeAndKeyToUrl(path, scope, key)); HttpResponse response = restClient.execute(HttpMethod.GET, url, config.getAccessToken(), HttpURLConnection.HTTP_NOT_FOUND); if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { String msg = "Either the workflow or its run id"; if (key != null) { msg = String.format("%s or the specified key at the specified scope", msg); } throw new NotFoundException(workflowRunId, msg); } return ObjectResponse.fromJsonBody(response, WorkflowTokenDetail.class, GSON).getResponseObject(); } /** * Retrieve all the key-value pairs in a workflow run's {@link WorkflowToken} that were set by the specified node. * * @param workflowRunId the {@link Id.Run} of the workflow * @param nodeName the name of the node * @return {@link WorkflowTokenNodeDetail} containing all the key-value pairs set by the specified node in the * specified workflow run's {@link WorkflowToken} */ public WorkflowTokenNodeDetail getWorkflowTokenAtNode(Id.Run workflowRunId, String nodeName) throws UnauthenticatedException, IOException, NotFoundException { return getWorkflowTokenAtNode(workflowRunId, nodeName, null, null); } /** * Retrieve all the key-value pairs in a workflow run's {@link WorkflowToken} that were set by the specified node * in the specified {@link WorkflowToken.Scope}. * * @param workflowRunId the {@link Id.Run} of the workflow * @param nodeName the name of the node * @param scope the specified {@link WorkflowToken.Scope} * @return {@link WorkflowTokenNodeDetail} containing all the keys set by the specified node with the * specified {@link WorkflowToken.Scope}in the specified workflow run's {@link WorkflowToken} */ public WorkflowTokenNodeDetail getWorkflowTokenAtNode(Id.Run workflowRunId, String nodeName, WorkflowToken.Scope scope) throws UnauthenticatedException, IOException, NotFoundException { return getWorkflowTokenAtNode(workflowRunId, nodeName, scope, null); } /** * Retrieve the specified key set by the specified node in a specified workflow run's {@link WorkflowToken}. * * @param workflowRunId the {@link Id.Run} of the workflow * @param nodeName the name of the node * @param key the specified key * @return {@link WorkflowTokenNodeDetail} containing the specified key set by the specified node with the * specified {@link WorkflowToken.Scope}in the specified workflow run's {@link WorkflowToken} */ public WorkflowTokenNodeDetail getWorkflowTokenAtNode(Id.Run workflowRunId, String nodeName, String key) throws UnauthenticatedException, IOException, NotFoundException { return getWorkflowTokenAtNode(workflowRunId, nodeName, null, key); } /** * Retrieve the keys set by the specified node in the {@link WorkflowToken} for the specified workflow run * filtered by the specified {@link WorkflowToken.Scope} and key. * * @param workflowRunId the {@link Id.Run} of the workflow * @param nodeName the name of the node * @param scope the specified {@link WorkflowToken.Scope} * @param key the specified key * @return {@link WorkflowTokenDetail} with the specified filters */ public WorkflowTokenNodeDetail getWorkflowTokenAtNode(Id.Run workflowRunId, String nodeName, @Nullable WorkflowToken.Scope scope, @Nullable String key) throws IOException, UnauthenticatedException, NotFoundException { String path = String.format("apps/%s/workflows/%s/runs/%s/nodes/%s/token", workflowRunId.getProgram().getApplicationId(), workflowRunId.getProgram().getId(), workflowRunId.getId(), nodeName); URL url = config.resolveNamespacedURLV3(workflowRunId.getNamespace(), appendScopeAndKeyToUrl(path, scope, key)); HttpResponse response = restClient.execute(HttpMethod.GET, url, config.getAccessToken(), HttpURLConnection.HTTP_NOT_FOUND); if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { String msg = "Either the workflow or its run id"; if (key != null) { msg = String.format("%s or the specified key at the specified scope", msg); } throw new NotFoundException(workflowRunId, msg); } return ObjectResponse.fromJsonBody(response, WorkflowTokenNodeDetail.class, GSON).getResponseObject(); } private String appendScopeAndKeyToUrl(String workflowTokenUrl, @Nullable WorkflowToken.Scope scope, String key) { StringBuilder output = new StringBuilder(workflowTokenUrl); if (scope != null) { output.append(String.format("?scope=%s", scope.name())); if (key != null) { output.append(String.format("&key=%s", key)); } } else if (key != null) { output.append(String.format("?key=%s", key)); } return output.toString(); } /** * Get the local datasets associated with the Workflow run. * @param workflowRunId run id for the Workflow * @return the map of local datasets to the {@link DatasetSpecificationSummary} * @throws IOException if the error occurred during executing the http request * @throws UnauthenticatedException if the request is not authorized successfully in the gateway server * @throws NotFoundException if the workflow with given runid not found */ public Map<String, DatasetSpecificationSummary> getWorkflowLocalDatasets(ProgramRunId workflowRunId) throws IOException, UnauthenticatedException, NotFoundException { HttpResponse response = restClient.execute(HttpMethod.GET, getWorkflowLocalDatasetURL(workflowRunId), config.getAccessToken(), HttpURLConnection.HTTP_NOT_FOUND); if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { throw new NotFoundException(workflowRunId); } return ObjectResponse.fromJsonBody(response, new TypeToken<Map<String, DatasetSpecificationSummary>>() { }) .getResponseObject(); } public void deleteWorkflowLocalDatasets(ProgramRunId workflowRunId) throws IOException, UnauthenticatedException, NotFoundException { HttpResponse response = restClient.execute(HttpMethod.DELETE, getWorkflowLocalDatasetURL(workflowRunId), config.getAccessToken(), HttpURLConnection.HTTP_NOT_FOUND); if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { throw new NotFoundException(workflowRunId); } } private URL getWorkflowLocalDatasetURL(ProgramRunId workflowRunId) throws MalformedURLException { String path = String.format("apps/%s/workflows/%s/runs/%s/localdatasets", workflowRunId.getApplication(), workflowRunId.getProgram(), workflowRunId.getRun()); NamespaceId namespaceId = workflowRunId.getParent().getNamespaceId(); return config.resolveNamespacedURLV3(namespaceId.toId(), path); } /** * Get node states associated with the Workflow run. * @param workflowRunId run id for the Workflow * @return the map of node id to the {@link WorkflowNodeStateDetail} * @throws IOException if the error occurred during executing the http request * @throws UnauthenticatedException if the request is not authorized successfully in the gateway server * @throws NotFoundException if the workflow with given runid not found */ public Map<String, WorkflowNodeStateDetail> getWorkflowNodeStates(ProgramRunId workflowRunId) throws IOException, UnauthenticatedException, NotFoundException { String path = String.format("apps/%s/workflows/%s/runs/%s/nodes/state", workflowRunId.getApplication(), workflowRunId.getProgram(), workflowRunId.getRun()); NamespaceId namespaceId = workflowRunId.getParent().getNamespaceId(); URL urlPath = config.resolveNamespacedURLV3(namespaceId.toId(), path); HttpResponse response = restClient.execute(HttpMethod.GET, urlPath, config.getAccessToken(), HttpURLConnection.HTTP_NOT_FOUND); if (response.getResponseCode() == HttpURLConnection.HTTP_NOT_FOUND) { throw new NotFoundException(workflowRunId); } return ObjectResponse.fromJsonBody(response, new TypeToken<Map<String, WorkflowNodeStateDetail>>() { }).getResponseObject(); } }