From 547fbfdcb13931b36f61c9f1356ec2244e62c5d5 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Mon, 31 Oct 2016 10:07:02 -0700 Subject: [PATCH 01/24] DO NOT MERGE: LRO sample --- src/OperationResponse.php | 103 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 src/OperationResponse.php diff --git a/src/OperationResponse.php b/src/OperationResponse.php new file mode 100644 index 000000000..cfbf96b08 --- /dev/null +++ b/src/OperationResponse.php @@ -0,0 +1,103 @@ +longRunningRpc(); +// ... do stuff ... +$op->pollUntilComplete(); +$result = $op->getResult(); + +// Save handle and resume +$opName = $sampleApi->longRunningRpc()->getName(); +// ... do stuff ... +$op = $sampleApi->getOperationsApi()->getOperation($opName); +$op->pollUntilComplete(); +$result = $op->getResult(); + +// Polling loop +$op = $sampleApi->longRunningRpc(); +while (!$op->isDone()) { + // ... do stuff ... + $op->refresh(); +} +$result = $op->getResult(); + +// Polling loop with promise +$op = $sampleApi->longRunningRpc(); +$op->promise()->then($funcToHandleResponse, $funcToHandleError); +while (true) { + // ... do stuff ... + $op->refresh(); +} + +class OperationResponse +{ + private $operationProto; + private $operationsApi; + + // OPTIONAL: support promises using a deferred object using + // https://github.com/reactphp/promise#deferred or similar + private $deferred; + + public function isDone() + { + return $this->operationProto->getDone(); + } + + public function getName() + { + return $this->operationProto->getName(); + } + + public function pollUntilComplete($pollSettings = []) + { + while (!$this->isDone()) { + // TODO: use poll settings + sleep(1); + $this->refresh(); + } + } + + // OPTIONAL: provide a promise (using https://github.com/reactphp/promise) + public function promise() { + return $this->deferred->promise(); + } + + public function refresh($resolvePromiseOnComplete = true) + { + $name = $this->getName(); + $this->operationsProto = $this->operationsApi->getOperation($name); + + // OPTIONAL: resolve promise + if ($resolvePromiseOnComplete && $this->isDone()) { + if ($this->operationsProto->hasError()) { + $this->deferred->reject($this->operationsProto->getError()); + } elseif ($this->operationsProto->hasResponse()) { + $this->deferred->resolve($this->operationsProto->getResponse()); + } else { + throw new Exception("this should never happen"); + } + } + } + + public function getResult() + { + if (!$this->isDone()) { + return null; + } + if ($this->operationsProto->hasError()) { + // TODO: throw detailed exception + $error = $this->operationsProto->getError(); + throw new Exception("$error"); + } + if ($this->opertationsProto->hasResponse()) { + // TODO: implement unpacking + return unpack_response($this->operationsProto->getResponse()); + } + throw new Exception("this should never happen..."); + } + + public function cancel() + { + $this->operationsApi->cancelOperation($this->getName()); + } +} From fa54b7cafda665f532be46b670a1d28295345515 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Mon, 31 Oct 2016 11:40:25 -0700 Subject: [PATCH 02/24] Update with getProtoResponse and other samples --- src/OperationResponse.php | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/OperationResponse.php b/src/OperationResponse.php index cfbf96b08..6d42f4ebd 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -29,6 +29,31 @@ $op->refresh(); } +// Sample using operation proto object +$op = $sampleApi->longRunningRpc(); +// ... do stuff ... +$op->pollUntilComplete(); +$opProto = $op->getProtoResponse(); +// ... use opProto ... + + +////////////////////////////////////////////////////////////////// +// Samples when returning an array with flag for checking success +list($success, $response) = $sampleApi->longRunningRpc()->pollUntilComplete(); +if ($success) { + // handle success +} else { + // handle failure +} + +list($statusCode, $response) = $sampleApi->longRunningRpc()->pollUntilComplete(); +if ($statusCode == Google\Rpc\Code::OK) { + // handle success +} else { + // handle failure +} + + class OperationResponse { private $operationProto; @@ -96,6 +121,11 @@ public function getResult() throw new Exception("this should never happen..."); } + public function getProtoResponse() + { + return $this->operationProto; + } + public function cancel() { $this->operationsApi->cancelOperation($this->getName()); From 4f66cb4edfb3db8b4c09bd7b440d3e3ec6470421 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 15 Nov 2016 16:48:15 -0800 Subject: [PATCH 03/24] LRO updates Generate operations API, update operations wrapper Update operation and callable --- LroSample.php | 16 + composer.json | 3 +- src/ApiCallable.php | 14 + src/OperationResponse.php | 140 ++--- src/longrunning/OperationsApi.php | 496 ++++++++++++++++++ .../resources/operations_client_config.json | 48 ++ 6 files changed, 629 insertions(+), 88 deletions(-) create mode 100644 LroSample.php create mode 100644 src/longrunning/OperationsApi.php create mode 100644 src/longrunning/resources/operations_client_config.json diff --git a/LroSample.php b/LroSample.php new file mode 100644 index 000000000..862e3f339 --- /dev/null +++ b/LroSample.php @@ -0,0 +1,16 @@ +asyncRecognize($data); +$result = $op->wait(); + +$op-> + +$op = $api->asyncRecognize($data); +// Do work... +while (!$op->isComplete()) { + $result = $op->wait(); +} diff --git a/composer.json b/composer.json index 390d3f1ba..62069bfed 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,8 @@ }, "autoload": { "psr-4": { - "Google\\GAX\\":"src/" + "Google\\GAX\\":"src/", + "Google\\Longrunning\\":"src/longrunning/" }, "classmap": ["src/generated", "src/generated/well-known-types"] } diff --git a/src/ApiCallable.php b/src/ApiCallable.php index 36ca8187c..7bf8f019f 100644 --- a/src/ApiCallable.php +++ b/src/ApiCallable.php @@ -120,6 +120,16 @@ private static function setPageStreaming($callable, $pageStreamingDescriptor) return $inner; } + private static function setLongRunnning($callable, $longRunningDescriptor) + { + $inner = function () use ($callable, $longRunningDescriptor) { + $response = call_user_func_array($callable, func_get_args()); + $name = $response->getName(); + return new OperationResponse($response->getName(), $longRunningDescriptor, $response); + }; + return $inner; + } + private static function setCustomHeader($callable, $headerDescriptor) { $inner = function () use ($callable, $headerDescriptor) { @@ -176,6 +186,10 @@ public static function createApiCall($stub, $methodName, CallSettings $settings, $apiCall = self::setPageStreaming($apiCall, $options['pageStreamingDescriptor']); } + if (array_key_exists('longRunningDescriptor', $options)) { + $apiCall = self::setLongRunnning($apiCall, $options['longRunningDescriptor']); + } + if (array_key_exists('headerDescriptor', $options)) { $apiCall = self::setCustomHeader($apiCall, $options['headerDescriptor']); } diff --git a/src/OperationResponse.php b/src/OperationResponse.php index 6d42f4ebd..148be0ef0 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -1,129 +1,95 @@ longRunningRpc(); -// ... do stuff ... -$op->pollUntilComplete(); -$result = $op->getResult(); - -// Save handle and resume -$opName = $sampleApi->longRunningRpc()->getName(); -// ... do stuff ... -$op = $sampleApi->getOperationsApi()->getOperation($opName); -$op->pollUntilComplete(); -$result = $op->getResult(); - -// Polling loop -$op = $sampleApi->longRunningRpc(); -while (!$op->isDone()) { - // ... do stuff ... - $op->refresh(); -} -$result = $op->getResult(); - -// Polling loop with promise -$op = $sampleApi->longRunningRpc(); -$op->promise()->then($funcToHandleResponse, $funcToHandleError); -while (true) { - // ... do stuff ... - $op->refresh(); -} - -// Sample using operation proto object -$op = $sampleApi->longRunningRpc(); -// ... do stuff ... -$op->pollUntilComplete(); -$opProto = $op->getProtoResponse(); -// ... use opProto ... - - -////////////////////////////////////////////////////////////////// -// Samples when returning an array with flag for checking success -list($success, $response) = $sampleApi->longRunningRpc()->pollUntilComplete(); -if ($success) { - // handle success -} else { - // handle failure -} - -list($statusCode, $response) = $sampleApi->longRunningRpc()->pollUntilComplete(); -if ($statusCode == Google\Rpc\Code::OK) { - // handle success -} else { - // handle failure -} +namespace Google\GAX; +use Google\Longrunning\OperationsApi; class OperationResponse { - private $operationProto; + private $operationName; private $operationsApi; + private $operationReturnType; + private $lastProtoResponse; - // OPTIONAL: support promises using a deferred object using - // https://github.com/reactphp/promise#deferred or similar - private $deferred; + public static function resumeOperation($operationName, $serviceAddress) + { + $operationsApi = new OperationsApi(['serviceAddress' => $serviceAddress]); + $longRunningDescriptor = [ + 'operationsApi' => $operationsApi, + 'operationReturnType' => null, + ]; + return new OperationResponse($operationName, $longRunningDescriptor, null); + } + + public function __construct($operationName, $longRunningDescriptor, $lastProtoResponse = null) + { + $this->operationName = $operationName; + $this->operationsApi = $longRunningDescriptor['operationsApi']; + $this->operationReturnType = $longRunningDescriptor['operationReturnType']; + $this->lastProtoResponse = $lastProtoResponse; + } public function isDone() { - return $this->operationProto->getDone(); + return is_null($this->lastProtoResponse) + ? false + : $this->lastProtoResponse->getDone(); } public function getName() { - return $this->operationProto->getName(); + return $this->operationName; } - public function pollUntilComplete($pollSettings = []) + public function pollUntilComplete($handler = null, $pollSettings = []) { while (!$this->isDone()) { // TODO: use poll settings sleep(1); + echo "refreshing...\n"; $this->refresh(); } - } - // OPTIONAL: provide a promise (using https://github.com/reactphp/promise) - public function promise() { - return $this->deferred->promise(); + if (!is_null($handler)) { + return $handler($this); + } + + return $this->lastProtoResponse->hasResponse(); } - public function refresh($resolvePromiseOnComplete = true) + public function refresh() { $name = $this->getName(); - $this->operationsProto = $this->operationsApi->getOperation($name); - - // OPTIONAL: resolve promise - if ($resolvePromiseOnComplete && $this->isDone()) { - if ($this->operationsProto->hasError()) { - $this->deferred->reject($this->operationsProto->getError()); - } elseif ($this->operationsProto->hasResponse()) { - $this->deferred->resolve($this->operationsProto->getResponse()); - } else { - throw new Exception("this should never happen"); - } - } + $this->lastProtoResponse = $this->operationsApi->getOperation($name); } public function getResult() { - if (!$this->isDone()) { + if (!$this->isDone() || !$this->lastProtoResponse->hasResponse()) { return null; } - if ($this->operationsProto->hasError()) { - // TODO: throw detailed exception - $error = $this->operationsProto->getError(); - throw new Exception("$error"); + + $anyResponse = $this->lastProtoResponse->getResponse(); + if (is_null($this->operationReturnType)) { + return $anyResponse; } - if ($this->opertationsProto->hasResponse()) { - // TODO: implement unpacking - return unpack_response($this->operationsProto->getResponse()); + $operationReturnType = $this->operationReturnType; + $response = new $operationReturnType(); + $response->parse($anyResponse->getValue()); + return $response; + } + + public function getError() + { + if (!$this->isDone() || !$this->lastProtoResponse->hasError()) { + return null; } - throw new Exception("this should never happen..."); + return $this->lastProtoResponse->getError(); } - public function getProtoResponse() + public function getLastProtoResponse() { - return $this->operationProto; + return $this->lastProtoResponse; } public function cancel() diff --git a/src/longrunning/OperationsApi.php b/src/longrunning/OperationsApi.php new file mode 100644 index 000000000..52e598b6b --- /dev/null +++ b/src/longrunning/OperationsApi.php @@ -0,0 +1,496 @@ +getOperation($formattedName); + * } finally { + * if (isset($operationsApi)) { + * $operationsApi->close(); + * } + * } + * ``` + * + * Many parameters require resource names to be formatted in a particular way. To assist + * with these names, this class includes a format method for each type of name, and additionally + * a parse method to extract the individual identifiers contained within names that are + * returned. + */ +class OperationsApi +{ + /** + * The default address of the service. + */ + const SERVICE_ADDRESS = 'longrunning.googleapis.com'; + + /** + * The default port of the service. + */ + const DEFAULT_SERVICE_PORT = 443; + + /** + * The default timeout for non-retrying methods. + */ + const DEFAULT_TIMEOUT_MILLIS = 30000; + + const _GAX_VERSION = '0.1.0'; + const _CODEGEN_NAME = 'GAPIC'; + const _CODEGEN_VERSION = '0.0.0'; + +private static $operationPathNameTemplate; + +private $grpcCredentialsHelper; +private $operationsStub; +private $scopes; +private $defaultCallSettings; +private $descriptors; + + /** + * Formats a string containing the fully-qualified path to represent + * a operation_path resource. + */ +public static function formatOperationPathName($operationPath) +{ + return self::getOperationPathNameTemplate()->render([ + 'operation_path' => $operationPath, + ]); +} + + /** + * Parses the operation_path from the given fully-qualified path which + * represents a operation_path resource. + */ +public static function parseOperationPathFromOperationPathName($operationPathName) +{ + return self::getOperationPathNameTemplate()->match($operationPathName)['operation_path']; +} + + +private static function getOperationPathNameTemplate() +{ + if (self::$operationPathNameTemplate == null) { + self::$operationPathNameTemplate = new PathTemplate('operations/{operation_path=**}'); + } + + return self::$operationPathNameTemplate; +} + +private static function getPageStreamingDescriptors() +{ + $listOperationsPageStreamingDescriptor = + new PageStreamingDescriptor([ + 'requestPageTokenField' => 'page_token', + 'requestPageSizeField' => 'page_size', + 'responsePageTokenField' => 'next_page_token', + 'resourceField' => 'operations', + ]); + + $pageStreamingDescriptors = [ + 'listOperations' => $listOperationsPageStreamingDescriptor, + ]; + + return $pageStreamingDescriptors; +} + + // TODO(garrettjones): add channel (when supported in gRPC) + /** + * Constructor. + * + * @param array $options { + * Optional. Options for configuring the service API wrapper. + * + * @type string $serviceAddress The domain name of the API remote host. + * Default 'longrunning.googleapis.com'. + * @type mixed $port The port on which to connect to the remote host. Default 443. + * @type Grpc\ChannelCredentials $sslCreds + * A `ChannelCredentials` for use with an SSL-enabled channel. + * Default: a credentials object returned from + * Grpc\ChannelCredentials::createSsl() + * @type array $scopes A string array of scopes to use when acquiring credentials. + * Default the scopes for the Google Long Running Operations API. + * @type array $retryingOverride + * An associative array of string => RetryOptions, where the keys + * are method names (e.g. 'createFoo'), that overrides default retrying + * settings. A value of null indicates that the method in question should + * not retry. + * @type int $timeoutMillis The timeout in milliseconds to use for calls + * that don't use retries. For calls that use retries, + * set the timeout in RetryOptions. + * Default: 30000 (30 seconds) + * @type string $appName The codename of the calling service. Default 'gax'. + * @type string $appVersion The version of the calling service. + * Default: the current version of GAX. + * @type Google\Auth\CredentialsLoader $credentialsLoader + * A CredentialsLoader object created using the + * Google\Auth library. + * } + */ +public function __construct($options = []) +{ + $defaultScopes = [ + ]; + $defaultOptions = [ + 'serviceAddress' => self::SERVICE_ADDRESS, + 'port' => self::DEFAULT_SERVICE_PORT, + 'scopes' => $defaultScopes, + 'retryingOverride' => null, + 'timeoutMillis' => self::DEFAULT_TIMEOUT_MILLIS, + 'appName' => 'gax', + 'appVersion' => self::_GAX_VERSION, + 'credentialsLoader' => null, + ]; + $options = array_merge($defaultOptions, $options); + + $headerDescriptor = new AgentHeaderDescriptor([ + 'clientName' => $options['appName'], + 'clientVersion' => $options['appVersion'], + 'codeGenName' => self::_CODEGEN_NAME, + 'codeGenVersion' => self::_CODEGEN_VERSION, + 'gaxVersion' => self::_GAX_VERSION, + 'phpVersion' => phpversion(), + ]); + + $defaultDescriptors = ['headerDescriptor' => $headerDescriptor]; + $this->descriptors = [ + 'getOperation' => $defaultDescriptors, + 'listOperations' => $defaultDescriptors, + 'cancelOperation' => $defaultDescriptors, + 'deleteOperation' => $defaultDescriptors, + ]; + $pageStreamingDescriptors = self::getPageStreamingDescriptors(); + foreach ($pageStreamingDescriptors as $method => $pageStreamingDescriptor) { + $this->descriptors[$method]['pageStreamingDescriptor'] = $pageStreamingDescriptor; + } + + $clientConfigJsonString = file_get_contents(__DIR__ . '/resources/operations_client_config.json'); + $clientConfig = json_decode($clientConfigJsonString, true); + $this->defaultCallSettings = + CallSettings::load( + 'google.longrunning.Operations', + $clientConfig, + $options['retryingOverride'], + GrpcConstants::getStatusCodeNames(), + $options['timeoutMillis'] + ); + + $this->scopes = $options['scopes']; + + $createStubOptions = []; + if (!empty($options['sslCreds'])) { + $createStubOptions['sslCreds'] = $options['sslCreds']; + } + $grpcCredentialsHelperOptions = array_diff_key($options, $defaultOptions); + $this->grpcCredentialsHelper = new GrpcCredentialsHelper($this->scopes, $grpcCredentialsHelperOptions); + + $createOperationsStubFunction = function ($hostname, $opts) { + return new OperationsClient($hostname, $opts); + }; + $this->operationsStub = $this->grpcCredentialsHelper->createStub( + $createOperationsStubFunction, + $options['serviceAddress'], + $options['port'], + $createStubOptions + ); +} + + /** + * Gets the latest state of a long-running operation. Clients can use this + * method to poll the operation result at intervals as recommended by the API + * service. + * + * Sample code: + * ``` + * try { + * $operationsApi = new OperationsApi(); + * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); + * $response = $operationsApi->getOperation($formattedName); + * } finally { + * if (isset($operationsApi)) { + * $operationsApi->close(); + * } + * } + * ``` + * + * @param string $name The name of the operation resource. + * @param array $optionalArgs { + * Optional. + * @type Google\GAX\RetrySettings $retrySettings + * Retry settings to use for this call. If present, then + * $timeoutMillis is ignored. + * @type int $timeoutMillis + * Timeout to use for this call. Only used if $retrySettings + * is not set. + * } + * + * @return google\longrunning\Operation + * + * @throws Google\GAX\ApiException if the remote call fails + */ +public function getOperation($name, $optionalArgs = []) +{ + $request = new GetOperationRequest(); + $request->setName($name); + + $mergedSettings = $this->defaultCallSettings['getOperation']->merge( + new CallSettings($optionalArgs) + ); + $callable = ApiCallable::createApiCall( + $this->operationsStub, + 'GetOperation', + $mergedSettings, + $this->descriptors['getOperation'] + ); + + return $callable( + $request, + [], + ['call_credentials_callback' => $this->createCredentialsCallback()]); +} + + /** + * Lists operations that match the specified filter in the request. If the + * server doesn't support this method, it returns `UNIMPLEMENTED`. + * + * NOTE: the `name` binding below allows API services to override the binding + * to use different resource name schemes, such as `users/* /operations`. + * + * Sample code: + * ``` + * try { + * $operationsApi = new OperationsApi(); + * $name = ""; + * $filter = ""; + * foreach ($operationsApi->listOperations($name, $filter) as $element) { + * // doThingsWith(element); + * + } + * + } finally { + * if (isset($operationsApi)) { + * $operationsApi->close(); + * + } + * + } + * ``` + * + * @param string $name The name of the operation collection. + * @param string $filter The standard list filter. + * @param array $optionalArgs { + * Optional. + * @type int $pageSize + * The maximum number of resources contained in the underlying API + * response. The API may return fewer values in a page, even if + * there are additional values to be retrieved. + * @type string $pageToken + * A page token is used to specify a page of values to be returned. + * If no page token is specified (the default), the first page + * of values will be returned. Any page token used here must have + * been generated by a previous call to the API. + * @type Google\GAX\RetrySettings $retrySettings + * Retry settings to use for this call. If present, then + * $timeoutMillis is ignored. + * @type int $timeoutMillis + * Timeout to use for this call. Only used if $retrySettings + * is not set. + * } + * + * @return Google\GAX\PagedListResponse + * + * @throws Google\GAX\ApiException if the remote call fails + */ + public function listOperations($name, $filter, $optionalArgs = []) + { + $request = new ListOperationsRequest(); + $request->setName($name); + $request->setFilter($filter); + if (isset($optionalArgs['pageSize'])) { + $request->setPageSize($optionalArgs['pageSize']); + } + if (isset($optionalArgs['pageToken'])) { + $request->setPageToken($optionalArgs['pageToken']); + } + + $mergedSettings = $this->defaultCallSettings['listOperations']->merge( + new CallSettings($optionalArgs)); + $callable = ApiCallable::createApiCall( + $this->operationsStub, 'ListOperations', $mergedSettings, $this->descriptors['listOperations']); + + return $callable( + $request, + [], + ['call_credentials_callback' => $this->createCredentialsCallback()]); + } + + /** + * Starts asynchronous cancellation on a long-running operation. The server + * makes a best effort to cancel the operation, but success is not + * guaranteed. If the server doesn't support this method, it returns + * `google.rpc.Code.UNIMPLEMENTED`. Clients can use + * [Operations.GetOperation][google.longrunning.Operations.GetOperation] or + * other methods to check whether the cancellation succeeded or whether the + * operation completed despite cancellation. On successful cancellation, + * the operation is not deleted; instead, it becomes an operation with + * an [Operation.error][google.longrunning.Operation.error] value with a [google.rpc.Status.code][google.rpc.Status.code] of 1, + * corresponding to `Code.CANCELLED`. + * + * Sample code: + * ``` + * try { + * $operationsApi = new OperationsApi(); + * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); + * $operationsApi->cancelOperation($formattedName); + * + } finally { + * if (isset($operationsApi)) { + * $operationsApi->close(); + * + } + * + } + * ``` + * + * @param string $name The name of the operation resource to be cancelled. + * @param array $optionalArgs { + * Optional. + * @type Google\GAX\RetrySettings $retrySettings + * Retry settings to use for this call. If present, then + * $timeoutMillis is ignored. + * @type int $timeoutMillis + * Timeout to use for this call. Only used if $retrySettings + * is not set. + * } + * + * @throws Google\GAX\ApiException if the remote call fails + */ + public function cancelOperation($name, $optionalArgs = []) + { + $request = new CancelOperationRequest(); + $request->setName($name); + + $mergedSettings = $this->defaultCallSettings['cancelOperation']->merge( + new CallSettings($optionalArgs)); + $callable = ApiCallable::createApiCall( + $this->operationsStub, 'CancelOperation', $mergedSettings, $this->descriptors['cancelOperation']); + + return $callable( + $request, + [], + ['call_credentials_callback' => $this->createCredentialsCallback()]); + } + + /** + * Deletes a long-running operation. This method indicates that the client is + * no longer interested in the operation result. It does not cancel the + * operation. If the server doesn't support this method, it returns + * `google.rpc.Code.UNIMPLEMENTED`. + * + * Sample code: + * ``` + * try { + * $operationsApi = new OperationsApi(); + * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); + * $operationsApi->deleteOperation($formattedName); + * + } finally { + * if (isset($operationsApi)) { + * $operationsApi->close(); + * + } + * + } + * ``` + * + * @param string $name The name of the operation resource to be deleted. + * @param array $optionalArgs { + * Optional. + * @type Google\GAX\RetrySettings $retrySettings + * Retry settings to use for this call. If present, then + * $timeoutMillis is ignored. + * @type int $timeoutMillis + * Timeout to use for this call. Only used if $retrySettings + * is not set. + * } + * + * @throws Google\GAX\ApiException if the remote call fails + */ + public function deleteOperation($name, $optionalArgs = []) + { + $request = new DeleteOperationRequest(); + $request->setName($name); + + $mergedSettings = $this->defaultCallSettings['deleteOperation']->merge( + new CallSettings($optionalArgs)); + $callable = ApiCallable::createApiCall( + $this->operationsStub, 'DeleteOperation', $mergedSettings, $this->descriptors['deleteOperation']); + + return $callable( + $request, + [], + ['call_credentials_callback' => $this->createCredentialsCallback()]); + } + + /** + * Initiates an orderly shutdown in which preexisting calls continue but new + * calls are immediately cancelled. + */ + public function close() + { + $this->operationsStub->close(); + } + + private function createCredentialsCallback() + { + return $this->grpcCredentialsHelper->createCallCredentialsCallback(); + } + } diff --git a/src/longrunning/resources/operations_client_config.json b/src/longrunning/resources/operations_client_config.json new file mode 100644 index 000000000..86873bc95 --- /dev/null +++ b/src/longrunning/resources/operations_client_config.json @@ -0,0 +1,48 @@ +{ + "interfaces": { + "google.longrunning.Operations": { + "retry_codes": { + "retry_codes_def": { + "idempotent": [ + "DEADLINE_EXCEEDED", + "UNAVAILABLE" + ], + "non_idempotent": [] + } + }, + "retry_params": { + "default": { + "initial_retry_delay_millis": 100, + "retry_delay_multiplier": 1.3, + "max_retry_delay_millis": 60000, + "initial_rpc_timeout_millis": 20000, + "rpc_timeout_multiplier": 1.0, + "max_rpc_timeout_millis": 20000, + "total_timeout_millis": 600000 + } + }, + "methods": { + "GetOperation": { + "timeout_millis": 60000, + "retry_codes_name": "idempotent", + "retry_params_name": "default" + }, + "ListOperations": { + "timeout_millis": 60000, + "retry_codes_name": "idempotent", + "retry_params_name": "default" + }, + "CancelOperation": { + "timeout_millis": 60000, + "retry_codes_name": "idempotent", + "retry_params_name": "default" + }, + "DeleteOperation": { + "timeout_millis": 60000, + "retry_codes_name": "idempotent", + "retry_params_name": "default" + } + } + } + } +} From 8c3e427484f279e26862c2cf28a1fec86b710d9d Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Mon, 21 Nov 2016 14:46:53 -0800 Subject: [PATCH 04/24] remove lro sample --- LroSample.php | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 LroSample.php diff --git a/LroSample.php b/LroSample.php deleted file mode 100644 index 862e3f339..000000000 --- a/LroSample.php +++ /dev/null @@ -1,16 +0,0 @@ -asyncRecognize($data); -$result = $op->wait(); - -$op-> - -$op = $api->asyncRecognize($data); -// Do work... -while (!$op->isComplete()) { - $result = $op->wait(); -} From fa113f87048b5fd3a38e9d6edcfac9f5a5194240 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Mon, 21 Nov 2016 17:12:38 -0800 Subject: [PATCH 05/24] Regenerate operations API --- src/longrunning/OperationsApi.php | 576 +++++++++++++++--------------- 1 file changed, 292 insertions(+), 284 deletions(-) diff --git a/src/longrunning/OperationsApi.php b/src/longrunning/OperationsApi.php index 52e598b6b..e54640181 100644 --- a/src/longrunning/OperationsApi.php +++ b/src/longrunning/OperationsApi.php @@ -87,67 +87,66 @@ class OperationsApi const _CODEGEN_NAME = 'GAPIC'; const _CODEGEN_VERSION = '0.0.0'; -private static $operationPathNameTemplate; + private static $operationPathNameTemplate; -private $grpcCredentialsHelper; -private $operationsStub; -private $scopes; -private $defaultCallSettings; -private $descriptors; + private $grpcCredentialsHelper; + private $operationsStub; + private $scopes; + private $defaultCallSettings; + private $descriptors; /** * Formats a string containing the fully-qualified path to represent * a operation_path resource. */ -public static function formatOperationPathName($operationPath) -{ - return self::getOperationPathNameTemplate()->render([ - 'operation_path' => $operationPath, - ]); -} + public static function formatOperationPathName($operationPath) + { + return self::getOperationPathNameTemplate()->render([ + 'operation_path' => $operationPath, + ]); + } /** * Parses the operation_path from the given fully-qualified path which * represents a operation_path resource. */ -public static function parseOperationPathFromOperationPathName($operationPathName) -{ - return self::getOperationPathNameTemplate()->match($operationPathName)['operation_path']; -} - - -private static function getOperationPathNameTemplate() -{ - if (self::$operationPathNameTemplate == null) { - self::$operationPathNameTemplate = new PathTemplate('operations/{operation_path=**}'); + public static function parseOperationPathFromOperationPathName($operationPathName) + { + return self::getOperationPathNameTemplate()->match($operationPathName)['operation_path']; } - return self::$operationPathNameTemplate; -} - -private static function getPageStreamingDescriptors() -{ - $listOperationsPageStreamingDescriptor = - new PageStreamingDescriptor([ - 'requestPageTokenField' => 'page_token', - 'requestPageSizeField' => 'page_size', - 'responsePageTokenField' => 'next_page_token', - 'resourceField' => 'operations', - ]); + private static function getOperationPathNameTemplate() + { + if (self::$operationPathNameTemplate == null) { + self::$operationPathNameTemplate = new PathTemplate('operations/{operation_path=**}'); + } - $pageStreamingDescriptors = [ - 'listOperations' => $listOperationsPageStreamingDescriptor, - ]; + return self::$operationPathNameTemplate; + } - return $pageStreamingDescriptors; -} + private static function getPageStreamingDescriptors() + { + $listOperationsPageStreamingDescriptor = + new PageStreamingDescriptor([ + 'requestPageTokenField' => 'page_token', + 'requestPageSizeField' => 'page_size', + 'responsePageTokenField' => 'next_page_token', + 'resourceField' => 'operations', + ]); + + $pageStreamingDescriptors = [ + 'listOperations' => $listOperationsPageStreamingDescriptor, + ]; + + return $pageStreamingDescriptors; + } // TODO(garrettjones): add channel (when supported in gRPC) /** * Constructor. * * @param array $options { - * Optional. Options for configuring the service API wrapper. + * Optional. Options for configuring the service API wrapper. * * @type string $serviceAddress The domain name of the API remote host. * Default 'longrunning.googleapis.com'. @@ -175,73 +174,73 @@ private static function getPageStreamingDescriptors() * Google\Auth library. * } */ -public function __construct($options = []) -{ - $defaultScopes = [ - ]; - $defaultOptions = [ - 'serviceAddress' => self::SERVICE_ADDRESS, - 'port' => self::DEFAULT_SERVICE_PORT, - 'scopes' => $defaultScopes, - 'retryingOverride' => null, - 'timeoutMillis' => self::DEFAULT_TIMEOUT_MILLIS, - 'appName' => 'gax', - 'appVersion' => self::_GAX_VERSION, - 'credentialsLoader' => null, - ]; - $options = array_merge($defaultOptions, $options); - - $headerDescriptor = new AgentHeaderDescriptor([ - 'clientName' => $options['appName'], - 'clientVersion' => $options['appVersion'], - 'codeGenName' => self::_CODEGEN_NAME, - 'codeGenVersion' => self::_CODEGEN_VERSION, - 'gaxVersion' => self::_GAX_VERSION, - 'phpVersion' => phpversion(), - ]); - - $defaultDescriptors = ['headerDescriptor' => $headerDescriptor]; - $this->descriptors = [ - 'getOperation' => $defaultDescriptors, - 'listOperations' => $defaultDescriptors, - 'cancelOperation' => $defaultDescriptors, - 'deleteOperation' => $defaultDescriptors, - ]; - $pageStreamingDescriptors = self::getPageStreamingDescriptors(); - foreach ($pageStreamingDescriptors as $method => $pageStreamingDescriptor) { - $this->descriptors[$method]['pageStreamingDescriptor'] = $pageStreamingDescriptor; - } - - $clientConfigJsonString = file_get_contents(__DIR__ . '/resources/operations_client_config.json'); - $clientConfig = json_decode($clientConfigJsonString, true); - $this->defaultCallSettings = - CallSettings::load( - 'google.longrunning.Operations', - $clientConfig, - $options['retryingOverride'], - GrpcConstants::getStatusCodeNames(), - $options['timeoutMillis'] - ); + public function __construct($options = []) + { + $defaultScopes = [ + ]; + $defaultOptions = [ + 'serviceAddress' => self::SERVICE_ADDRESS, + 'port' => self::DEFAULT_SERVICE_PORT, + 'scopes' => $defaultScopes, + 'retryingOverride' => null, + 'timeoutMillis' => self::DEFAULT_TIMEOUT_MILLIS, + 'appName' => 'gax', + 'appVersion' => self::_GAX_VERSION, + 'credentialsLoader' => null, + ]; + $options = array_merge($defaultOptions, $options); + + $headerDescriptor = new AgentHeaderDescriptor([ + 'clientName' => $options['appName'], + 'clientVersion' => $options['appVersion'], + 'codeGenName' => self::_CODEGEN_NAME, + 'codeGenVersion' => self::_CODEGEN_VERSION, + 'gaxVersion' => self::_GAX_VERSION, + 'phpVersion' => phpversion(), + ]); - $this->scopes = $options['scopes']; + $defaultDescriptors = ['headerDescriptor' => $headerDescriptor]; + $this->descriptors = [ + 'getOperation' => $defaultDescriptors, + 'listOperations' => $defaultDescriptors, + 'cancelOperation' => $defaultDescriptors, + 'deleteOperation' => $defaultDescriptors, + ]; + $pageStreamingDescriptors = self::getPageStreamingDescriptors(); + foreach ($pageStreamingDescriptors as $method => $pageStreamingDescriptor) { + $this->descriptors[$method]['pageStreamingDescriptor'] = $pageStreamingDescriptor; + } - $createStubOptions = []; - if (!empty($options['sslCreds'])) { - $createStubOptions['sslCreds'] = $options['sslCreds']; - } - $grpcCredentialsHelperOptions = array_diff_key($options, $defaultOptions); - $this->grpcCredentialsHelper = new GrpcCredentialsHelper($this->scopes, $grpcCredentialsHelperOptions); + $clientConfigJsonString = file_get_contents(__DIR__.'/resources/operations_client_config.json'); + $clientConfig = json_decode($clientConfigJsonString, true); + $this->defaultCallSettings = + CallSettings::load( + 'google.longrunning.Operations', + $clientConfig, + $options['retryingOverride'], + GrpcConstants::getStatusCodeNames(), + $options['timeoutMillis'] + ); + + $this->scopes = $options['scopes']; + + $createStubOptions = []; + if (!empty($options['sslCreds'])) { + $createStubOptions['sslCreds'] = $options['sslCreds']; + } + $grpcCredentialsHelperOptions = array_diff_key($options, $defaultOptions); + $this->grpcCredentialsHelper = new GrpcCredentialsHelper($this->scopes, $grpcCredentialsHelperOptions); - $createOperationsStubFunction = function ($hostname, $opts) { - return new OperationsClient($hostname, $opts); - }; + $createOperationsStubFunction = function ($hostname, $opts) { + return new OperationsClient($hostname, $opts); + }; $this->operationsStub = $this->grpcCredentialsHelper->createStub( $createOperationsStubFunction, $options['serviceAddress'], $options['port'], $createStubOptions ); -} + } /** * Gets the latest state of a long-running operation. Clients can use this @@ -261,9 +260,10 @@ public function __construct($options = []) * } * ``` * - * @param string $name The name of the operation resource. - * @param array $optionalArgs { - * Optional. + * @param string $name The name of the operation resource. + * @param array $optionalArgs { + * Optional. + * * @type Google\GAX\RetrySettings $retrySettings * Retry settings to use for this call. If present, then * $timeoutMillis is ignored. @@ -276,33 +276,33 @@ public function __construct($options = []) * * @throws Google\GAX\ApiException if the remote call fails */ -public function getOperation($name, $optionalArgs = []) -{ - $request = new GetOperationRequest(); - $request->setName($name); - - $mergedSettings = $this->defaultCallSettings['getOperation']->merge( - new CallSettings($optionalArgs) - ); - $callable = ApiCallable::createApiCall( - $this->operationsStub, - 'GetOperation', - $mergedSettings, - $this->descriptors['getOperation'] - ); - - return $callable( - $request, - [], - ['call_credentials_callback' => $this->createCredentialsCallback()]); -} + public function getOperation($name, $optionalArgs = []) + { + $request = new GetOperationRequest(); + $request->setName($name); + + $mergedSettings = $this->defaultCallSettings['getOperation']->merge( + new CallSettings($optionalArgs) + ); + $callable = ApiCallable::createApiCall( + $this->operationsStub, + 'GetOperation', + $mergedSettings, + $this->descriptors['getOperation'] + ); + + return $callable( + $request, + [], + ['call_credentials_callback' => $this->createCredentialsCallback()]); + } /** * Lists operations that match the specified filter in the request. If the * server doesn't support this method, it returns `UNIMPLEMENTED`. * * NOTE: the `name` binding below allows API services to override the binding - * to use different resource name schemes, such as `users/* /operations`. + * to use different resource name schemes, such as `users/{@*}/operations`. * * Sample code: * ``` @@ -311,46 +311,43 @@ public function getOperation($name, $optionalArgs = []) * $name = ""; * $filter = ""; * foreach ($operationsApi->listOperations($name, $filter) as $element) { - * // doThingsWith(element); - * - } - * - } finally { - * if (isset($operationsApi)) { - * $operationsApi->close(); - * - } - * - } - * ``` - * - * @param string $name The name of the operation collection. - * @param string $filter The standard list filter. - * @param array $optionalArgs { - * Optional. - * @type int $pageSize - * The maximum number of resources contained in the underlying API - * response. The API may return fewer values in a page, even if - * there are additional values to be retrieved. - * @type string $pageToken - * A page token is used to specify a page of values to be returned. - * If no page token is specified (the default), the first page - * of values will be returned. Any page token used here must have - * been generated by a previous call to the API. - * @type Google\GAX\RetrySettings $retrySettings - * Retry settings to use for this call. If present, then - * $timeoutMillis is ignored. - * @type int $timeoutMillis - * Timeout to use for this call. Only used if $retrySettings - * is not set. - * } - * - * @return Google\GAX\PagedListResponse - * - * @throws Google\GAX\ApiException if the remote call fails - */ - public function listOperations($name, $filter, $optionalArgs = []) - { + * // doThingsWith(element); + * } + * } finally { + * if (isset($operationsApi)) { + * $operationsApi->close(); + * } + * } + * ``` + * + * @param string $name The name of the operation collection. + * @param string $filter The standard list filter. + * @param array $optionalArgs { + * Optional. + * + * @type int $pageSize + * The maximum number of resources contained in the underlying API + * response. The API may return fewer values in a page, even if + * there are additional values to be retrieved. + * @type string $pageToken + * A page token is used to specify a page of values to be returned. + * If no page token is specified (the default), the first page + * of values will be returned. Any page token used here must have + * been generated by a previous call to the API. + * @type Google\GAX\RetrySettings $retrySettings + * Retry settings to use for this call. If present, then + * $timeoutMillis is ignored. + * @type int $timeoutMillis + * Timeout to use for this call. Only used if $retrySettings + * is not set. + * } + * + * @return Google\GAX\PagedListResponse + * + * @throws Google\GAX\ApiException if the remote call fails + */ + public function listOperations($name, $filter, $optionalArgs = []) + { $request = new ListOperationsRequest(); $request->setName($name); $request->setFilter($filter); @@ -362,135 +359,146 @@ public function listOperations($name, $filter, $optionalArgs = []) } $mergedSettings = $this->defaultCallSettings['listOperations']->merge( - new CallSettings($optionalArgs)); + new CallSettings($optionalArgs) + ); $callable = ApiCallable::createApiCall( - $this->operationsStub, 'ListOperations', $mergedSettings, $this->descriptors['listOperations']); + $this->operationsStub, + 'ListOperations', + $mergedSettings, + $this->descriptors['listOperations'] + ); return $callable( $request, [], ['call_credentials_callback' => $this->createCredentialsCallback()]); - } + } - /** - * Starts asynchronous cancellation on a long-running operation. The server - * makes a best effort to cancel the operation, but success is not - * guaranteed. If the server doesn't support this method, it returns - * `google.rpc.Code.UNIMPLEMENTED`. Clients can use - * [Operations.GetOperation][google.longrunning.Operations.GetOperation] or - * other methods to check whether the cancellation succeeded or whether the - * operation completed despite cancellation. On successful cancellation, - * the operation is not deleted; instead, it becomes an operation with - * an [Operation.error][google.longrunning.Operation.error] value with a [google.rpc.Status.code][google.rpc.Status.code] of 1, - * corresponding to `Code.CANCELLED`. - * - * Sample code: - * ``` - * try { - * $operationsApi = new OperationsApi(); - * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); - * $operationsApi->cancelOperation($formattedName); - * - } finally { - * if (isset($operationsApi)) { - * $operationsApi->close(); - * - } - * - } - * ``` - * - * @param string $name The name of the operation resource to be cancelled. - * @param array $optionalArgs { - * Optional. - * @type Google\GAX\RetrySettings $retrySettings - * Retry settings to use for this call. If present, then - * $timeoutMillis is ignored. - * @type int $timeoutMillis - * Timeout to use for this call. Only used if $retrySettings - * is not set. - * } - * - * @throws Google\GAX\ApiException if the remote call fails - */ - public function cancelOperation($name, $optionalArgs = []) - { - $request = new CancelOperationRequest(); - $request->setName($name); - - $mergedSettings = $this->defaultCallSettings['cancelOperation']->merge( - new CallSettings($optionalArgs)); - $callable = ApiCallable::createApiCall( - $this->operationsStub, 'CancelOperation', $mergedSettings, $this->descriptors['cancelOperation']); - - return $callable( + /** + * Starts asynchronous cancellation on a long-running operation. The server + * makes a best effort to cancel the operation, but success is not + * guaranteed. If the server doesn't support this method, it returns + * `google.rpc.Code.UNIMPLEMENTED`. Clients can use + * [Operations.GetOperation][google.longrunning.Operations.GetOperation] or + * other methods to check whether the cancellation succeeded or whether the + * operation completed despite cancellation. On successful cancellation, + * the operation is not deleted; instead, it becomes an operation with + * an [Operation.error][google.longrunning.Operation.error] value with a [google.rpc.Status.code][google.rpc.Status.code] of 1, + * corresponding to `Code.CANCELLED`. + * + * Sample code: + * ``` + * try { + * $operationsApi = new OperationsApi(); + * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); + * $operationsApi->cancelOperation($formattedName); + * } finally { + * if (isset($operationsApi)) { + * $operationsApi->close(); + * } + * } + * ``` + * + * @param string $name The name of the operation resource to be cancelled. + * @param array $optionalArgs { + * Optional. + * + * @type Google\GAX\RetrySettings $retrySettings + * Retry settings to use for this call. If present, then + * $timeoutMillis is ignored. + * @type int $timeoutMillis + * Timeout to use for this call. Only used if $retrySettings + * is not set. + * } + * + * @throws Google\GAX\ApiException if the remote call fails + */ + public function cancelOperation($name, $optionalArgs = []) + { + $request = new CancelOperationRequest(); + $request->setName($name); + + $mergedSettings = $this->defaultCallSettings['cancelOperation']->merge( + new CallSettings($optionalArgs) + ); + $callable = ApiCallable::createApiCall( + $this->operationsStub, + 'CancelOperation', + $mergedSettings, + $this->descriptors['cancelOperation'] + ); + + return $callable( + $request, + [], + ['call_credentials_callback' => $this->createCredentialsCallback()]); + } + + /** + * Deletes a long-running operation. This method indicates that the client is + * no longer interested in the operation result. It does not cancel the + * operation. If the server doesn't support this method, it returns + * `google.rpc.Code.UNIMPLEMENTED`. + * + * Sample code: + * ``` + * try { + * $operationsApi = new OperationsApi(); + * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); + * $operationsApi->deleteOperation($formattedName); + * } finally { + * if (isset($operationsApi)) { + * $operationsApi->close(); + * } + * } + * ``` + * + * @param string $name The name of the operation resource to be deleted. + * @param array $optionalArgs { + * Optional. + * + * @type Google\GAX\RetrySettings $retrySettings + * Retry settings to use for this call. If present, then + * $timeoutMillis is ignored. + * @type int $timeoutMillis + * Timeout to use for this call. Only used if $retrySettings + * is not set. + * } + * + * @throws Google\GAX\ApiException if the remote call fails + */ + public function deleteOperation($name, $optionalArgs = []) + { + $request = new DeleteOperationRequest(); + $request->setName($name); + + $mergedSettings = $this->defaultCallSettings['deleteOperation']->merge( + new CallSettings($optionalArgs) + ); + $callable = ApiCallable::createApiCall( + $this->operationsStub, + 'DeleteOperation', + $mergedSettings, + $this->descriptors['deleteOperation'] + ); + + return $callable( $request, [], ['call_credentials_callback' => $this->createCredentialsCallback()]); - } - - /** - * Deletes a long-running operation. This method indicates that the client is - * no longer interested in the operation result. It does not cancel the - * operation. If the server doesn't support this method, it returns - * `google.rpc.Code.UNIMPLEMENTED`. - * - * Sample code: - * ``` - * try { - * $operationsApi = new OperationsApi(); - * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); - * $operationsApi->deleteOperation($formattedName); - * - } finally { - * if (isset($operationsApi)) { - * $operationsApi->close(); - * - } - * - } - * ``` - * - * @param string $name The name of the operation resource to be deleted. - * @param array $optionalArgs { - * Optional. - * @type Google\GAX\RetrySettings $retrySettings - * Retry settings to use for this call. If present, then - * $timeoutMillis is ignored. - * @type int $timeoutMillis - * Timeout to use for this call. Only used if $retrySettings - * is not set. - * } - * - * @throws Google\GAX\ApiException if the remote call fails - */ - public function deleteOperation($name, $optionalArgs = []) - { - $request = new DeleteOperationRequest(); - $request->setName($name); - - $mergedSettings = $this->defaultCallSettings['deleteOperation']->merge( - new CallSettings($optionalArgs)); - $callable = ApiCallable::createApiCall( - $this->operationsStub, 'DeleteOperation', $mergedSettings, $this->descriptors['deleteOperation']); - - return $callable( - $request, - [], - ['call_credentials_callback' => $this->createCredentialsCallback()]); - } - - /** - * Initiates an orderly shutdown in which preexisting calls continue but new - * calls are immediately cancelled. - */ - public function close() - { - $this->operationsStub->close(); - } - - private function createCredentialsCallback() - { - return $this->grpcCredentialsHelper->createCallCredentialsCallback(); - } - } + } + + /** + * Initiates an orderly shutdown in which preexisting calls continue but new + * calls are immediately cancelled. + */ + public function close() + { + $this->operationsStub->close(); + } + + private function createCredentialsCallback() + { + return $this->grpcCredentialsHelper->createCallCredentialsCallback(); + } +} From bb333ba35fed5e79569c7294d48275b2f50e968e Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 22 Nov 2016 10:54:20 -0800 Subject: [PATCH 06/24] Start adding polling --- src/OperationResponse.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/OperationResponse.php b/src/OperationResponse.php index 148be0ef0..fce5ccb8a 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -6,6 +6,8 @@ class OperationResponse { + const DEFAULT_POLLING_INTERVAL = 1.0; + private $operationName; private $operationsApi; private $operationReturnType; @@ -43,6 +45,7 @@ public function getName() public function pollUntilComplete($handler = null, $pollSettings = []) { + while (!$this->isDone()) { // TODO: use poll settings sleep(1); From 68bfcc4eca82bf090942e32e89a017e5d7e398aa Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Sat, 3 Dec 2016 16:05:06 -0800 Subject: [PATCH 07/24] Restructure, add comments --- src/ApiCallable.php | 6 +++- src/OperationResponse.php | 61 +++++++++++++++++++++++++-------------- 2 files changed, 44 insertions(+), 23 deletions(-) diff --git a/src/ApiCallable.php b/src/ApiCallable.php index 7bf8f019f..5f7da5104 100644 --- a/src/ApiCallable.php +++ b/src/ApiCallable.php @@ -125,7 +125,11 @@ private static function setLongRunnning($callable, $longRunningDescriptor) $inner = function () use ($callable, $longRunningDescriptor) { $response = call_user_func_array($callable, func_get_args()); $name = $response->getName(); - return new OperationResponse($response->getName(), $longRunningDescriptor, $response); + $client = $longRunningDescriptor['operationsClient']; + $options = $longRunningDescriptor + [ + 'lastProtoResponse' => $response, + ] + return new OperationResponse($name, $client, $options); }; return $inner; } diff --git a/src/OperationResponse.php b/src/OperationResponse.php index fce5ccb8a..17b67f7ec 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -2,33 +2,41 @@ namespace Google\GAX; -use Google\Longrunning\OperationsApi; - +use Google\Longrunning\OperationsClient; + +/** + * Response object from a long running API method + * + * The OperationResponse object is returned by API methods that perform + * a long running operation. It provides methods that can be used to + * poll the status of the operation, retrieve the results, and cancel + * the operation. + * + * To support a long running operation, the server must implement the + * Operations API, which is used by the OperationResponse object. If + * more control is required, it is possible to make calls against the + * Operations API directly instead of via the OperationResponse object + * using an OperationsClient instance. + */ class OperationResponse { const DEFAULT_POLLING_INTERVAL = 1.0; private $operationName; - private $operationsApi; + private $operationsClient; private $operationReturnType; private $lastProtoResponse; - public static function resumeOperation($operationName, $serviceAddress) - { - $operationsApi = new OperationsApi(['serviceAddress' => $serviceAddress]); - $longRunningDescriptor = [ - 'operationsApi' => $operationsApi, - 'operationReturnType' => null, - ]; - return new OperationResponse($operationName, $longRunningDescriptor, null); - } - - public function __construct($operationName, $longRunningDescriptor, $lastProtoResponse = null) + public function __construct($operationName, $operationsClient, $options = []) { $this->operationName = $operationName; - $this->operationsApi = $longRunningDescriptor['operationsApi']; - $this->operationReturnType = $longRunningDescriptor['operationReturnType']; - $this->lastProtoResponse = $lastProtoResponse; + $this->operationsClient = $operationsClient; + if (isset($options['operationReturnType'])) { + $this->operationReturnType = $options['operationReturnType']; + } + if (isset($options['lastProtoResponse'])) { + $this->lastProtoResponse = $options['lastProtoResponse']; + } } public function isDone() @@ -45,11 +53,15 @@ public function getName() public function pollUntilComplete($handler = null, $pollSettings = []) { + $defaultPollSettings = [ + 'pollingIntervalSeconds' => this::DEFAULT_POLLING_INTERVAL, + ]; + $pollSettings = array_merge($defaultPollSettings, $pollSettings); + + $pollingIntervalMicros = $pollSettings['pollingIntervalSeconds'] * 1000000; while (!$this->isDone()) { - // TODO: use poll settings - sleep(1); - echo "refreshing...\n"; + usleep($pollingIntervalMicros); $this->refresh(); } @@ -63,7 +75,7 @@ public function pollUntilComplete($handler = null, $pollSettings = []) public function refresh() { $name = $this->getName(); - $this->lastProtoResponse = $this->operationsApi->getOperation($name); + $this->lastProtoResponse = $this->operationsClient->getOperation($name); } public function getResult() @@ -95,8 +107,13 @@ public function getLastProtoResponse() return $this->lastProtoResponse; } + public function getOperationsClient() + { + return $this->operationsClient; + } + public function cancel() { - $this->operationsApi->cancelOperation($this->getName()); + $this->operationsClient->cancelOperation($this->getName()); } } From 53198df5c0281700c6a83a96eab294493fee2002 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 13 Dec 2016 14:19:42 -0800 Subject: [PATCH 08/24] Update operations api and response object --- src/OperationResponse.php | 61 ++++++++- src/generated/operations.php | 6 +- ...OperationsApi.php => OperationsClient.php} | 128 +++++++----------- 3 files changed, 110 insertions(+), 85 deletions(-) rename src/longrunning/{OperationsApi.php => OperationsClient.php} (82%) diff --git a/src/OperationResponse.php b/src/OperationResponse.php index 17b67f7ec..7a8df36ee 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -2,10 +2,8 @@ namespace Google\GAX; -use Google\Longrunning\OperationsClient; - /** - * Response object from a long running API method + * Response object from a long running API method. * * The OperationResponse object is returned by API methods that perform * a long running operation. It provides methods that can be used to @@ -39,6 +37,11 @@ public function __construct($operationName, $operationsClient, $options = []) } } + /** + * Check whether the operation has completed. + * + * @return bool + */ public function isDone() { return is_null($this->lastProtoResponse) @@ -46,11 +49,34 @@ public function isDone() : $this->lastProtoResponse->getDone(); } + /** + * Get the formatted name of the operation + * + * @return string The formatted name of the operation + */ public function getName() { return $this->operationName; } + /** + * Poll the server in a loop until the operation is complete. + * + * If the $handler callable is not null, then call $handler (with $this as argument) and return + * the result. Otherwise, return true if the operation completed successfully, otherwise return + * false. + * + * The $pollSettings optional argument can be used to control the polling loop. + * + * @param callable $handler An optional callable which accepts $this as its argument. If not + * null, $handler will be called once the operation is complete (when $this->isDone() is true). + * @param array $pollSettings { + * An optional array to control the polling behavior. + * @type float $pollingIntervalSeconds The polling interval to use, in seconds. + * Default: 1.0 + * } + * @return mixed + */ public function pollUntilComplete($handler = null, $pollSettings = []) { $defaultPollSettings = [ @@ -62,7 +88,7 @@ public function pollUntilComplete($handler = null, $pollSettings = []) while (!$this->isDone()) { usleep($pollingIntervalMicros); - $this->refresh(); + $this->reload(); } if (!is_null($handler)) { @@ -72,12 +98,20 @@ public function pollUntilComplete($handler = null, $pollSettings = []) return $this->lastProtoResponse->hasResponse(); } - public function refresh() + /** + * Reload the status of the operation with a request to the service. + */ + public function reload() { $name = $this->getName(); $this->lastProtoResponse = $this->operationsClient->getOperation($name); } + /** + * Return the result of the operation. If the operation is not complete, or if the operation + * failed, return null. + * @return mixed The result of the operation, or null if the operation failed or is not complete + */ public function getResult() { if (!$this->isDone() || !$this->lastProtoResponse->hasResponse()) { @@ -94,6 +128,13 @@ public function getResult() return $response; } + /** + * If the operation failed, return the status. If the operation succeeded or is not complete, + * return null. + * + * @return \google\rpc\Status|null The status of the operation in case of failure, otherwise + * null. + */ public function getError() { if (!$this->isDone() || !$this->lastProtoResponse->hasError()) { @@ -102,16 +143,26 @@ public function getError() return $this->lastProtoResponse->getError(); } + /** + * @return \google\longrunning\Operation The last Operation object received from the server. + */ public function getLastProtoResponse() { return $this->lastProtoResponse; } + /** + * @return \Google\Longrunning\OperationsClient The OperationsClient object used to make + * requests to the operations API. + */ public function getOperationsClient() { return $this->operationsClient; } + /** + * Cancel the operation. + */ public function cancel() { $this->operationsClient->cancelOperation($this->getName()); diff --git a/src/generated/operations.php b/src/generated/operations.php index 069112b3f..4a5346727 100644 --- a/src/generated/operations.php +++ b/src/generated/operations.php @@ -827,7 +827,7 @@ public function setName( $value){ namespace google\longrunning { - class OperationsClient extends \Grpc\BaseStub { + class OperationsGrpcClient extends \Grpc\BaseStub { public function __construct($hostname, $opts) { parent::__construct($hostname, $opts); @@ -848,13 +848,13 @@ public function ListOperations(\google\longrunning\ListOperationsRequest $argume * @param google\longrunning\CancelOperationRequest $input */ public function CancelOperation(\google\longrunning\CancelOperationRequest $argument, $metadata = array(), $options = array()) { - return $this->_simpleRequest('/google.longrunning.Operations/CancelOperation', $argument, '\google\protobuf\Empty::deserialize', $metadata, $options); + return $this->_simpleRequest('/google.longrunning.Operations/CancelOperation', $argument, '\google\protobuf\EmptyC::deserialize', $metadata, $options); } /** * @param google\longrunning\DeleteOperationRequest $input */ public function DeleteOperation(\google\longrunning\DeleteOperationRequest $argument, $metadata = array(), $options = array()) { - return $this->_simpleRequest('/google.longrunning.Operations/DeleteOperation', $argument, '\google\protobuf\Empty::deserialize', $metadata, $options); + return $this->_simpleRequest('/google.longrunning.Operations/DeleteOperation', $argument, '\google\protobuf\EmptyC::deserialize', $metadata, $options); } } } diff --git a/src/longrunning/OperationsApi.php b/src/longrunning/OperationsClient.php similarity index 82% rename from src/longrunning/OperationsApi.php rename to src/longrunning/OperationsClient.php index e54640181..2a4e47aa5 100644 --- a/src/longrunning/OperationsApi.php +++ b/src/longrunning/OperationsClient.php @@ -18,6 +18,10 @@ * This file was generated from the file * https://github.com/google/googleapis/blob/master/google/longrunning/operations.proto * and updates to that file get reflected here through a refresh process. + * + * EXPERIMENTAL: this client library class has not yet been declared beta. This class may change + * more frequently than those which have been declared beta or 1.0, including changes which break + * backwards compatibility. */ namespace Google\Longrunning; @@ -28,12 +32,11 @@ use Google\GAX\GrpcConstants; use Google\GAX\GrpcCredentialsHelper; use Google\GAX\PageStreamingDescriptor; -use Google\GAX\PathTemplate; use google\longrunning\CancelOperationRequest; use google\longrunning\DeleteOperationRequest; use google\longrunning\GetOperationRequest; use google\longrunning\ListOperationsRequest; -use google\longrunning\OperationsClient; +use google\longrunning\OperationsGrpcClient; /** * Service Description: Manages long-running operations with an API service. @@ -46,17 +49,21 @@ * returns long-running operations should implement the `Operations` interface * so developers can have a consistent client experience. * + * EXPERIMENTAL: this client library class has not yet been declared beta. This class may change + * more frequently than those which have been declared beta or 1.0, including changes which break + * backwards compatibility. + * * This class provides the ability to make remote calls to the backing service through method * calls that map to API methods. Sample code to get started: * * ``` * try { - * $operationsApi = new OperationsApi(); - * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); - * $response = $operationsApi->getOperation($formattedName); + * $operationsClient = new OperationsClient(); + * $name = ""; + * $response = $operationsClient->getOperation($name); * } finally { - * if (isset($operationsApi)) { - * $operationsApi->close(); + * if (isset($operationsClient)) { + * $operationsClient->close(); * } * } * ``` @@ -66,7 +73,7 @@ * a parse method to extract the individual identifiers contained within names that are * returned. */ -class OperationsApi +class OperationsClient { /** * The default address of the service. @@ -83,11 +90,8 @@ class OperationsApi */ const DEFAULT_TIMEOUT_MILLIS = 30000; - const _GAX_VERSION = '0.1.0'; - const _CODEGEN_NAME = 'GAPIC'; - const _CODEGEN_VERSION = '0.0.0'; - - private static $operationPathNameTemplate; + const _CODEGEN_NAME = 'gapic'; + const _CODEGEN_VERSION = '0.1.0'; private $grpcCredentialsHelper; private $operationsStub; @@ -95,35 +99,6 @@ class OperationsApi private $defaultCallSettings; private $descriptors; - /** - * Formats a string containing the fully-qualified path to represent - * a operation_path resource. - */ - public static function formatOperationPathName($operationPath) - { - return self::getOperationPathNameTemplate()->render([ - 'operation_path' => $operationPath, - ]); - } - - /** - * Parses the operation_path from the given fully-qualified path which - * represents a operation_path resource. - */ - public static function parseOperationPathFromOperationPathName($operationPathName) - { - return self::getOperationPathNameTemplate()->match($operationPathName)['operation_path']; - } - - private static function getOperationPathNameTemplate() - { - if (self::$operationPathNameTemplate == null) { - self::$operationPathNameTemplate = new PathTemplate('operations/{operation_path=**}'); - } - - return self::$operationPathNameTemplate; - } - private static function getPageStreamingDescriptors() { $listOperationsPageStreamingDescriptor = @@ -185,8 +160,7 @@ public function __construct($options = []) 'retryingOverride' => null, 'timeoutMillis' => self::DEFAULT_TIMEOUT_MILLIS, 'appName' => 'gax', - 'appVersion' => self::_GAX_VERSION, - 'credentialsLoader' => null, + 'appVersion' => AgentHeaderDescriptor::getGaxVersion(), ]; $options = array_merge($defaultOptions, $options); @@ -195,7 +169,7 @@ public function __construct($options = []) 'clientVersion' => $options['appVersion'], 'codeGenName' => self::_CODEGEN_NAME, 'codeGenVersion' => self::_CODEGEN_VERSION, - 'gaxVersion' => self::_GAX_VERSION, + 'gaxVersion' => AgentHeaderDescriptor::getGaxVersion(), 'phpVersion' => phpversion(), ]); @@ -225,14 +199,14 @@ public function __construct($options = []) $this->scopes = $options['scopes']; $createStubOptions = []; - if (!empty($options['sslCreds'])) { + if (array_key_exists('sslCreds', $options)) { $createStubOptions['sslCreds'] = $options['sslCreds']; } $grpcCredentialsHelperOptions = array_diff_key($options, $defaultOptions); $this->grpcCredentialsHelper = new GrpcCredentialsHelper($this->scopes, $grpcCredentialsHelperOptions); $createOperationsStubFunction = function ($hostname, $opts) { - return new OperationsClient($hostname, $opts); + return new OperationsGrpcClient($hostname, $opts); }; $this->operationsStub = $this->grpcCredentialsHelper->createStub( $createOperationsStubFunction, @@ -250,12 +224,12 @@ public function __construct($options = []) * Sample code: * ``` * try { - * $operationsApi = new OperationsApi(); - * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); - * $response = $operationsApi->getOperation($formattedName); + * $operationsClient = new OperationsClient(); + * $name = ""; + * $response = $operationsClient->getOperation($name); * } finally { - * if (isset($operationsApi)) { - * $operationsApi->close(); + * if (isset($operationsClient)) { + * $operationsClient->close(); * } * } * ``` @@ -264,7 +238,7 @@ public function __construct($options = []) * @param array $optionalArgs { * Optional. * - * @type Google\GAX\RetrySettings $retrySettings + * @type \Google\GAX\RetrySettings $retrySettings * Retry settings to use for this call. If present, then * $timeoutMillis is ignored. * @type int $timeoutMillis @@ -272,9 +246,9 @@ public function __construct($options = []) * is not set. * } * - * @return google\longrunning\Operation + * @return \google\longrunning\Operation * - * @throws Google\GAX\ApiException if the remote call fails + * @throws \Google\GAX\ApiException if the remote call fails */ public function getOperation($name, $optionalArgs = []) { @@ -302,20 +276,20 @@ public function getOperation($name, $optionalArgs = []) * server doesn't support this method, it returns `UNIMPLEMENTED`. * * NOTE: the `name` binding below allows API services to override the binding - * to use different resource name schemes, such as `users/{@*}/operations`. + * to use different resource name schemes, such as `users/*/operations`. * * Sample code: * ``` * try { - * $operationsApi = new OperationsApi(); + * $operationsClient = new OperationsClient(); * $name = ""; * $filter = ""; - * foreach ($operationsApi->listOperations($name, $filter) as $element) { + * foreach ($operationsClient->listOperations($name, $filter) as $element) { * // doThingsWith(element); * } * } finally { - * if (isset($operationsApi)) { - * $operationsApi->close(); + * if (isset($operationsClient)) { + * $operationsClient->close(); * } * } * ``` @@ -334,7 +308,7 @@ public function getOperation($name, $optionalArgs = []) * If no page token is specified (the default), the first page * of values will be returned. Any page token used here must have * been generated by a previous call to the API. - * @type Google\GAX\RetrySettings $retrySettings + * @type \Google\GAX\RetrySettings $retrySettings * Retry settings to use for this call. If present, then * $timeoutMillis is ignored. * @type int $timeoutMillis @@ -342,9 +316,9 @@ public function getOperation($name, $optionalArgs = []) * is not set. * } * - * @return Google\GAX\PagedListResponse + * @return \Google\GAX\PagedListResponse * - * @throws Google\GAX\ApiException if the remote call fails + * @throws \Google\GAX\ApiException if the remote call fails */ public function listOperations($name, $filter, $optionalArgs = []) { @@ -389,12 +363,12 @@ public function listOperations($name, $filter, $optionalArgs = []) * Sample code: * ``` * try { - * $operationsApi = new OperationsApi(); - * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); - * $operationsApi->cancelOperation($formattedName); + * $operationsClient = new OperationsClient(); + * $name = ""; + * $operationsClient->cancelOperation($name); * } finally { - * if (isset($operationsApi)) { - * $operationsApi->close(); + * if (isset($operationsClient)) { + * $operationsClient->close(); * } * } * ``` @@ -403,7 +377,7 @@ public function listOperations($name, $filter, $optionalArgs = []) * @param array $optionalArgs { * Optional. * - * @type Google\GAX\RetrySettings $retrySettings + * @type \Google\GAX\RetrySettings $retrySettings * Retry settings to use for this call. If present, then * $timeoutMillis is ignored. * @type int $timeoutMillis @@ -411,7 +385,7 @@ public function listOperations($name, $filter, $optionalArgs = []) * is not set. * } * - * @throws Google\GAX\ApiException if the remote call fails + * @throws \Google\GAX\ApiException if the remote call fails */ public function cancelOperation($name, $optionalArgs = []) { @@ -443,12 +417,12 @@ public function cancelOperation($name, $optionalArgs = []) * Sample code: * ``` * try { - * $operationsApi = new OperationsApi(); - * $formattedName = OperationsApi::formatOperationPathName("[OPERATION_PATH]"); - * $operationsApi->deleteOperation($formattedName); + * $operationsClient = new OperationsClient(); + * $name = ""; + * $operationsClient->deleteOperation($name); * } finally { - * if (isset($operationsApi)) { - * $operationsApi->close(); + * if (isset($operationsClient)) { + * $operationsClient->close(); * } * } * ``` @@ -457,7 +431,7 @@ public function cancelOperation($name, $optionalArgs = []) * @param array $optionalArgs { * Optional. * - * @type Google\GAX\RetrySettings $retrySettings + * @type \Google\GAX\RetrySettings $retrySettings * Retry settings to use for this call. If present, then * $timeoutMillis is ignored. * @type int $timeoutMillis @@ -465,7 +439,7 @@ public function cancelOperation($name, $optionalArgs = []) * is not set. * } * - * @throws Google\GAX\ApiException if the remote call fails + * @throws \Google\GAX\ApiException if the remote call fails */ public function deleteOperation($name, $optionalArgs = []) { From e4bffe5b7107bcc71cef2778bfa1bb79dee26b8e Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 13 Dec 2016 14:29:51 -0800 Subject: [PATCH 09/24] Fix syntax error --- src/ApiCallable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ApiCallable.php b/src/ApiCallable.php index 5f7da5104..8813f6ba0 100644 --- a/src/ApiCallable.php +++ b/src/ApiCallable.php @@ -128,7 +128,7 @@ private static function setLongRunnning($callable, $longRunningDescriptor) $client = $longRunningDescriptor['operationsClient']; $options = $longRunningDescriptor + [ 'lastProtoResponse' => $response, - ] + ]; return new OperationResponse($name, $client, $options); }; return $inner; From 24d8b81074822dfab10da7e7c33aa5593d3bbf88 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 13 Dec 2016 17:05:53 -0800 Subject: [PATCH 10/24] Fix syntax error --- src/OperationResponse.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OperationResponse.php b/src/OperationResponse.php index 7a8df36ee..f0939de43 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -80,7 +80,7 @@ public function getName() public function pollUntilComplete($handler = null, $pollSettings = []) { $defaultPollSettings = [ - 'pollingIntervalSeconds' => this::DEFAULT_POLLING_INTERVAL, + 'pollingIntervalSeconds' => $this::DEFAULT_POLLING_INTERVAL, ]; $pollSettings = array_merge($defaultPollSettings, $pollSettings); From 824a64c99f3f039f2546e2665f6c147e30a4fada Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 20 Dec 2016 13:36:18 -0800 Subject: [PATCH 11/24] Add tests --- src/ApiCallable.php | 10 +- src/GrpcCredentialsHelper.php | 4 +- .../OperationsClient.php | 3 + .../resources/operations_client_config.json | 0 src/OperationResponse.php | 57 +++- tests/ApiCallableTest.php | 305 ++++++++++++++++++ tests/OperationResponseTest.php | 128 ++++++++ tests/PagedListResponseTest.php | 3 +- tests/mocks/MockStub.php | 12 +- 9 files changed, 511 insertions(+), 11 deletions(-) rename src/{longrunning => Longrunning}/OperationsClient.php (99%) rename src/{longrunning => Longrunning}/resources/operations_client_config.json (100%) create mode 100644 tests/OperationResponseTest.php diff --git a/src/ApiCallable.php b/src/ApiCallable.php index 8813f6ba0..66fb57630 100644 --- a/src/ApiCallable.php +++ b/src/ApiCallable.php @@ -152,16 +152,18 @@ private static function setCustomHeader($callable, $headerDescriptor) } /** - * @param Grpc\BaseStub $stub the gRPC stub to make calls through. + * @param \Grpc\BaseStub $stub the gRPC stub to make calls through. * @param string $methodName the method name on the stub to call. - * @param Google\GAX\CallSettings $settings the call settings to use for this call. + * @param \Google\GAX\CallSettings $settings the call settings to use for this call. * @param array $options { * Optional. - * @type Google\GAX\PageStreamingDescriptor $pageStreamingDescriptor + * @type \Google\GAX\PageStreamingDescriptor $pageStreamingDescriptor * the descriptor used for page-streaming. - * @type Google\GAX\AgentHeaderDescriptor $headerDescriptor + * @type \Google\GAX\AgentHeaderDescriptor $headerDescriptor * the descriptor used for creating GAPIC header. * } + * + * @return callable */ public static function createApiCall($stub, $methodName, CallSettings $settings, $options = []) { diff --git a/src/GrpcCredentialsHelper.php b/src/GrpcCredentialsHelper.php index f0f70d8b4..8774ddc35 100644 --- a/src/GrpcCredentialsHelper.php +++ b/src/GrpcCredentialsHelper.php @@ -121,10 +121,10 @@ public function createCallCredentialsCallback() * @param array $options { * Optional. Options for configuring the gRPC stub. * - * @type Grpc\ChannelCredentials $sslCreds + * @type \Grpc\ChannelCredentials $sslCreds * A `ChannelCredentials` for use with an SSL-enabled channel. * Default: a credentials object returned from - * Grpc\ChannelCredentials::createSsl() + * \Grpc\ChannelCredentials::createSsl() * } */ public function createStub($generatedCreateStub, $serviceAddress, $port, $options = []) diff --git a/src/longrunning/OperationsClient.php b/src/Longrunning/OperationsClient.php similarity index 99% rename from src/longrunning/OperationsClient.php rename to src/Longrunning/OperationsClient.php index 2a4e47aa5..1d0fb9c9c 100644 --- a/src/longrunning/OperationsClient.php +++ b/src/Longrunning/OperationsClient.php @@ -208,6 +208,9 @@ public function __construct($options = []) $createOperationsStubFunction = function ($hostname, $opts) { return new OperationsGrpcClient($hostname, $opts); }; + if (array_key_exists('createOperationsStubFunction', $options)) { + $createOperationsStubFunction = $options['createOperationsStubFunction']; + } $this->operationsStub = $this->grpcCredentialsHelper->createStub( $createOperationsStubFunction, $options['serviceAddress'], diff --git a/src/longrunning/resources/operations_client_config.json b/src/Longrunning/resources/operations_client_config.json similarity index 100% rename from src/longrunning/resources/operations_client_config.json rename to src/Longrunning/resources/operations_client_config.json diff --git a/src/OperationResponse.php b/src/OperationResponse.php index f0939de43..ffbc6c347 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -23,7 +23,9 @@ class OperationResponse private $operationName; private $operationsClient; private $operationReturnType; + private $metadataReturnType; private $lastProtoResponse; + private $deleted = false; public function __construct($operationName, $operationsClient, $options = []) { @@ -32,6 +34,9 @@ public function __construct($operationName, $operationsClient, $options = []) if (isset($options['operationReturnType'])) { $this->operationReturnType = $options['operationReturnType']; } + if (isset($options['metadataReturnType'])) { + $this->metadataReturnType = $options['metadataReturnType']; + } if (isset($options['lastProtoResponse'])) { $this->lastProtoResponse = $options['lastProtoResponse']; } @@ -44,7 +49,7 @@ public function __construct($operationName, $operationsClient, $options = []) */ public function isDone() { - return is_null($this->lastProtoResponse) + return (is_null($this->lastProtoResponse) || is_null($this->lastProtoResponse->getDone())) ? false : $this->lastProtoResponse->getDone(); } @@ -103,6 +108,9 @@ public function pollUntilComplete($handler = null, $pollSettings = []) */ public function reload() { + if ($this->deleted) { + throw new ValidationException("Cannot call reload() on a deleted operation"); + } $name = $this->getName(); $this->lastProtoResponse = $this->operationsClient->getOperation($name); } @@ -161,10 +169,55 @@ public function getOperationsClient() } /** - * Cancel the operation. + * Starts asynchronous cancellation on a long-running operation. The server + * makes a best effort to cancel the operation, but success is not + * guaranteed. If the server doesn't support this method, it will throw an + * ApiException with code \google\rpc\Code::UNIMPLEMENTED. Clients can continue + * to use reload and pollUntilComplete methods to check whether the cancellation + * succeeded or whether the operation completed despite cancellation. + * On successful cancellation, the operation is not deleted; instead, it becomes + * an operation with a getError() value with a \google\rpc\Status code of 1, + * corresponding to \google\rpc\Code::CANCELLED. */ public function cancel() { $this->operationsClient->cancelOperation($this->getName()); } + + /** + * Delete the long-running operation. This method indicates that the client is + * no longer interested in the operation result. It does not cancel the operation. + * If the server doesn't support this method, it will throw an ApiException with + * code google\rpc\Code::UNIMPLEMENTED. + */ + public function delete() + { + $this->operationsClient->deleteOperation($this->getName()); + $this->deleted = true; + } + + /** + * Get the metadata returned with the last proto response. If a metadata type was provided, then + * the return value will be of that type - otherwise, the return value will be of type Any. If + * no metadata object is available, returns null. + * + * @return mixed The metadata returned from the server in the last response. + */ + public function getMetadata() + { + if (is_null($this->lastProtoResponse)) { + return null; + } + $any = $this->lastProtoResponse->getMetadata(); + if (is_null($this->metadataReturnType)) { + return $any; + } + if (is_null($any) || is_null($any->getValue())) { + return null; + } + $metadataReturnType = $this->metadataReturnType; + $metadata = new $metadataReturnType(); + $metadata->parse($any->getValue()); + return $metadata; + } } diff --git a/tests/ApiCallableTest.php b/tests/ApiCallableTest.php index 5dc2c4d5f..6e72f9575 100644 --- a/tests/ApiCallableTest.php +++ b/tests/ApiCallableTest.php @@ -40,6 +40,12 @@ use Google\GAX\Testing\MockStatus; use Google\GAX\Testing\MockRequest; use Google\GAX\Testing\MockResponse; +use google\longrunning\Operation; +use Google\Longrunning\OperationsClient; +use google\protobuf\Any; +use google\protobuf\EmptyC; +use google\rpc\Code; +use google\rpc\Status; class ApiCallableTest extends PHPUnit_Framework_TestCase { @@ -430,4 +436,303 @@ public function testCustomHeader() ]; $this->assertEquals($expectedMetadata, $actualCalls[0]['metadata']); } + + public static function createIncompleteOperationResponse($name, $metadataString = '') + { + $metadata = OperationResponseTest::createAny(OperationResponseTest::createStatus(Code::OK, $metadataString)); + $op = new Operation(); + $op->setName($name)->setMetadata($metadata)->setDone(false); + return $op; + } + + public static function createSuccessfulOperationResponse($name, $response, $metadataString = '') + { + $op = self::createIncompleteOperationResponse($name, $metadataString); + $op->setDone(true)->setResponse(OperationResponseTest::createAny($response)); + return $op; + } + + public static function createFailedOperationResponse($name, $code, $message, $metadataString = '') + { + $error = OperationResponseTest::createStatus($code, $message); + $op = self::createIncompleteOperationResponse($name, $metadataString); + $op->setDone(true)->setError($error); + return $op; + } + + public static function createOperationsClient($stub) + { + $client = new OperationsClient([ + 'createOperationsStubFunction' => function ($hostname, $opts) use ($stub) { + return $stub; + }, + ]); + return $client; + } + + public function testLongrunningSuccess() + { + $opName = 'operation/someop'; + + $request = null; + $result = OperationResponseTest::createStatus(Code::OK, 'someMessage'); + + $initialResponse = self::createIncompleteOperationResponse($opName, 'm1'); + $responseA = self::createIncompleteOperationResponse($opName, 'm2'); + $responseB = self::createSuccessfulOperationResponse($opName, $result, 'm3'); + $responseSequence = [ + [$responseA, new MockStatus(Grpc\STATUS_OK, '')], + [$responseB, new MockStatus(Grpc\STATUS_OK, '')], + ]; + $callStub = MockStub::createWithResponseSequence([[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); + $opStub = MockStub::createWithResponseSequence($responseSequence); + $opClient = self::createOperationsClient($opStub); + $descriptor = [ + 'operationsClient' => $opClient, + 'operationReturnType' => '\google\rpc\Status', + 'metadataReturnType' => '\google\rpc\Status', + ]; + $callSettings = new CallSettings(); + $apiCall = ApiCallable::createApiCall( + $callStub, 'takeAction', $callSettings, ['longRunningDescriptor' => $descriptor]); + + /* @var $response \Google\GAX\OperationResponse */ + $response = $apiCall($request, [], []); + + $results = [$response->getResult()]; + $errors = [$response->getError()]; + $metadataResponses = [$response->getMetadata()]; + $isDoneResponses = [$response->isDone()]; + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(0, count($opStub->actualCalls)); + + while (!$response->isDone()) { + $response->reload(); + $results[] = $response->getResult(); + $errors[] = $response->getError(); + $metadataResponses[] = $response->getMetadata(); + $isDoneResponses[] = $response->isDone(); + } + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(2, count($opStub->actualCalls)); + + $this->assertSame('takeAction', $callStub->actualCalls[0]['funcName']); + $this->assertSame('GetOperation', $opStub->actualCalls[0]['funcName']); + $this->assertSame('GetOperation', $opStub->actualCalls[1]['funcName']); + + $this->assertEquals([null, null, OperationResponseTest::createStatus(Code::OK, 'someMessage')], $results); + $this->assertEquals([null, null, null], $errors); + $this->assertEquals([ + OperationResponseTest::createStatus(Code::OK, 'm1'), + OperationResponseTest::createStatus(Code::OK, 'm2'), + OperationResponseTest::createStatus(Code::OK, 'm3') + ], $metadataResponses); + $this->assertEquals([false, false, true], $isDoneResponses); + } + + public function testLongrunningPollingSettings() + { + $opName = 'operation/someop'; + + $request = null; + $result = OperationResponseTest::createStatus(Code::OK, 'someMessage'); + + $initialResponse = self::createIncompleteOperationResponse($opName, 'm1'); + $responseA = self::createIncompleteOperationResponse($opName, 'm2'); + $responseB = self::createSuccessfulOperationResponse($opName, $result, 'm3'); + $responseSequence = [ + [$responseA, new MockStatus(Grpc\STATUS_OK, '')], + [$responseB, new MockStatus(Grpc\STATUS_OK, '')], + ]; + $callStub = MockStub::createWithResponseSequence([[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); + $opStub = MockStub::createWithResponseSequence($responseSequence); + $opClient = self::createOperationsClient($opStub); + $descriptor = [ + 'operationsClient' => $opClient, + 'operationReturnType' => '\google\rpc\Status', + 'metadataReturnType' => '\google\rpc\Status', + ]; + $callSettings = new CallSettings(); + $apiCall = ApiCallable::createApiCall( + $callStub, 'takeAction', $callSettings, ['longRunningDescriptor' => $descriptor]); + + /* @var $response \Google\GAX\OperationResponse */ + $response = $apiCall($request, [], []); + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(0, count($opStub->actualCalls)); + + $response->pollUntilComplete(null, ['pollingIntervalSeconds' => 0.1]); + + $this->assertTrue($response->isDone()); + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(2, count($opStub->actualCalls)); + + $this->assertSame('takeAction', $callStub->actualCalls[0]['funcName']); + $this->assertSame('GetOperation', $opStub->actualCalls[0]['funcName']); + $this->assertSame('GetOperation', $opStub->actualCalls[1]['funcName']); + + $this->assertEquals(OperationResponseTest::createStatus(Code::OK, 'someMessage'), $response->getResult()); + $this->assertNull($response->getError()); + $this->assertEquals(OperationResponseTest::createStatus(Code::OK, 'm3'), $response->getMetadata()); + } + + public function testLongrunningFailure() + { + $opName = 'operation/someop'; + + $request = null; + + $initialResponse = self::createIncompleteOperationResponse($opName, 'm1'); + $responseA = self::createIncompleteOperationResponse($opName, 'm2'); + $responseB = self::createFailedOperationResponse($opName, Code::UNKNOWN, 'someError', 'm3'); + $responseSequence = [ + [$responseA, new MockStatus(Grpc\STATUS_OK, '')], + [$responseB, new MockStatus(Grpc\STATUS_OK, '')], + ]; + $callStub = MockStub::createWithResponseSequence( + [[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); + $opStub = MockStub::createWithResponseSequence($responseSequence); + $opClient = self::createOperationsClient($opStub); + $descriptor = [ + 'operationsClient' => $opClient, + 'operationReturnType' => '\google\rpc\Status', + 'metadataReturnType' => '\google\rpc\Status', + ]; + $callSettings = new CallSettings(); + $apiCall = ApiCallable::createApiCall( + $callStub, 'takeAction', $callSettings, ['longRunningDescriptor' => $descriptor]); + + /* @var $response \Google\GAX\OperationResponse */ + $response = $apiCall($request, [], []); + + $results = [$response->getResult()]; + $errors = [$response->getError()]; + $metadataResponses = [$response->getMetadata()]; + $isDoneResponses = [$response->isDone()]; + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(0, count($opStub->actualCalls)); + + while (!$response->isDone()) { + $response->reload(); + $results[] = $response->getResult(); + $errors[] = $response->getError(); + $metadataResponses[] = $response->getMetadata(); + $isDoneResponses[] = $response->isDone(); + } + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(2, count($opStub->actualCalls)); + + $this->assertSame('takeAction', $callStub->actualCalls[0]['funcName']); + $this->assertSame('GetOperation', $opStub->actualCalls[0]['funcName']); + $this->assertSame('GetOperation', $opStub->actualCalls[1]['funcName']); + + $this->assertEquals([null, null, null], $results); + $this->assertEquals([null, null, OperationResponseTest::createStatus(Code::UNKNOWN, 'someError')], $errors); + $this->assertEquals([ + OperationResponseTest::createStatus(Code::OK, 'm1'), + OperationResponseTest::createStatus(Code::OK, 'm2'), + OperationResponseTest::createStatus(Code::OK, 'm3') + ], $metadataResponses); + $this->assertEquals([false, false, true], $isDoneResponses); + } + + public function testLongrunningCancel() + { + $opName = 'operation/someop'; + + $request = null; + + $initialResponse = self::createIncompleteOperationResponse($opName, 'm1'); + $responseA = self::createIncompleteOperationResponse($opName, 'm2'); + $responseB = self::createFailedOperationResponse($opName, Code::CANCELLED, 'someError', 'm3'); + $responseSequence = [ + [new EmptyC(), new MockStatus(Grpc\STATUS_OK, '')], + [$responseA, new MockStatus(Grpc\STATUS_OK, '')], + [$responseB, new MockStatus(Grpc\STATUS_OK, '')], + ]; + $callStub = MockStub::createWithResponseSequence([[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); + $opStub = MockStub::createWithResponseSequence($responseSequence); + $opClient = self::createOperationsClient($opStub); + $descriptor = [ + 'operationsClient' => $opClient, + 'operationReturnType' => '\google\rpc\Status', + 'metadataReturnType' => '\google\rpc\Status', + ]; + $callSettings = new CallSettings(); + $apiCall = ApiCallable::createApiCall( + $callStub, 'takeAction', $callSettings, ['longRunningDescriptor' => $descriptor]); + + /* @var $response \Google\GAX\OperationResponse */ + $response = $apiCall($request, [], []); + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(0, count($opStub->actualCalls)); + + $response->cancel(); + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(1, count($opStub->actualCalls)); + + while (!$response->isDone()) { + $response->reload(); + } + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(3, count($opStub->actualCalls)); + + $this->assertSame('takeAction', $callStub->actualCalls[0]['funcName']); + $this->assertSame('CancelOperation', $opStub->actualCalls[0]['funcName']); + $this->assertSame('GetOperation', $opStub->actualCalls[1]['funcName']); + $this->assertSame('GetOperation', $opStub->actualCalls[2]['funcName']); + + $this->assertNull($response->getResult()); + $this->assertEquals(OperationResponseTest::createStatus(Code::CANCELLED, 'someError'), $response->getError()); + $this->assertEquals(OperationResponseTest::createStatus(Code::OK, 'm3'), $response->getMetadata()); + } + + /** + * @expectedException \Google\GAX\ValidationException + * @expectedExceptionMessage Cannot call reload() on a deleted operation + */ + public function testLongrunningDelete() + { + $opName = 'operation/someop'; + + $request = null; + + $initialResponse = self::createIncompleteOperationResponse($opName, 'm1'); + $callStub = MockStub::createWithResponseSequence([[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); + $opStub = MockStub::createWithResponseSequence([[new EmptyC(), new MockStatus(Grpc\STATUS_OK, '')]]); + $opClient = self::createOperationsClient($opStub); + $descriptor = [ + 'operationsClient' => $opClient, + 'operationReturnType' => '\google\rpc\Status', + 'metadataReturnType' => '\google\rpc\Status', + ]; + $callSettings = new CallSettings(); + $apiCall = ApiCallable::createApiCall( + $callStub, 'takeAction', $callSettings, ['longRunningDescriptor' => $descriptor]); + + /* @var $response \Google\GAX\OperationResponse */ + $response = $apiCall($request, [], []); + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(0, count($opStub->actualCalls)); + + $response->delete(); + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(1, count($opStub->actualCalls)); + + $this->assertSame('takeAction', $callStub->actualCalls[0]['funcName']); + $this->assertSame('DeleteOperation', $opStub->actualCalls[0]['funcName']); + + $response->reload(); + } } diff --git a/tests/OperationResponseTest.php b/tests/OperationResponseTest.php new file mode 100644 index 000000000..2d37c7f4a --- /dev/null +++ b/tests/OperationResponseTest.php @@ -0,0 +1,128 @@ +assertSame($opName, $op->getName()); + $this->assertSame($opClient, $op->getOperationsClient()); + } + + public function testWithoutResponse() + { + $opName = 'operations/opname'; + $opClient = new OperationsClient(); + $op = new OperationResponse($opName, $opClient); + + $this->assertNull($op->getLastProtoResponse()); + $this->assertFalse($op->isDone()); + $this->assertNull($op->getResult()); + $this->assertNull($op->getError()); + $this->assertNull($op->getMetadata()); + } + + public function testWithResponse() + { + $opName = 'operations/opname'; + $opClient = new OperationsClient(); + $protoResponse = new Operation(); + $op = new OperationResponse($opName, $opClient, [ + 'lastProtoResponse' => $protoResponse, + ]); + + $this->assertSame($protoResponse, $op->getLastProtoResponse()); + $this->assertFalse($op->isDone()); + $this->assertNull($op->getResult()); + $this->assertNull($op->getError()); + $this->assertNull($op->getMetadata()); + + $response = self::createAny(self::createStatus(0, "response")); + $error = self::createStatus(2, "error"); + $metadata = self::createAny(self::createStatus(0, "metadata")); + + $protoResponse->setDone(true)->setResponse($response)->setMetadata($metadata); + $this->assertTrue($op->isDone()); + $this->assertSame($response, $op->getResult()); + $this->assertSame($metadata, $op->getMetadata()); + + $protoResponse->clearResponse()->setError($error); + $this->assertNull($op->getResult()); + $this->assertSame($error, $op->getError()); + } + + public function testWithOptions() + { + $opName = 'operations/opname'; + $opClient = new OperationsClient(); + $protoResponse = new Operation(); + $op = new OperationResponse($opName, $opClient, [ + 'operationReturnType' => '\google\rpc\Status', + 'metadataReturnType' => '\google\rpc\Status', + 'lastProtoResponse' => $protoResponse, + ]); + + $this->assertSame($protoResponse, $op->getLastProtoResponse()); + $this->assertFalse($op->isDone()); + $this->assertNull($op->getResult()); + $this->assertNull($op->getError()); + $this->assertNull($op->getMetadata()); + + $response = self::createAny(self::createStatus(0, "response")); + $metadata = self::createAny(self::createStatus(0, "metadata")); + + $protoResponse->setDone(true)->setResponse($response)->setMetadata($metadata); + $this->assertTrue($op->isDone()); + $this->assertEquals(self::createStatus(0, "response"), $op->getResult()); + $this->assertEquals(self::createStatus(0, "metadata"), $op->getMetadata()); + } + + public static function createAny($value) { + $any = new Any(); + return $any->setValue($value->serialize()); + } + + public static function createStatus($code, $message) { + $value = new Status(); + return $value->setCode($code)->setMessage($message); + } +} diff --git a/tests/PagedListResponseTest.php b/tests/PagedListResponseTest.php index db50febb6..f04e7d2d3 100644 --- a/tests/PagedListResponseTest.php +++ b/tests/PagedListResponseTest.php @@ -39,7 +39,8 @@ class PagedListResponseTest extends PHPUnit_Framework_TestCase { - public function testNextPageToken() { + public function testNextPageToken() + { $mockRequest = MockRequest::createPageStreamingRequest('mockToken'); $descriptor = new PageStreamingDescriptor([ 'requestPageTokenField' => 'pageToken', diff --git a/tests/mocks/MockStub.php b/tests/mocks/MockStub.php index 908af84c1..db65c9ff0 100644 --- a/tests/mocks/MockStub.php +++ b/tests/mocks/MockStub.php @@ -66,12 +66,20 @@ public static function createWithResponseSequence($sequence) return $stub; } - public function takeAction($request, $metadata = array(), $options = array()) + public function __call($name, $arguments) + { + $newArgs = array_merge([$name], $arguments); + return call_user_func_array(array($this, 'handleCall'), $newArgs); + } + + private function handleCall($funcName, $request, $metadata = array(), $options = array()) { $actualCall = [ + 'funcName' => $funcName, 'request' => $request, 'metadata' => $metadata, - 'options' => $options]; + 'options' => $options, + ]; array_push($this->actualCalls, $actualCall); if (count($this->responseSequence) == 1) { return new MockGrpcCall($this->responseSequence[0]); From 7f6c83823f00f947698de4fb845c256178f32bad Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 20 Dec 2016 13:44:06 -0800 Subject: [PATCH 12/24] Update docs, composer --- composer.json | 2 +- src/OperationResponse.php | 13 +++++++++++++ tests/ApiCallableTest.php | 2 -- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 62069bfed..6bc5bb1ba 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "autoload": { "psr-4": { "Google\\GAX\\":"src/", - "Google\\Longrunning\\":"src/longrunning/" + "Google\\Longrunning\\":"src/Longrunning/" }, "classmap": ["src/generated", "src/generated/well-known-types"] } diff --git a/src/OperationResponse.php b/src/OperationResponse.php index ffbc6c347..6db5652cc 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -27,6 +27,19 @@ class OperationResponse private $lastProtoResponse; private $deleted = false; + /** + * OperationResponse constructor. + * + * @param string $operationName + * @param \Google\Longrunning\OperationsClient $operationsClient + * @param array $options { + * Optional. Options for configuring the Operation response object. + * + * @type string $operationReturnType The return type of the longrunning operation. + * @type string $metadataReturnType The type of the metadata returned in the Operation response. + * @type \google\longrunning\Operation $lastProtoResponse A response already received from the server. + * } + */ public function __construct($operationName, $operationsClient, $options = []) { $this->operationName = $operationName; diff --git a/tests/ApiCallableTest.php b/tests/ApiCallableTest.php index 6e72f9575..0c982b181 100644 --- a/tests/ApiCallableTest.php +++ b/tests/ApiCallableTest.php @@ -42,10 +42,8 @@ use Google\GAX\Testing\MockResponse; use google\longrunning\Operation; use Google\Longrunning\OperationsClient; -use google\protobuf\Any; use google\protobuf\EmptyC; use google\rpc\Code; -use google\rpc\Status; class ApiCallableTest extends PHPUnit_Framework_TestCase { From 7c899c5e4de0975173ae89580468c13927708f44 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 20 Dec 2016 13:50:42 -0800 Subject: [PATCH 13/24] remove handler from pollUntilComplete --- src/OperationResponse.php | 22 ++++++++-------------- tests/ApiCallableTest.php | 2 +- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/OperationResponse.php b/src/OperationResponse.php index 6db5652cc..3cf9b1e81 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -80,27 +80,25 @@ public function getName() /** * Poll the server in a loop until the operation is complete. * - * If the $handler callable is not null, then call $handler (with $this as argument) and return - * the result. Otherwise, return true if the operation completed successfully, otherwise return + * Return true if the operation completed successfully, otherwise return * false. * * The $pollSettings optional argument can be used to control the polling loop. * - * @param callable $handler An optional callable which accepts $this as its argument. If not - * null, $handler will be called once the operation is complete (when $this->isDone() is true). - * @param array $pollSettings { - * An optional array to control the polling behavior. - * @type float $pollingIntervalSeconds The polling interval to use, in seconds. + * @param array $options { + * Options for configuring the polling behaviour. + * + * @type float $pollingIntervalSeconds The polling interval to use, in seconds. * Default: 1.0 * } - * @return mixed + * @return boolean Indicates if the operation completed successfully. */ - public function pollUntilComplete($handler = null, $pollSettings = []) + public function pollUntilComplete($options = []) { $defaultPollSettings = [ 'pollingIntervalSeconds' => $this::DEFAULT_POLLING_INTERVAL, ]; - $pollSettings = array_merge($defaultPollSettings, $pollSettings); + $pollSettings = array_merge($defaultPollSettings, $options); $pollingIntervalMicros = $pollSettings['pollingIntervalSeconds'] * 1000000; @@ -109,10 +107,6 @@ public function pollUntilComplete($handler = null, $pollSettings = []) $this->reload(); } - if (!is_null($handler)) { - return $handler($this); - } - return $this->lastProtoResponse->hasResponse(); } diff --git a/tests/ApiCallableTest.php b/tests/ApiCallableTest.php index 0c982b181..adda1f806 100644 --- a/tests/ApiCallableTest.php +++ b/tests/ApiCallableTest.php @@ -562,7 +562,7 @@ public function testLongrunningPollingSettings() $this->assertSame(1, count($callStub->actualCalls)); $this->assertSame(0, count($opStub->actualCalls)); - $response->pollUntilComplete(null, ['pollingIntervalSeconds' => 0.1]); + $response->pollUntilComplete(['pollingIntervalSeconds' => 0.1]); $this->assertTrue($response->isDone()); From ff8a73465c28a922296d04b262d58bf4373c477d Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 20 Dec 2016 13:53:19 -0800 Subject: [PATCH 14/24] Doc fixes --- src/GrpcCredentialsHelper.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GrpcCredentialsHelper.php b/src/GrpcCredentialsHelper.php index 8774ddc35..712b2e76f 100644 --- a/src/GrpcCredentialsHelper.php +++ b/src/GrpcCredentialsHelper.php @@ -56,7 +56,7 @@ class GrpcCredentialsHelper * will be passed as optional arguments to Google\Auth\FetchAuthTokenCache * when caching is enabled. * - * @var Google\Auth\CredentialsLoader $credentialsLoader + * @var \Google\Auth\CredentialsLoader $credentialsLoader * A user-created CredentialsLoader object. Defaults to using * ApplicationDefaultCredentials * @var boolean $enableCaching @@ -111,7 +111,7 @@ public function createCallCredentialsCallback() /** * Creates a gRPC client stub. * - * @param function $generatedCreateStub + * @param callable $generatedCreateStub * Function callback which must accept two arguments ($hostname, $opts) * and return an instance of the stub of the specific API to call. * Generally, this should just call the stub's constructor and return From 3cd9959bbdf6703eda002b866138887aeae9240b Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 20 Dec 2016 16:40:56 -0800 Subject: [PATCH 15/24] Regenerate OperationsClient without scopes and serviceAddress --- src/Longrunning/OperationsClient.php | 31 +++++++++++++--------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/src/Longrunning/OperationsClient.php b/src/Longrunning/OperationsClient.php index 1d0fb9c9c..f175be6b4 100644 --- a/src/Longrunning/OperationsClient.php +++ b/src/Longrunning/OperationsClient.php @@ -32,6 +32,7 @@ use Google\GAX\GrpcConstants; use Google\GAX\GrpcCredentialsHelper; use Google\GAX\PageStreamingDescriptor; +use Google\GAX\ValidationException; use google\longrunning\CancelOperationRequest; use google\longrunning\DeleteOperationRequest; use google\longrunning\GetOperationRequest; @@ -75,11 +76,6 @@ */ class OperationsClient { - /** - * The default address of the service. - */ - const SERVICE_ADDRESS = 'longrunning.googleapis.com'; - /** * The default port of the service. */ @@ -121,17 +117,16 @@ private static function getPageStreamingDescriptors() * Constructor. * * @param array $options { - * Optional. Options for configuring the service API wrapper. + * Required. Options for configuring the service API wrapper. Those options + * that must be provided are marked as Required. * - * @type string $serviceAddress The domain name of the API remote host. - * Default 'longrunning.googleapis.com'. + * @type string $serviceAddress Required. The domain name of the API remote host. * @type mixed $port The port on which to connect to the remote host. Default 443. - * @type Grpc\ChannelCredentials $sslCreds + * @type \Grpc\ChannelCredentials $sslCreds * A `ChannelCredentials` for use with an SSL-enabled channel. * Default: a credentials object returned from - * Grpc\ChannelCredentials::createSsl() - * @type array $scopes A string array of scopes to use when acquiring credentials. - * Default the scopes for the Google Long Running Operations API. + * \Grpc\ChannelCredentials::createSsl() + * @type array $scopes Required. A string array of scopes to use when acquiring credentials. * @type array $retryingOverride * An associative array of string => RetryOptions, where the keys * are method names (e.g. 'createFoo'), that overrides default retrying @@ -144,19 +139,21 @@ private static function getPageStreamingDescriptors() * @type string $appName The codename of the calling service. Default 'gax'. * @type string $appVersion The version of the calling service. * Default: the current version of GAX. - * @type Google\Auth\CredentialsLoader $credentialsLoader + * @type \Google\Auth\CredentialsLoader $credentialsLoader * A CredentialsLoader object created using the * Google\Auth library. * } */ public function __construct($options = []) { - $defaultScopes = [ - ]; + if (!array_key_exists('serviceAddress', $options)) { + throw new ValidationException("The 'serviceAddress' option must be provided."); + } + if (!array_key_exists('scopes', $options)) { + throw new ValidationException("The 'scopes' option must be provided."); + } $defaultOptions = [ - 'serviceAddress' => self::SERVICE_ADDRESS, 'port' => self::DEFAULT_SERVICE_PORT, - 'scopes' => $defaultScopes, 'retryingOverride' => null, 'timeoutMillis' => self::DEFAULT_TIMEOUT_MILLIS, 'appName' => 'gax', From ec982e2ad3d9ce37bb67e87cbd6cbec6d0a91baa Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 20 Dec 2016 16:55:16 -0800 Subject: [PATCH 16/24] Update tests to set serviceAddress and scopes in OperationsClient --- tests/ApiCallableTest.php | 20 +++++--------------- tests/OperationResponseTest.php | 20 ++++++++++++++++---- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/tests/ApiCallableTest.php b/tests/ApiCallableTest.php index adda1f806..1ba8fe728 100644 --- a/tests/ApiCallableTest.php +++ b/tests/ApiCallableTest.php @@ -458,16 +458,6 @@ public static function createFailedOperationResponse($name, $code, $message, $me return $op; } - public static function createOperationsClient($stub) - { - $client = new OperationsClient([ - 'createOperationsStubFunction' => function ($hostname, $opts) use ($stub) { - return $stub; - }, - ]); - return $client; - } - public function testLongrunningSuccess() { $opName = 'operation/someop'; @@ -484,7 +474,7 @@ public function testLongrunningSuccess() ]; $callStub = MockStub::createWithResponseSequence([[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); $opStub = MockStub::createWithResponseSequence($responseSequence); - $opClient = self::createOperationsClient($opStub); + $opClient = OperationResponseTest::createOperationsClient($opStub); $descriptor = [ 'operationsClient' => $opClient, 'operationReturnType' => '\google\rpc\Status', @@ -546,7 +536,7 @@ public function testLongrunningPollingSettings() ]; $callStub = MockStub::createWithResponseSequence([[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); $opStub = MockStub::createWithResponseSequence($responseSequence); - $opClient = self::createOperationsClient($opStub); + $opClient = OperationResponseTest::createOperationsClient($opStub); $descriptor = [ 'operationsClient' => $opClient, 'operationReturnType' => '\google\rpc\Status', @@ -594,7 +584,7 @@ public function testLongrunningFailure() $callStub = MockStub::createWithResponseSequence( [[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); $opStub = MockStub::createWithResponseSequence($responseSequence); - $opClient = self::createOperationsClient($opStub); + $opClient = OperationResponseTest::createOperationsClient($opStub); $descriptor = [ 'operationsClient' => $opClient, 'operationReturnType' => '\google\rpc\Status', @@ -656,7 +646,7 @@ public function testLongrunningCancel() ]; $callStub = MockStub::createWithResponseSequence([[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); $opStub = MockStub::createWithResponseSequence($responseSequence); - $opClient = self::createOperationsClient($opStub); + $opClient = OperationResponseTest::createOperationsClient($opStub); $descriptor = [ 'operationsClient' => $opClient, 'operationReturnType' => '\google\rpc\Status', @@ -707,7 +697,7 @@ public function testLongrunningDelete() $initialResponse = self::createIncompleteOperationResponse($opName, 'm1'); $callStub = MockStub::createWithResponseSequence([[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); $opStub = MockStub::createWithResponseSequence([[new EmptyC(), new MockStatus(Grpc\STATUS_OK, '')]]); - $opClient = self::createOperationsClient($opStub); + $opClient = OperationResponseTest::createOperationsClient($opStub); $descriptor = [ 'operationsClient' => $opClient, 'operationReturnType' => '\google\rpc\Status', diff --git a/tests/OperationResponseTest.php b/tests/OperationResponseTest.php index 2d37c7f4a..5885e7fdc 100644 --- a/tests/OperationResponseTest.php +++ b/tests/OperationResponseTest.php @@ -41,7 +41,7 @@ class OperationResponseTest extends PHPUnit_Framework_TestCase public function testBasic() { $opName = 'operations/opname'; - $opClient = new OperationsClient(); + $opClient = self::createOperationsClient(); $op = new OperationResponse($opName, $opClient); $this->assertSame($opName, $op->getName()); @@ -51,7 +51,7 @@ public function testBasic() public function testWithoutResponse() { $opName = 'operations/opname'; - $opClient = new OperationsClient(); + $opClient = self::createOperationsClient(); $op = new OperationResponse($opName, $opClient); $this->assertNull($op->getLastProtoResponse()); @@ -64,7 +64,7 @@ public function testWithoutResponse() public function testWithResponse() { $opName = 'operations/opname'; - $opClient = new OperationsClient(); + $opClient = self::createOperationsClient(); $protoResponse = new Operation(); $op = new OperationResponse($opName, $opClient, [ 'lastProtoResponse' => $protoResponse, @@ -93,7 +93,7 @@ public function testWithResponse() public function testWithOptions() { $opName = 'operations/opname'; - $opClient = new OperationsClient(); + $opClient = self::createOperationsClient(); $protoResponse = new Operation(); $op = new OperationResponse($opName, $opClient, [ 'operationReturnType' => '\google\rpc\Status', @@ -125,4 +125,16 @@ public static function createStatus($code, $message) { $value = new Status(); return $value->setCode($code)->setMessage($message); } + + public static function createOperationsClient($stub = null) + { + $client = new OperationsClient([ + 'createOperationsStubFunction' => function ($hostname, $opts) use ($stub) { + return $stub; + }, + 'serviceAddress' => '', + 'scopes' => [], + ]); + return $client; + } } From 59c746db444eee7f5154f00740e550dc9b38aa4f Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Thu, 22 Dec 2016 13:19:17 -0800 Subject: [PATCH 17/24] Address PR comments --- src/OperationResponse.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/OperationResponse.php b/src/OperationResponse.php index 3cf9b1e81..c87f22a3b 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -83,15 +83,14 @@ public function getName() * Return true if the operation completed successfully, otherwise return * false. * - * The $pollSettings optional argument can be used to control the polling loop. - * * @param array $options { * Options for configuring the polling behaviour. * * @type float $pollingIntervalSeconds The polling interval to use, in seconds. * Default: 1.0 * } - * @return boolean Indicates if the operation completed successfully. + * @return bool Indicates if the operation completed successfully. + * @throws ApiException If the API call fails. */ public function pollUntilComplete($options = []) { @@ -112,6 +111,9 @@ public function pollUntilComplete($options = []) /** * Reload the status of the operation with a request to the service. + * + * @throws ApiException If the API call fails. + * @throws ValidationException If called on a deleted operation. */ public function reload() { @@ -159,7 +161,7 @@ public function getError() } /** - * @return \google\longrunning\Operation The last Operation object received from the server. + * @return \google\longrunning\Operation|null The last Operation object received from the server. */ public function getLastProtoResponse() { @@ -185,6 +187,8 @@ public function getOperationsClient() * On successful cancellation, the operation is not deleted; instead, it becomes * an operation with a getError() value with a \google\rpc\Status code of 1, * corresponding to \google\rpc\Code::CANCELLED. + * + * @throws ApiException If the API call fails. */ public function cancel() { @@ -196,6 +200,8 @@ public function cancel() * no longer interested in the operation result. It does not cancel the operation. * If the server doesn't support this method, it will throw an ApiException with * code google\rpc\Code::UNIMPLEMENTED. + * + * @throws ApiException If the API call fails. */ public function delete() { From a12e50fa102a5820fbd001eb1e05ed973957a40d Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Thu, 22 Dec 2016 13:55:28 -0800 Subject: [PATCH 18/24] Add max duration setting to pollUntilComplete --- src/OperationResponse.php | 20 +++++++++----- tests/ApiCallableTest.php | 56 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 67 insertions(+), 9 deletions(-) diff --git a/src/OperationResponse.php b/src/OperationResponse.php index c87f22a3b..98b496bcb 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -80,33 +80,41 @@ public function getName() /** * Poll the server in a loop until the operation is complete. * - * Return true if the operation completed successfully, otherwise return - * false. + * Return true if the operation completed, otherwise return false. If the + * $options['maxPollingDuration'] setting is not set (or set <= 0.0) then + * pollUntilComplete will continue polling until the operation completes, + * and therefore will always return true. * * @param array $options { * Options for configuring the polling behaviour. * * @type float $pollingIntervalSeconds The polling interval to use, in seconds. * Default: 1.0 + * @type float $maxPollingDurationSeconds The maximum amount of time to continue polling. + * Default: 0.0 (no maximum) * } - * @return bool Indicates if the operation completed successfully. - * @throws ApiException If the API call fails. + * @return bool Indicates if the operation completed. + * @throws ApiException If an API call fails. */ public function pollUntilComplete($options = []) { $defaultPollSettings = [ 'pollingIntervalSeconds' => $this::DEFAULT_POLLING_INTERVAL, + 'maxPollingDurationSeconds' => 0.0, ]; $pollSettings = array_merge($defaultPollSettings, $options); $pollingIntervalMicros = $pollSettings['pollingIntervalSeconds'] * 1000000; + $maxPollingDuration = $pollSettings['maxPollingDurationSeconds']; - while (!$this->isDone()) { + $hasMaxPollingDuration = $maxPollingDuration > 0.0; + $endTime = microtime(true) + $maxPollingDuration; + while (!$this->isDone() && (!$hasMaxPollingDuration || microtime(true) < $endTime)) { usleep($pollingIntervalMicros); $this->reload(); } - return $this->lastProtoResponse->hasResponse(); + return $this->isDone(); } /** diff --git a/tests/ApiCallableTest.php b/tests/ApiCallableTest.php index 1ba8fe728..a6ebcfefe 100644 --- a/tests/ApiCallableTest.php +++ b/tests/ApiCallableTest.php @@ -520,7 +520,7 @@ public function testLongrunningSuccess() $this->assertEquals([false, false, true], $isDoneResponses); } - public function testLongrunningPollingSettings() + public function testLongrunningPollingInterval() { $opName = 'operation/someop'; @@ -552,8 +552,8 @@ public function testLongrunningPollingSettings() $this->assertSame(1, count($callStub->actualCalls)); $this->assertSame(0, count($opStub->actualCalls)); - $response->pollUntilComplete(['pollingIntervalSeconds' => 0.1]); - + $complete = $response->pollUntilComplete(['pollingIntervalSeconds' => 0.1]); + $this->assertTrue($complete); $this->assertTrue($response->isDone()); $this->assertSame(1, count($callStub->actualCalls)); @@ -568,6 +568,56 @@ public function testLongrunningPollingSettings() $this->assertEquals(OperationResponseTest::createStatus(Code::OK, 'm3'), $response->getMetadata()); } + public function testLongrunningMaxPollingDuration() + { + $opName = 'operation/someop'; + + $request = null; + $result = OperationResponseTest::createStatus(Code::OK, 'someMessage'); + + $initialResponse = self::createIncompleteOperationResponse($opName, 'm1'); + $responseA = self::createIncompleteOperationResponse($opName, 'm2'); + $responseB = self::createIncompleteOperationResponse($opName, 'm3'); + $responseSequence = [ + [$responseA, new MockStatus(Grpc\STATUS_OK, '')], + [$responseB, new MockStatus(Grpc\STATUS_OK, '')], + ]; + $callStub = MockStub::createWithResponseSequence([[$initialResponse, new MockStatus(Grpc\STATUS_OK, '')]]); + $opStub = MockStub::createWithResponseSequence($responseSequence); + $opClient = OperationResponseTest::createOperationsClient($opStub); + $descriptor = [ + 'operationsClient' => $opClient, + 'operationReturnType' => '\google\rpc\Status', + 'metadataReturnType' => '\google\rpc\Status', + ]; + $callSettings = new CallSettings(); + $apiCall = ApiCallable::createApiCall( + $callStub, 'takeAction', $callSettings, ['longRunningDescriptor' => $descriptor]); + + /* @var $response \Google\GAX\OperationResponse */ + $response = $apiCall($request, [], []); + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(0, count($opStub->actualCalls)); + + $complete = $response->pollUntilComplete([ + 'pollingIntervalSeconds' => 0.1, + 'maxPollingDurationSeconds' => 0.15, + ]); + $this->assertFalse($complete); + $this->assertFalse($response->isDone()); + + $this->assertSame(1, count($callStub->actualCalls)); + $this->assertSame(2, count($opStub->actualCalls)); + + $this->assertSame('takeAction', $callStub->actualCalls[0]['funcName']); + $this->assertSame('GetOperation', $opStub->actualCalls[0]['funcName']); + + $this->assertNull($response->getResult()); + $this->assertNull($response->getError()); + $this->assertEquals(OperationResponseTest::createStatus(Code::OK, 'm3'), $response->getMetadata()); + } + public function testLongrunningFailure() { $opName = 'operation/someop'; From 3eef2142473e03804e3d6d02f4e36f8172dc356b Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Thu, 22 Dec 2016 16:35:58 -0800 Subject: [PATCH 19/24] Add operationSucceeded and operationFailed functions --- src/OperationResponse.php | 41 +++++++++++++++++++++++++-------- tests/OperationResponseTest.php | 8 +++++++ 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/OperationResponse.php b/src/OperationResponse.php index 98b496bcb..693cf6337 100644 --- a/src/OperationResponse.php +++ b/src/OperationResponse.php @@ -62,9 +62,31 @@ public function __construct($operationName, $operationsClient, $options = []) */ public function isDone() { - return (is_null($this->lastProtoResponse) || is_null($this->lastProtoResponse->getDone())) - ? false - : $this->lastProtoResponse->getDone(); + return (is_null($this->lastProtoResponse) || is_null($this->lastProtoResponse->getDone())) + ? false + : $this->lastProtoResponse->getDone(); + } + + /** + * Check whether the operation completed successfully. If the operation is not complete, or if the operation + * failed, return false. + * + * @return bool + */ + public function operationSucceeded() + { + return !is_null($this->getResult()); + } + + /** + * Check whether the operation failed. If the operation is not complete, or if the operation + * succeeded, return false. + * + * @return bool + */ + public function operationFailed() + { + return !is_null($this->getError()); } /** @@ -133,9 +155,9 @@ public function reload() } /** - * Return the result of the operation. If the operation is not complete, or if the operation - * failed, return null. - * @return mixed The result of the operation, or null if the operation failed or is not complete + * Return the result of the operation. If operationSucceeded() is false, return null. + * + * @return mixed|null The result of the operation, or null if operationSucceeded() is false */ public function getResult() { @@ -154,11 +176,10 @@ public function getResult() } /** - * If the operation failed, return the status. If the operation succeeded or is not complete, - * return null. + * If the operation failed, return the status. If operationFailed() is false, return null. * - * @return \google\rpc\Status|null The status of the operation in case of failure, otherwise - * null. + * @return \google\rpc\Status|null The status of the operation in case of failure, or null if + * operationFailed() is false. */ public function getError() { diff --git a/tests/OperationResponseTest.php b/tests/OperationResponseTest.php index 5885e7fdc..c64f8ea25 100644 --- a/tests/OperationResponseTest.php +++ b/tests/OperationResponseTest.php @@ -59,6 +59,8 @@ public function testWithoutResponse() $this->assertNull($op->getResult()); $this->assertNull($op->getError()); $this->assertNull($op->getMetadata()); + $this->assertFalse($op->operationSucceeded()); + $this->assertFalse($op->operationFailed()); } public function testWithResponse() @@ -75,6 +77,8 @@ public function testWithResponse() $this->assertNull($op->getResult()); $this->assertNull($op->getError()); $this->assertNull($op->getMetadata()); + $this->assertFalse($op->operationSucceeded()); + $this->assertFalse($op->operationFailed()); $response = self::createAny(self::createStatus(0, "response")); $error = self::createStatus(2, "error"); @@ -84,10 +88,14 @@ public function testWithResponse() $this->assertTrue($op->isDone()); $this->assertSame($response, $op->getResult()); $this->assertSame($metadata, $op->getMetadata()); + $this->assertTrue($op->operationSucceeded()); + $this->assertFalse($op->operationFailed()); $protoResponse->clearResponse()->setError($error); $this->assertNull($op->getResult()); $this->assertSame($error, $op->getError()); + $this->assertFalse($op->operationSucceeded()); + $this->assertTrue($op->operationFailed()); } public function testWithOptions() From 774079a464836804f488afabb00be7ea72e69fbd Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 27 Dec 2016 09:40:57 -0800 Subject: [PATCH 20/24] Update OperationsClient namespace --- src/Longrunning/OperationsClient.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Longrunning/OperationsClient.php b/src/Longrunning/OperationsClient.php index f175be6b4..71b98ee71 100644 --- a/src/Longrunning/OperationsClient.php +++ b/src/Longrunning/OperationsClient.php @@ -24,7 +24,7 @@ * backwards compatibility. */ -namespace Google\Longrunning; +namespace Google\GAX\Longrunning; use Google\GAX\AgentHeaderDescriptor; use Google\GAX\ApiCallable; From c84d4120af2d268abaef2885f69335b887ea7cb1 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 27 Dec 2016 09:48:35 -0800 Subject: [PATCH 21/24] Update namespace in import --- tests/OperationResponseTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OperationResponseTest.php b/tests/OperationResponseTest.php index c64f8ea25..080b9211a 100644 --- a/tests/OperationResponseTest.php +++ b/tests/OperationResponseTest.php @@ -32,7 +32,7 @@ use Google\GAX\OperationResponse; use google\longrunning\Operation; -use Google\Longrunning\OperationsClient; +use Google\GAX\Longrunning\OperationsClient; use google\protobuf\Any; use google\rpc\Status; From aceb645e0008250e31814070c10347a1d3c40a18 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Tue, 27 Dec 2016 15:27:43 -0800 Subject: [PATCH 22/24] Change LRO namespace to camel case - Change LRO namespace to camel case - Add license --- composer.json | 3 +- .../OperationsClient.php | 34 ++++++++++++++----- .../resources/operations_client_config.json | 0 src/OperationResponse.php | 34 +++++++++++++++++-- 4 files changed, 58 insertions(+), 13 deletions(-) rename src/{Longrunning => LongRunning}/OperationsClient.php (91%) rename src/{Longrunning => LongRunning}/resources/operations_client_config.json (100%) diff --git a/composer.json b/composer.json index 6bc5bb1ba..390d3f1ba 100644 --- a/composer.json +++ b/composer.json @@ -17,8 +17,7 @@ }, "autoload": { "psr-4": { - "Google\\GAX\\":"src/", - "Google\\Longrunning\\":"src/Longrunning/" + "Google\\GAX\\":"src/" }, "classmap": ["src/generated", "src/generated/well-known-types"] } diff --git a/src/Longrunning/OperationsClient.php b/src/LongRunning/OperationsClient.php similarity index 91% rename from src/Longrunning/OperationsClient.php rename to src/LongRunning/OperationsClient.php index 71b98ee71..2fb1545f9 100644 --- a/src/Longrunning/OperationsClient.php +++ b/src/LongRunning/OperationsClient.php @@ -1,16 +1,32 @@ Date: Tue, 27 Dec 2016 15:37:46 -0800 Subject: [PATCH 23/24] Camel case Longrunning namespace --- tests/OperationResponseTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OperationResponseTest.php b/tests/OperationResponseTest.php index 080b9211a..259629b96 100644 --- a/tests/OperationResponseTest.php +++ b/tests/OperationResponseTest.php @@ -32,7 +32,7 @@ use Google\GAX\OperationResponse; use google\longrunning\Operation; -use Google\GAX\Longrunning\OperationsClient; +use Google\GAX\LongRunning\OperationsClient; use google\protobuf\Any; use google\rpc\Status; From 545efa85764fbfaf296534406d1d3a55ca7815f7 Mon Sep 17 00:00:00 2001 From: Michael Bausor Date: Thu, 29 Dec 2016 15:43:51 -0800 Subject: [PATCH 24/24] Edit OperationsClient comments --- src/LongRunning/OperationsClient.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/LongRunning/OperationsClient.php b/src/LongRunning/OperationsClient.php index 2fb1545f9..4ae7a9260 100644 --- a/src/LongRunning/OperationsClient.php +++ b/src/LongRunning/OperationsClient.php @@ -64,7 +64,9 @@ * operation resource, or pass the operation resource to another API (such as * Google Cloud Pub/Sub API) to receive the response. Any API service that * returns long-running operations should implement the `Operations` interface - * so developers can have a consistent client experience. + * (see https://github.com/googleapis/googleapis/blob/master/google/longrunning/operations.proto#L40) + * so developers can have a consistent client experience. This class provides + * methods to make calls to the `Operations` interface of an API service. * * EXPERIMENTAL: this client library class has not yet been declared beta. This class may change * more frequently than those which have been declared beta or 1.0, including changes which break