/*
 *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 *  Modifications copyright (C) 2017 Uber Technologies, Inc.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"). You may not
 *  use this file except in compliance with the License. A copy of the License is
 *  located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 *  or in the "license" file accompanying this file. This file 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 com.uber.cadence.serviceclient;

import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import com.uber.cadence.*;
import com.uber.cadence.internal.compatibility.proto.mappers.*;
import com.uber.cadence.internal.compatibility.proto.serviceclient.IGrpcServiceStubs;
import io.grpc.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

public class WorkflowServiceGrpc implements IWorkflowService {

  private final IGrpcServiceStubs grpcServiceStubs;
  private final Executor executor = MoreExecutors.directExecutor();

  public WorkflowServiceGrpc(ClientOptions options) {
    this.grpcServiceStubs = IGrpcServiceStubs.newInstance(options);
  }

  @Override
  public void close() {
    grpcServiceStubs.shutdown();
  }

  @Override
  public ClientOptions getOptions() {
    return grpcServiceStubs.getOptions();
  }

  @Override
  public CompletableFuture<Boolean> isHealthy() {
    CompletableFuture<Boolean> completableFuture = new CompletableFuture<>();
    Futures.addCallback(
        grpcServiceStubs
            .metaFutureStub()
            .health(com.uber.cadence.api.v1.HealthRequest.getDefaultInstance()),
        new FutureCallback<com.uber.cadence.api.v1.HealthResponse>() {
          @Override
          public void onSuccess(com.uber.cadence.api.v1.HealthResponse response) {
            completableFuture.complete(response.getOk());
          }

          @Override
          public void onFailure(Throwable throwable) {
            completableFuture.completeExceptionally(toServiceClientException(throwable));
          }
        },
        executor);
    return completableFuture;
  }

  @Override
  public void StartWorkflowExecutionWithTimeout(
      StartWorkflowExecutionRequest startRequest,
      AsyncMethodCallback<StartWorkflowExecutionResponse> resultHandler,
      Long timeoutInMillis)
      throws CadenceError {
    Futures.addCallback(
        grpcServiceStubs
            .workflowFutureStub()
            .withDeadlineAfter(timeoutInMillis, TimeUnit.MILLISECONDS)
            .startWorkflowExecution(RequestMapper.startWorkflowExecutionRequest(startRequest)),
        toFutureCallback(resultHandler, ResponseMapper::startWorkflowExecutionResponse),
        executor);
  }

  @Override
  public void StartWorkflowExecutionAsyncWithTimeout(
      StartWorkflowExecutionAsyncRequest startAsyncRequest,
      AsyncMethodCallback<StartWorkflowExecutionAsyncResponse> resultHandler,
      Long timeoutInMillis)
      throws CadenceError {
    Futures.addCallback(
        grpcServiceStubs
            .workflowFutureStub()
            .withDeadlineAfter(timeoutInMillis, TimeUnit.MILLISECONDS)
            .startWorkflowExecutionAsync(
                RequestMapper.startWorkflowExecutionAsyncRequest(startAsyncRequest)),
        toFutureCallback(resultHandler, ResponseMapper::startWorkflowExecutionAsyncResponse),
        executor);
  }

  @Override
  public GetWorkflowExecutionHistoryResponse GetWorkflowExecutionHistoryWithTimeout(
      GetWorkflowExecutionHistoryRequest getRequest, Long timeoutInMillis) throws CadenceError {
    try {
      return ResponseMapper.getWorkflowExecutionHistoryResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .withDeadlineAfter(timeoutInMillis, TimeUnit.MILLISECONDS)
              .getWorkflowExecutionHistory(
                  RequestMapper.getWorkflowExecutionHistoryRequest(getRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void GetWorkflowExecutionHistoryWithTimeout(
      GetWorkflowExecutionHistoryRequest getRequest,
      AsyncMethodCallback<GetWorkflowExecutionHistoryResponse> resultHandler,
      Long timeoutInMillis)
      throws CadenceError {
    Futures.addCallback(
        grpcServiceStubs
            .workflowFutureStub()
            .withDeadlineAfter(timeoutInMillis, TimeUnit.MILLISECONDS)
            .getWorkflowExecutionHistory(
                RequestMapper.getWorkflowExecutionHistoryRequest(getRequest)),
        toFutureCallback(resultHandler, ResponseMapper::getWorkflowExecutionHistoryResponse),
        executor);
  }

  @Override
  public void SignalWorkflowExecutionWithTimeout(
      SignalWorkflowExecutionRequest signalRequest,
      AsyncMethodCallback<Void> resultHandler,
      Long timeoutInMillis)
      throws CadenceError {
    Futures.addCallback(
        grpcServiceStubs
            .workflowFutureStub()
            .withDeadlineAfter(timeoutInMillis, TimeUnit.MILLISECONDS)
            .signalWorkflowExecution(RequestMapper.signalWorkflowExecutionRequest(signalRequest)),
        toFutureCallback(resultHandler, r -> null),
        executor);
  }

  @Override
  public void RegisterDomain(RegisterDomainRequest registerRequest)
      throws BadRequestError, DomainAlreadyExistsError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      grpcServiceStubs
          .domainBlockingStub()
          .registerDomain(RequestMapper.registerDomainRequest(registerRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public DescribeDomainResponse DescribeDomain(DescribeDomainRequest describeRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.describeDomainResponse(
          grpcServiceStubs
              .domainBlockingStub()
              .describeDomain(RequestMapper.describeDomainRequest(describeRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public DiagnoseWorkflowExecutionResponse DiagnoseWorkflowExecution(
      DiagnoseWorkflowExecutionRequest diagnoseRequest)
      throws DomainNotActiveError, ServiceBusyError, EntityNotExistsError,
          ClientVersionNotSupportedError, CadenceError {
    throw new UnsupportedOperationException("DiagnoseWorkflowExecution is not implemented");
  }

  @Override
  public ListDomainsResponse ListDomains(ListDomainsRequest listRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.listDomainsResponse(
          grpcServiceStubs
              .domainBlockingStub()
              .listDomains(RequestMapper.listDomainsRequest(listRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public UpdateDomainResponse UpdateDomain(UpdateDomainRequest updateRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError, DomainNotActiveError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.updateDomainResponse(
          grpcServiceStubs
              .domainBlockingStub()
              .updateDomain(RequestMapper.updateDomainRequest(updateRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void DeprecateDomain(DeprecateDomainRequest deprecateRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError, DomainNotActiveError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      grpcServiceStubs
          .domainBlockingStub()
          .deprecateDomain(RequestMapper.deprecateDomainRequest(deprecateRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public RestartWorkflowExecutionResponse RestartWorkflowExecution(
      RestartWorkflowExecutionRequest restartRequest)
      throws BadRequestError, ServiceBusyError, DomainNotActiveError, LimitExceededError,
          EntityNotExistsError, ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.restartWorkflowExecutionResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .restartWorkflowExecution(
                  RequestMapper.restartWorkflowExecutionRequest(restartRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public StartWorkflowExecutionResponse StartWorkflowExecution(
      StartWorkflowExecutionRequest startRequest)
      throws BadRequestError, WorkflowExecutionAlreadyStartedError, ServiceBusyError,
          DomainNotActiveError, LimitExceededError, EntityNotExistsError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.startWorkflowExecutionResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .startWorkflowExecution(RequestMapper.startWorkflowExecutionRequest(startRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public StartWorkflowExecutionAsyncResponse StartWorkflowExecutionAsync(
      StartWorkflowExecutionAsyncRequest startRequest)
      throws BadRequestError, WorkflowExecutionAlreadyStartedError, ServiceBusyError,
          DomainNotActiveError, LimitExceededError, EntityNotExistsError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.startWorkflowExecutionAsyncResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .startWorkflowExecutionAsync(
                  RequestMapper.startWorkflowExecutionAsyncRequest(startRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public GetWorkflowExecutionHistoryResponse GetWorkflowExecutionHistory(
      GetWorkflowExecutionHistoryRequest getRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.getWorkflowExecutionHistoryResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .getWorkflowExecutionHistory(
                  RequestMapper.getWorkflowExecutionHistoryRequest(getRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public PollForDecisionTaskResponse PollForDecisionTask(PollForDecisionTaskRequest pollRequest)
      throws BadRequestError, ServiceBusyError, LimitExceededError, EntityNotExistsError,
          DomainNotActiveError, ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.pollForDecisionTaskResponse(
          grpcServiceStubs
              .workerBlockingStub()
              .pollForDecisionTask(RequestMapper.pollForDecisionTaskRequest(pollRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public RespondDecisionTaskCompletedResponse RespondDecisionTaskCompleted(
      RespondDecisionTaskCompletedRequest completeRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      return ResponseMapper.respondDecisionTaskCompletedResponse(
          grpcServiceStubs
              .workerBlockingStub()
              .respondDecisionTaskCompleted(
                  RequestMapper.respondDecisionTaskCompletedRequest(completeRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RespondDecisionTaskFailed(RespondDecisionTaskFailedRequest failedRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      grpcServiceStubs
          .workerBlockingStub()
          .respondDecisionTaskFailed(RequestMapper.respondDecisionTaskFailedRequest(failedRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public PollForActivityTaskResponse PollForActivityTask(PollForActivityTaskRequest pollRequest)
      throws BadRequestError, ServiceBusyError, LimitExceededError, EntityNotExistsError,
          DomainNotActiveError, ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.pollForActivityTaskResponse(
          grpcServiceStubs
              .workerBlockingStub()
              .pollForActivityTask(RequestMapper.pollForActivityTaskRequest(pollRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public RecordActivityTaskHeartbeatResponse RecordActivityTaskHeartbeat(
      RecordActivityTaskHeartbeatRequest heartbeatRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      return ResponseMapper.recordActivityTaskHeartbeatResponse(
          grpcServiceStubs
              .workerBlockingStub()
              .recordActivityTaskHeartbeat(
                  RequestMapper.recordActivityTaskHeartbeatRequest(heartbeatRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public RecordActivityTaskHeartbeatResponse RecordActivityTaskHeartbeatByID(
      RecordActivityTaskHeartbeatByIDRequest heartbeatRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      return ResponseMapper.recordActivityTaskHeartbeatResponse(
          grpcServiceStubs
              .workerBlockingStub()
              .recordActivityTaskHeartbeatByID(
                  RequestMapper.recordActivityTaskHeartbeatByIDRequest(heartbeatRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RespondActivityTaskCompleted(RespondActivityTaskCompletedRequest completeRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      grpcServiceStubs
          .workerBlockingStub()
          .respondActivityTaskCompleted(
              RequestMapper.respondActivityTaskCompletedRequest(completeRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RespondActivityTaskCompletedByID(
      RespondActivityTaskCompletedByIDRequest completeRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      grpcServiceStubs
          .workerBlockingStub()
          .respondActivityTaskCompletedByID(
              RequestMapper.respondActivityTaskCompletedByIDRequest(completeRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RespondActivityTaskFailed(RespondActivityTaskFailedRequest failRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      grpcServiceStubs
          .workerBlockingStub()
          .respondActivityTaskFailed(RequestMapper.respondActivityTaskFailedRequest(failRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RespondActivityTaskFailedByID(RespondActivityTaskFailedByIDRequest failRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      grpcServiceStubs
          .workerBlockingStub()
          .respondActivityTaskFailedByID(
              RequestMapper.respondActivityTaskFailedByIDRequest(failRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RespondActivityTaskCanceled(RespondActivityTaskCanceledRequest canceledRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      grpcServiceStubs
          .workerBlockingStub()
          .respondActivityTaskCanceled(
              RequestMapper.respondActivityTaskCanceledRequest(canceledRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RespondActivityTaskCanceledByID(
      RespondActivityTaskCanceledByIDRequest canceledRequest)
      throws BadRequestError, EntityNotExistsError, DomainNotActiveError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError,
          CadenceError {
    try {
      grpcServiceStubs
          .workerBlockingStub()
          .respondActivityTaskCanceledByID(
              RequestMapper.respondActivityTaskCanceledByIDRequest(canceledRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RequestCancelWorkflowExecution(RequestCancelWorkflowExecutionRequest cancelRequest)
      throws BadRequestError, EntityNotExistsError, CancellationAlreadyRequestedError,
          ServiceBusyError, DomainNotActiveError, LimitExceededError,
          ClientVersionNotSupportedError, WorkflowExecutionAlreadyCompletedError, CadenceError {
    try {
      grpcServiceStubs
          .workflowBlockingStub()
          .requestCancelWorkflowExecution(
              RequestMapper.requestCancelWorkflowExecutionRequest(cancelRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void SignalWorkflowExecution(SignalWorkflowExecutionRequest signalRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError, DomainNotActiveError,
          LimitExceededError, ClientVersionNotSupportedError,
          WorkflowExecutionAlreadyCompletedError, CadenceError {
    try {
      grpcServiceStubs
          .workflowBlockingStub()
          .signalWorkflowExecution(RequestMapper.signalWorkflowExecutionRequest(signalRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public StartWorkflowExecutionResponse SignalWithStartWorkflowExecution(
      SignalWithStartWorkflowExecutionRequest signalWithStartRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError, DomainNotActiveError,
          LimitExceededError, WorkflowExecutionAlreadyStartedError, ClientVersionNotSupportedError,
          CadenceError {
    try {
      return ResponseMapper.signalWithStartWorkflowExecutionResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .signalWithStartWorkflowExecution(
                  RequestMapper.signalWithStartWorkflowExecutionRequest(signalWithStartRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public SignalWithStartWorkflowExecutionAsyncResponse SignalWithStartWorkflowExecutionAsync(
      SignalWithStartWorkflowExecutionAsyncRequest signalWithStartRequest)
      throws BadRequestError, WorkflowExecutionAlreadyStartedError, ServiceBusyError,
          DomainNotActiveError, LimitExceededError, EntityNotExistsError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.signalWithStartWorkflowExecutionAsyncResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .signalWithStartWorkflowExecutionAsync(
                  RequestMapper.signalWithStartWorkflowExecutionAsyncRequest(
                      signalWithStartRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public ResetWorkflowExecutionResponse ResetWorkflowExecution(
      ResetWorkflowExecutionRequest resetRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError, DomainNotActiveError,
          LimitExceededError, ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.resetWorkflowExecutionResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .resetWorkflowExecution(RequestMapper.resetWorkflowExecutionRequest(resetRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void TerminateWorkflowExecution(TerminateWorkflowExecutionRequest terminateRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError, DomainNotActiveError,
          LimitExceededError, ClientVersionNotSupportedError,
          WorkflowExecutionAlreadyCompletedError, CadenceError {
    try {
      grpcServiceStubs
          .workflowBlockingStub()
          .terminateWorkflowExecution(
              RequestMapper.terminateWorkflowExecutionRequest(terminateRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public ListOpenWorkflowExecutionsResponse ListOpenWorkflowExecutions(
      ListOpenWorkflowExecutionsRequest listRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError, LimitExceededError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.listOpenWorkflowExecutionsResponse(
          grpcServiceStubs
              .visibilityBlockingStub()
              .listOpenWorkflowExecutions(
                  RequestMapper.listOpenWorkflowExecutionsRequest(listRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public ListClosedWorkflowExecutionsResponse ListClosedWorkflowExecutions(
      ListClosedWorkflowExecutionsRequest listRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.listClosedWorkflowExecutionsResponse(
          grpcServiceStubs
              .visibilityBlockingStub()
              .listClosedWorkflowExecutions(
                  RequestMapper.listClosedWorkflowExecutionsRequest(listRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public ListWorkflowExecutionsResponse ListWorkflowExecutions(
      ListWorkflowExecutionsRequest listRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.listWorkflowExecutionsResponse(
          grpcServiceStubs
              .visibilityBlockingStub()
              .listWorkflowExecutions(RequestMapper.listWorkflowExecutionsRequest(listRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public ListArchivedWorkflowExecutionsResponse ListArchivedWorkflowExecutions(
      ListArchivedWorkflowExecutionsRequest listRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.listArchivedWorkflowExecutionsResponse(
          grpcServiceStubs
              .visibilityBlockingStub()
              .listArchivedWorkflowExecutions(
                  RequestMapper.listArchivedWorkflowExecutionsRequest(listRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public ListWorkflowExecutionsResponse ScanWorkflowExecutions(
      ListWorkflowExecutionsRequest listRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.scanWorkflowExecutionsResponse(
          grpcServiceStubs
              .visibilityBlockingStub()
              .scanWorkflowExecutions(RequestMapper.scanWorkflowExecutionsRequest(listRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public CountWorkflowExecutionsResponse CountWorkflowExecutions(
      CountWorkflowExecutionsRequest countRequest)
      throws BadRequestError, EntityNotExistsError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.countWorkflowExecutionsResponse(
          grpcServiceStubs
              .visibilityBlockingStub()
              .countWorkflowExecutions(RequestMapper.countWorkflowExecutionsRequest(countRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public GetSearchAttributesResponse GetSearchAttributes()
      throws ServiceBusyError, ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.getSearchAttributesResponse(
          grpcServiceStubs
              .visibilityBlockingStub()
              .getSearchAttributes(
                  com.uber.cadence.api.v1.GetSearchAttributesRequest.newBuilder().build()));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RespondQueryTaskCompleted(RespondQueryTaskCompletedRequest completeRequest)
      throws BadRequestError, EntityNotExistsError, LimitExceededError, ServiceBusyError,
          DomainNotActiveError, ClientVersionNotSupportedError, CadenceError {
    try {
      grpcServiceStubs
          .workerBlockingStub()
          .respondQueryTaskCompleted(
              RequestMapper.respondQueryTaskCompletedRequest(completeRequest));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public ResetStickyTaskListResponse ResetStickyTaskList(ResetStickyTaskListRequest resetRequest)
      throws BadRequestError, EntityNotExistsError, LimitExceededError, ServiceBusyError,
          DomainNotActiveError, ClientVersionNotSupportedError,
          WorkflowExecutionAlreadyCompletedError, CadenceError {
    try {
      return ResponseMapper.resetStickyTaskListResponse(
          grpcServiceStubs
              .workerBlockingStub()
              .resetStickyTaskList(RequestMapper.resetStickyTaskListRequest(resetRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public QueryWorkflowResponse QueryWorkflow(QueryWorkflowRequest queryRequest)
      throws BadRequestError, EntityNotExistsError, QueryFailedError, LimitExceededError,
          ServiceBusyError, ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.queryWorkflowResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .queryWorkflow(RequestMapper.queryWorkflowRequest(queryRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public DescribeWorkflowExecutionResponse DescribeWorkflowExecution(
      DescribeWorkflowExecutionRequest describeRequest)
      throws BadRequestError, EntityNotExistsError, LimitExceededError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.describeWorkflowExecutionResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .describeWorkflowExecution(
                  RequestMapper.describeWorkflowExecutionRequest(describeRequest)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public DescribeTaskListResponse DescribeTaskList(DescribeTaskListRequest request)
      throws BadRequestError, EntityNotExistsError, LimitExceededError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.describeTaskListResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .describeTaskList(RequestMapper.describeTaskListRequest(request)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public ClusterInfo GetClusterInfo() throws InternalServiceError, ServiceBusyError, CadenceError {
    try {
      return ResponseMapper.clusterInfoResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .getClusterInfo(com.uber.cadence.api.v1.GetClusterInfoRequest.getDefaultInstance()));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public GetTaskListsByDomainResponse GetTaskListsByDomain(GetTaskListsByDomainRequest request)
      throws BadRequestError, EntityNotExistsError, LimitExceededError, ServiceBusyError,
          ClientVersionNotSupportedError, CadenceError {
    try {
      return ResponseMapper.getTaskListsByDomainResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .getTaskListsByDomain(RequestMapper.getTaskListsByDomainRequest(request)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public ListTaskListPartitionsResponse ListTaskListPartitions(
      ListTaskListPartitionsRequest request)
      throws BadRequestError, EntityNotExistsError, LimitExceededError, ServiceBusyError,
          CadenceError {
    try {
      return ResponseMapper.listTaskListPartitionsResponse(
          grpcServiceStubs
              .workflowBlockingStub()
              .listTaskListPartitions(RequestMapper.listTaskListPartitionsRequest(request)));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RefreshWorkflowTasks(RefreshWorkflowTasksRequest request)
      throws BadRequestError, DomainNotActiveError, ServiceBusyError, EntityNotExistsError,
          CadenceError {
    try {
      grpcServiceStubs
          .workflowBlockingStub()
          .refreshWorkflowTasks(RequestMapper.refreshWorkflowTasksRequest(request));
    } catch (Exception e) {
      throw toServiceClientException(e);
    }
  }

  @Override
  public void RegisterDomain(
      RegisterDomainRequest registerRequest, AsyncMethodCallback<Void> resultHandler)
      throws CadenceError {
    Futures.addCallback(
        grpcServiceStubs
            .domainFutureStub()
            .registerDomain(RequestMapper.registerDomainRequest(registerRequest)),
        toFutureCallback(resultHandler, r -> null),
        executor);
  }

  @Override
  public void DescribeDomain(
      DescribeDomainRequest describeRequest,
      AsyncMethodCallback<DescribeDomainResponse> resultHandler)
      throws CadenceError {
    Futures.addCallback(
        grpcServiceStubs
            .domainFutureStub()
            .describeDomain(RequestMapper.describeDomainRequest(describeRequest)),
        toFutureCallback(resultHandler, ResponseMapper::describeDomainResponse),
        executor);
  }

  @Override
  public void ListDomains(
      ListDomainsRequest listRequest, AsyncMethodCallback<ListDomainsResponse> resultHandler)
      throws CadenceError {
    Futures.addCallback(
        grpcServiceStubs
            .domainFutureStub()
            .listDomains(RequestMapper.listDomainsRequest(listRequest)),
        toFutureCallback(resultHandler, ResponseMapper::listDomainsResponse),
        executor);
  }

  @Override
  public void UpdateDomain(
      UpdateDomainRequest updateRequest, AsyncMethodCallback<UpdateDomainResponse> resultHandler)
      throws CadenceError {
    Futures.addCallback(
