123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937 |
- <?php
- /*
- * Copyright 2018 Google LLC
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- * * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- namespace Google\ApiCore;
- use Google\ApiCore\LongRunning\OperationsClient;
- use Google\ApiCore\Middleware\CredentialsWrapperMiddleware;
- use Google\ApiCore\Middleware\FixedHeaderMiddleware;
- use Google\ApiCore\Middleware\OperationsMiddleware;
- use Google\ApiCore\Middleware\OptionsFilterMiddleware;
- use Google\ApiCore\Middleware\PagedMiddleware;
- use Google\ApiCore\Middleware\RetryMiddleware;
- use Google\ApiCore\Options\CallOptions;
- use Google\ApiCore\Options\ClientOptions;
- use Google\ApiCore\Options\TransportOptions;
- use Google\ApiCore\Transport\GrpcFallbackTransport;
- use Google\ApiCore\Transport\GrpcTransport;
- use Google\ApiCore\Transport\RestTransport;
- use Google\ApiCore\Transport\TransportInterface;
- use Google\Auth\FetchAuthTokenInterface;
- use Google\LongRunning\Operation;
- use Google\Protobuf\Internal\Message;
- use GuzzleHttp\Promise\PromiseInterface;
- /**
- * Common functions used to work with various clients.
- *
- * @internal
- */
- trait GapicClientTrait
- {
- use ClientOptionsTrait;
- use ValidationTrait {
- ValidationTrait::validate as traitValidate;
- }
- use GrpcSupportTrait;
- private ?TransportInterface $transport = null;
- private ?CredentialsWrapper $credentialsWrapper = null;
- /** @var RetrySettings[] $retrySettings */
- private array $retrySettings = [];
- private string $serviceName = '';
- private array $agentHeader = [];
- private array $descriptors = [];
- /** @var array<callable> $middlewareCallables */
- private array $middlewareCallables = [];
- private array $transportCallMethods = [
- Call::UNARY_CALL => 'startUnaryCall',
- Call::BIDI_STREAMING_CALL => 'startBidiStreamingCall',
- Call::CLIENT_STREAMING_CALL => 'startClientStreamingCall',
- Call::SERVER_STREAMING_CALL => 'startServerStreamingCall',
- ];
- private bool $backwardsCompatibilityMode;
- /**
- * Add a middleware to the call stack by providing a callable which will be
- * invoked at the start of each call, and will return an instance of
- * {@see MiddlewareInterface} when invoked.
- *
- * The callable must have the following method signature:
- *
- * callable(MiddlewareInterface): MiddlewareInterface
- *
- * An implementation may look something like this:
- * ```
- * $client->addMiddleware(function (MiddlewareInterface $handler) {
- * return new class ($handler) implements MiddlewareInterface {
- * public function __construct(private MiddlewareInterface $handler) {
- * }
- *
- * public function __invoke(Call $call, array $options) {
- * // modify call and options (pre-request)
- * $response = ($this->handler)($call, $options);
- * // modify the response (post-request)
- * return $response;
- * }
- * };
- * });
- * ```
- *
- * @param callable $middlewareCallable A callable which returns an instance
- * of {@see MiddlewareInterface} when invoked with a
- * MiddlewareInterface instance as its first argument.
- * @return void
- */
- public function addMiddleware(callable $middlewareCallable): void
- {
- $this->middlewareCallables[] = $middlewareCallable;
- }
- /**
- * Initiates an orderly shutdown in which preexisting calls continue but new
- * calls are immediately cancelled.
- *
- * @experimental
- */
- public function close()
- {
- $this->transport->close();
- }
- /**
- * Get the transport for the client. This method is protected to support
- * use by customized clients.
- *
- * @access private
- * @return TransportInterface
- */
- protected function getTransport()
- {
- return $this->transport;
- }
- /**
- * Get the credentials for the client. This method is protected to support
- * use by customized clients.
- *
- * @access private
- * @return CredentialsWrapper
- */
- protected function getCredentialsWrapper()
- {
- return $this->credentialsWrapper;
- }
- /**
- * Configures the GAPIC client based on an array of options.
- *
- * @param array $options {
- * An array of required and optional arguments.
- *
- * @type string $apiEndpoint
- * The address of the API remote host, for example "example.googleapis.com. May also
- * include the port, for example "example.googleapis.com:443"
- * @type bool $disableRetries
- * Determines whether or not retries defined by the client configuration should be
- * disabled. Defaults to `false`.
- * @type string|array $clientConfig
- * Client method configuration, including retry settings. This option can be either a
- * path to a JSON file, or a PHP array containing the decoded JSON data.
- * By default this settings points to the default client config file, which is provided
- * in the resources folder.
- * @type string|array|FetchAuthTokenInterface|CredentialsWrapper $credentials
- * The credentials to be used by the client to authorize API calls. This option
- * accepts either a path to a credentials file, or a decoded credentials file as a
- * PHP array.
- * *Advanced usage*: In addition, this option can also accept a pre-constructed
- * \Google\Auth\FetchAuthTokenInterface object or \Google\ApiCore\CredentialsWrapper
- * object. Note that when one of these objects are provided, any settings in
- * $authConfig will be ignored.
- * @type array $credentialsConfig
- * Options used to configure credentials, including auth token caching, for the client.
- * For a full list of supporting configuration options, see
- * \Google\ApiCore\CredentialsWrapper::build.
- * @type string|TransportInterface $transport
- * The transport used for executing network requests. May be either the string `rest`,
- * `grpc`, or 'grpc-fallback'. Defaults to `grpc` if gRPC support is detected on the system.
- * *Advanced usage*: Additionally, it is possible to pass in an already instantiated
- * TransportInterface object. Note that when this objects is provided, any settings in
- * $transportConfig, and any `$apiEndpoint` setting, will be ignored.
- * @type array $transportConfig
- * Configuration options that will be used to construct the transport. Options for
- * each supported transport type should be passed in a key for that transport. For
- * example:
- * $transportConfig = [
- * 'grpc' => [...],
- * 'rest' => [...],
- * 'grpc-fallback' => [...],
- * ];
- * See the GrpcTransport::build and RestTransport::build
- * methods for the supported options.
- * @type string $versionFile
- * The path to a file which contains the current version of the client.
- * @type string $descriptorsConfigPath
- * The path to a descriptor configuration file.
- * @type string $serviceName
- * The name of the service.
- * @type string $libName
- * The name of the client application.
- * @type string $libVersion
- * The version of the client application.
- * @type string $gapicVersion
- * The code generator version of the GAPIC library.
- * @type callable $clientCertSource
- * A callable which returns the client cert as a string.
- * }
- * @throws ValidationException
- */
- private function setClientOptions(array $options)
- {
- // serviceAddress is now deprecated and acts as an alias for apiEndpoint
- if (isset($options['serviceAddress'])) {
- $options['apiEndpoint'] = $this->pluck('serviceAddress', $options, false);
- }
- $this->validateNotNull($options, [
- 'apiEndpoint',
- 'serviceName',
- 'descriptorsConfigPath',
- 'clientConfig',
- 'disableRetries',
- 'credentialsConfig',
- 'transportConfig',
- ]);
- $this->traitValidate($options, [
- 'credentials',
- 'transport',
- 'gapicVersion',
- 'libName',
- 'libVersion',
- ]);
- if ($this->isBackwardsCompatibilityMode()) {
- if (is_string($options['clientConfig'])) {
- // perform validation for V1 surfaces which is done in the
- // ClientOptions class for v2 surfaces.
- $options['clientConfig'] = json_decode(
- file_get_contents($options['clientConfig']),
- true
- );
- self::validateFileExists($options['descriptorsConfigPath']);
- }
- } else {
- // cast to ClientOptions for new surfaces only
- $options = new ClientOptions($options);
- }
- $this->serviceName = $options['serviceName'];
- $this->retrySettings = RetrySettings::load(
- $this->serviceName,
- $options['clientConfig'],
- $options['disableRetries']
- );
- $headerInfo = [
- 'libName' => $options['libName'],
- 'libVersion' => $options['libVersion'],
- 'gapicVersion' => $options['gapicVersion'],
- ];
- // Edge case: If the client has the gRPC extension installed, but is
- // a REST-only library, then the grpcVersion header should not be set.
- if ($this->transport instanceof GrpcTransport) {
- $headerInfo['grpcVersion'] = phpversion('grpc');
- } elseif ($this->transport instanceof RestTransport
- || $this->transport instanceof GrpcFallbackTransport) {
- $headerInfo['restVersion'] = Version::getApiCoreVersion();
- }
- $this->agentHeader = AgentHeader::buildAgentHeader($headerInfo);
- // Set "client_library_name" depending on client library surface being used
- $userAgentHeader = sprintf(
- 'gcloud-php-%s/%s',
- $this->isBackwardsCompatibilityMode() ? 'legacy' : 'new',
- $options['gapicVersion']
- );
- $this->agentHeader['User-Agent'] = [$userAgentHeader];
- self::validateFileExists($options['descriptorsConfigPath']);
- $descriptors = require($options['descriptorsConfigPath']);
- $this->descriptors = $descriptors['interfaces'][$this->serviceName];
- $this->credentialsWrapper = $this->createCredentialsWrapper(
- $options['credentials'],
- $options['credentialsConfig'],
- $options['universeDomain']
- );
- $transport = $options['transport'] ?: self::defaultTransport();
- $this->transport = $transport instanceof TransportInterface
- ? $transport
- : $this->createTransport(
- $options['apiEndpoint'],
- $transport,
- $options['transportConfig'],
- $options['clientCertSource']
- );
- }
- /**
- * @param string $apiEndpoint
- * @param string $transport
- * @param TransportOptions|array $transportConfig
- * @param callable $clientCertSource
- * @return TransportInterface
- * @throws ValidationException
- */
- private function createTransport(
- string $apiEndpoint,
- $transport,
- $transportConfig,
- callable $clientCertSource = null
- ) {
- if (!is_string($transport)) {
- throw new ValidationException(
- "'transport' must be a string, instead got:" .
- print_r($transport, true)
- );
- }
- $supportedTransports = self::supportedTransports();
- if (!in_array($transport, $supportedTransports)) {
- throw new ValidationException(sprintf(
- 'Unexpected transport option "%s". Supported transports: %s',
- $transport,
- implode(', ', $supportedTransports)
- ));
- }
- $configForSpecifiedTransport = $transportConfig[$transport] ?? [];
- if (is_array($configForSpecifiedTransport)) {
- $configForSpecifiedTransport['clientCertSource'] = $clientCertSource;
- } else {
- $configForSpecifiedTransport->setClientCertSource($clientCertSource);
- $configForSpecifiedTransport = $configForSpecifiedTransport->toArray();
- }
- switch ($transport) {
- case 'grpc':
- // Setting the user agent for gRPC requires special handling
- if (isset($this->agentHeader['User-Agent'])) {
- if ($configForSpecifiedTransport['stubOpts']['grpc.primary_user_agent'] ??= '') {
- $configForSpecifiedTransport['stubOpts']['grpc.primary_user_agent'] .= ' ';
- }
- $configForSpecifiedTransport['stubOpts']['grpc.primary_user_agent'] .=
- $this->agentHeader['User-Agent'][0];
- }
- return GrpcTransport::build($apiEndpoint, $configForSpecifiedTransport);
- case 'grpc-fallback':
- return GrpcFallbackTransport::build($apiEndpoint, $configForSpecifiedTransport);
- case 'rest':
- if (!isset($configForSpecifiedTransport['restClientConfigPath'])) {
- throw new ValidationException(
- "The 'restClientConfigPath' config is required for 'rest' transport."
- );
- }
- $restConfigPath = $configForSpecifiedTransport['restClientConfigPath'];
- return RestTransport::build($apiEndpoint, $restConfigPath, $configForSpecifiedTransport);
- default:
- throw new ValidationException(
- "Unexpected 'transport' option: $transport. " .
- "Supported values: ['grpc', 'rest', 'grpc-fallback']"
- );
- }
- }
- /**
- * @param array $options
- * @return OperationsClient
- */
- private function createOperationsClient(array $options)
- {
- $this->pluckArray([
- 'serviceName',
- 'clientConfig',
- 'descriptorsConfigPath',
- ], $options);
- // User-supplied operations client
- if ($operationsClient = $this->pluck('operationsClient', $options, false)) {
- return $operationsClient;
- }
- // operationsClientClass option
- $operationsClientClass = $this->pluck('operationsClientClass', $options, false)
- ?: OperationsCLient::class;
- return new $operationsClientClass($options);
- }
- /**
- * @return string
- */
- private static function defaultTransport()
- {
- return self::getGrpcDependencyStatus()
- ? 'grpc'
- : 'rest';
- }
- private function validateCallConfig(string $methodName)
- {
- // Ensure a method descriptor exists for the target method.
- if (!isset($this->descriptors[$methodName])) {
- throw new ValidationException("Requested method '$methodName' does not exist in descriptor configuration.");
- }
- $methodDescriptors = $this->descriptors[$methodName];
- // Ensure required descriptor configuration exists.
- if (!isset($methodDescriptors['callType'])) {
- throw new ValidationException("Requested method '$methodName' does not have a callType " .
- 'in descriptor configuration.');
- }
- $callType = $methodDescriptors['callType'];
- // Validate various callType specific configurations.
- if ($callType == Call::LONGRUNNING_CALL) {
- if (!isset($methodDescriptors['longRunning'])) {
- throw new ValidationException("Requested method '$methodName' does not have a longRunning config " .
- 'in descriptor configuration.');
- }
- // @TODO: check if the client implements `OperationsClientInterface` instead
- if (!method_exists($this, 'getOperationsClient')) {
- throw new ValidationException('Client missing required getOperationsClient ' .
- "for longrunning call '$methodName'");
- }
- } elseif ($callType == Call::PAGINATED_CALL) {
- if (!isset($methodDescriptors['pageStreaming'])) {
- throw new ValidationException("Requested method '$methodName' with callType PAGINATED_CALL does not " .
- 'have a pageStreaming in descriptor configuration.');
- }
- }
- // LRO are either Standard LRO response type or custom, which are handled by
- // startOperationCall, so no need to validate responseType for those callType.
- if ($callType != Call::LONGRUNNING_CALL) {
- if (!isset($methodDescriptors['responseType'])) {
- throw new ValidationException("Requested method '$methodName' does not have a responseType " .
- 'in descriptor configuration.');
- }
- }
- return $methodDescriptors;
- }
- /**
- * @param string $methodName
- * @param Message $request
- * @param array $optionalArgs {
- * Call Options
- *
- * @type array $headers [optional] key-value array containing headers
- * @type int $timeoutMillis [optional] the timeout in milliseconds for the call
- * @type array $transportOptions [optional] transport-specific call options
- * @type RetrySettings|array $retrySettings [optional] A retry settings override for the call.
- * }
- *
- * @experimental
- *
- * @return PromiseInterface
- */
- private function startAsyncCall(
- string $methodName,
- Message $request,
- array $optionalArgs = []
- ) {
- // Convert method name to the UpperCamelCase of RPC names from lowerCamelCase of GAPIC method names
- // in order to find the method in the descriptor config.
- $methodName = ucfirst($methodName);
- $methodDescriptors = $this->validateCallConfig($methodName);
- $callType = $methodDescriptors['callType'];
- switch ($callType) {
- case Call::PAGINATED_CALL:
- return $this->getPagedListResponseAsync(
- $methodName,
- $optionalArgs,
- $methodDescriptors['responseType'],
- $request,
- $methodDescriptors['interfaceOverride'] ?? $this->serviceName
- );
- case Call::SERVER_STREAMING_CALL:
- case Call::CLIENT_STREAMING_CALL:
- case Call::BIDI_STREAMING_CALL:
- throw new ValidationException("Call type '$callType' of requested method " .
- "'$methodName' is not supported for async execution.");
- }
- return $this->startApiCall($methodName, $request, $optionalArgs);
- }
- /**
- * @param string $methodName
- * @param Message $request
- * @param array $optionalArgs {
- * Call Options
- *
- * @type array $headers [optional] key-value array containing headers
- * @type int $timeoutMillis [optional] the timeout in milliseconds for the call
- * @type array $transportOptions [optional] transport-specific call options
- * @type RetrySettings|array $retrySettings [optional] A retry settings
- * override for the call.
- * }
- *
- * @experimental
- *
- * @return PromiseInterface|PagedListResponse|BidiStream|ClientStream|ServerStream
- */
- private function startApiCall(
- string $methodName,
- Message $request = null,
- array $optionalArgs = []
- ) {
- $methodDescriptors =$this->validateCallConfig($methodName);
- $callType = $methodDescriptors['callType'];
- // Prepare request-based headers, merge with user-provided headers,
- // which take precedence.
- $headerParams = $methodDescriptors['headerParams'] ?? [];
- $requestHeaders = $this->buildRequestParamsHeader($headerParams, $request);
- $optionalArgs['headers'] = array_merge($requestHeaders, $optionalArgs['headers'] ?? []);
- // Default the interface name, if not set, to the client's protobuf service name.
- $interfaceName = $methodDescriptors['interfaceOverride'] ?? $this->serviceName;
- // Handle call based on call type configured in the method descriptor config.
- if ($callType == Call::LONGRUNNING_CALL) {
- return $this->startOperationsCall(
- $methodName,
- $optionalArgs,
- $request,
- $this->getOperationsClient(),
- $interfaceName,
- // Custom operations will define their own operation response type, whereas standard
- // LRO defaults to the same type.
- $methodDescriptors['responseType'] ?? null
- );
- }
- // Fully-qualified name of the response message PHP class.
- $decodeType = $methodDescriptors['responseType'];
- if ($callType == Call::PAGINATED_CALL) {
- return $this->getPagedListResponse($methodName, $optionalArgs, $decodeType, $request, $interfaceName);
- }
- // Unary, and all Streaming types handled by startCall.
- return $this->startCall($methodName, $decodeType, $optionalArgs, $request, $callType, $interfaceName);
- }
- /**
- * @param string $methodName
- * @param string $decodeType
- * @param array $optionalArgs {
- * Call Options
- *
- * @type array $headers [optional] key-value array containing headers
- * @type int $timeoutMillis [optional] the timeout in milliseconds for the call
- * @type array $transportOptions [optional] transport-specific call options
- * @type RetrySettings|array $retrySettings [optional] A retry settings
- * override for the call.
- * }
- * @param Message $request
- * @param int $callType
- * @param string $interfaceName
- *
- * @return PromiseInterface|BidiStream|ClientStream|ServerStream
- */
- private function startCall(
- string $methodName,
- string $decodeType,
- array $optionalArgs = [],
- Message $request = null,
- int $callType = Call::UNARY_CALL,
- string $interfaceName = null
- ) {
- $optionalArgs = $this->configureCallOptions($optionalArgs);
- $callStack = $this->createCallStack(
- $this->configureCallConstructionOptions($methodName, $optionalArgs)
- );
- $descriptor = $this->descriptors[$methodName]['grpcStreaming'] ?? null;
- $call = new Call(
- $this->buildMethod($interfaceName, $methodName),
- $decodeType,
- $request,
- $descriptor,
- $callType
- );
- switch ($callType) {
- case Call::UNARY_CALL:
- $this->modifyUnaryCallable($callStack);
- break;
- case Call::BIDI_STREAMING_CALL:
- case Call::CLIENT_STREAMING_CALL:
- case Call::SERVER_STREAMING_CALL:
- $this->modifyStreamingCallable($callStack);
- break;
- }
- return $callStack($call, $optionalArgs + array_filter([
- 'audience' => self::getDefaultAudience()
- ]));
- }
- /**
- * @param array $callConstructionOptions {
- * Call Construction Options
- *
- * @type RetrySettings $retrySettings [optional] A retry settings override
- * For the call.
- * }
- *
- * @return callable
- */
- private function createCallStack(array $callConstructionOptions)
- {
- $quotaProject = $this->credentialsWrapper->getQuotaProject();
- $fixedHeaders = $this->agentHeader;
- if ($quotaProject) {
- $fixedHeaders += [
- 'X-Goog-User-Project' => [$quotaProject]
- ];
- }
- $callStack = function (Call $call, array $options) {
- $startCallMethod = $this->transportCallMethods[$call->getCallType()];
- return $this->transport->$startCallMethod($call, $options);
- };
- $callStack = new CredentialsWrapperMiddleware($callStack, $this->credentialsWrapper);
- $callStack = new FixedHeaderMiddleware($callStack, $fixedHeaders, true);
- $callStack = new RetryMiddleware($callStack, $callConstructionOptions['retrySettings']);
- $callStack = new OptionsFilterMiddleware($callStack, [
- 'headers',
- 'timeoutMillis',
- 'transportOptions',
- 'metadataCallback',
- 'audience',
- 'metadataReturnType'
- ]);
- foreach (\array_reverse($this->middlewareCallables) as $fn) {
- /** @var MiddlewareInterface $callStack */
- $callStack = $fn($callStack);
- }
- return $callStack;
- }
- /**
- * @param string $methodName
- * @param array $optionalArgs {
- * Optional arguments
- *
- * @type RetrySettings|array $retrySettings [optional] A retry settings
- * override for the call.
- * }
- *
- * @return array
- */
- private function configureCallConstructionOptions(string $methodName, array $optionalArgs)
- {
- $retrySettings = $this->retrySettings[$methodName];
- // Allow for retry settings to be changed at call time
- if (isset($optionalArgs['retrySettings'])) {
- if ($optionalArgs['retrySettings'] instanceof RetrySettings) {
- $retrySettings = $optionalArgs['retrySettings'];
- } else {
- $retrySettings = $retrySettings->with(
- $optionalArgs['retrySettings']
- );
- }
- }
- return [
- 'retrySettings' => $retrySettings,
- ];
- }
- /**
- * @return array
- */
- private function configureCallOptions(array $optionalArgs): array
- {
- if ($this->isBackwardsCompatibilityMode()) {
- return $optionalArgs;
- }
- // cast to CallOptions for new surfaces only
- return (new CallOptions($optionalArgs))->toArray();
- }
- /**
- * @param string $methodName
- * @param array $optionalArgs {
- * Call Options
- *
- * @type array $headers [optional] key-value array containing headers
- * @type int $timeoutMillis [optional] the timeout in milliseconds for the call
- * @type array $transportOptions [optional] transport-specific call options
- * }
- * @param Message $request
- * @param OperationsClient|object $client
- * @param string $interfaceName
- * @param string $operationClass If provided, will be used instead of the default
- * operation response class of {@see \Google\LongRunning\Operation}.
- *
- * @return PromiseInterface
- */
- private function startOperationsCall(
- string $methodName,
- array $optionalArgs,
- Message $request,
- $client,
- string $interfaceName = null,
- string $operationClass = null
- ) {
- $optionalArgs = $this->configureCallOptions($optionalArgs);
- $callStack = $this->createCallStack(
- $this->configureCallConstructionOptions($methodName, $optionalArgs)
- );
- $descriptor = $this->descriptors[$methodName]['longRunning'];
- $metadataReturnType = null;
- // Call the methods supplied in "additionalArgumentMethods" on the request Message object
- // to build the "additionalOperationArguments" option for the operation response.
- if (isset($descriptor['additionalArgumentMethods'])) {
- $additionalArgs = [];
- foreach ($descriptor['additionalArgumentMethods'] as $additionalArgsMethodName) {
- $additionalArgs[] = $request->$additionalArgsMethodName();
- }
- $descriptor['additionalOperationArguments'] = $additionalArgs;
- unset($descriptor['additionalArgumentMethods']);
- }
- if (isset($descriptor['metadataReturnType'])) {
- $metadataReturnType = $descriptor['metadataReturnType'];
- }
- $callStack = new OperationsMiddleware($callStack, $client, $descriptor);
- $call = new Call(
- $this->buildMethod($interfaceName, $methodName),
- $operationClass ?: Operation::class,
- $request,
- [],
- Call::UNARY_CALL
- );
- $this->modifyUnaryCallable($callStack);
- return $callStack($call, $optionalArgs + array_filter([
- 'metadataReturnType' => $metadataReturnType,
- 'audience' => self::getDefaultAudience()
- ]));
- }
- /**
- * @param string $methodName
- * @param array $optionalArgs
- * @param string $decodeType
- * @param Message $request
- * @param string $interfaceName
- *
- * @return PagedListResponse
- */
- private function getPagedListResponse(
- string $methodName,
- array $optionalArgs,
- string $decodeType,
- Message $request,
- string $interfaceName = null
- ) {
- return $this->getPagedListResponseAsync(
- $methodName,
- $optionalArgs,
- $decodeType,
- $request,
- $interfaceName
- )->wait();
- }
- /**
- * @param string $methodName
- * @param array $optionalArgs
- * @param string $decodeType
- * @param Message $request
- * @param string $interfaceName
- *
- * @return PromiseInterface
- */
- private function getPagedListResponseAsync(
- string $methodName,
- array $optionalArgs,
- string $decodeType,
- Message $request,
- string $interfaceName = null
- ) {
- $optionalArgs = $this->configureCallOptions($optionalArgs);
- $callStack = $this->createCallStack(
- $this->configureCallConstructionOptions($methodName, $optionalArgs)
- );
- $descriptor = new PageStreamingDescriptor(
- $this->descriptors[$methodName]['pageStreaming']
- );
- $callStack = new PagedMiddleware($callStack, $descriptor);
- $call = new Call(
- $this->buildMethod($interfaceName, $methodName),
- $decodeType,
- $request,
- [],
- Call::UNARY_CALL
- );
- $this->modifyUnaryCallable($callStack);
- return $callStack($call, $optionalArgs + array_filter([
- 'audience' => self::getDefaultAudience()
- ]));
- }
- /**
- * @param string $interfaceName
- * @param string $methodName
- *
- * @return string
- */
- private function buildMethod(string $interfaceName = null, string $methodName = null)
- {
- return sprintf(
- '%s/%s',
- $interfaceName ?: $this->serviceName,
- $methodName
- );
- }
- /**
- * @param array $headerParams
- * @param Message|null $request
- *
- * @return array
- */
- private function buildRequestParamsHeader(array $headerParams, Message $request = null)
- {
- $headers = [];
- // No request message means no request-based headers.
- if (!$request) {
- return $headers;
- }
- foreach ($headerParams as $headerParam) {
- $msg = $request;
- $value = null;
- foreach ($headerParam['fieldAccessors'] as $accessor) {
- $value = $msg->$accessor();
- // In case the field in question is nested in another message,
- // skip the header param when the nested message field is unset.
- $msg = $value;
- if (is_null($msg)) {
- break;
- }
- }
- $keyName = $headerParam['keyName'];
- // If there are value pattern matchers configured and the target
- // field was set, evaluate the matchers in the order that they were
- // annotated in with last one matching wins.
- $original = $value;
- $matchers = isset($headerParam['matchers']) && !is_null($value) ?
- $headerParam['matchers'] :
- [];
- foreach ($matchers as $matcher) {
- $matches = [];
- if (preg_match($matcher, $original, $matches)) {
- $value = $matches[$keyName];
- }
- }
- // If there are no matches or the target field was unset, skip this
- // header param.
- if (!$value) {
- continue;
- }
- $headers[$keyName] = $value;
- }
- $requestParams = new RequestParamsHeaderDescriptor($headers);
- return $requestParams->getHeader();
- }
- /**
- * The SERVICE_ADDRESS constant is set by GAPIC clients
- */
- private static function getDefaultAudience()
- {
- if (!defined('self::SERVICE_ADDRESS')) {
- return null;
- }
- return 'https://' . self::SERVICE_ADDRESS . '/'; // @phpstan-ignore-line
- }
- /**
- * Modify the unary callable.
- *
- * @param callable $callable
- * @access private
- */
- protected function modifyUnaryCallable(callable &$callable)
- {
- // Do nothing - this method exists to allow callable modification by partial veneers.
- }
- /**
- * Modify the streaming callable.
- *
- * @param callable $callable
- * @access private
- */
- protected function modifyStreamingCallable(callable &$callable)
- {
- // Do nothing - this method exists to allow callable modification by partial veneers.
- }
- /**
- * @internal
- */
- private function isBackwardsCompatibilityMode(): bool
- {
- return $this->backwardsCompatibilityMode
- ?? $this->backwardsCompatibilityMode = substr(__CLASS__, -11) === 'GapicClient';
- }
- }
|