[f9c482b] | 1 | <?php
|
---|
| 2 | /**
|
---|
| 3 | * Copyright 2010 Google Inc.
|
---|
| 4 | *
|
---|
| 5 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
| 6 | * you may not use this file except in compliance with the License.
|
---|
| 7 | * You may obtain a copy of the License at
|
---|
| 8 | *
|
---|
| 9 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
| 10 | *
|
---|
| 11 | * Unless required by applicable law or agreed to in writing, software
|
---|
| 12 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
| 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
| 14 | * See the License for the specific language governing permissions and
|
---|
| 15 | * limitations under the License.
|
---|
| 16 | */
|
---|
| 17 |
|
---|
| 18 | namespace Google\Service;
|
---|
| 19 |
|
---|
| 20 | use Google\Exception as GoogleException;
|
---|
| 21 | use Google\Http\MediaFileUpload;
|
---|
| 22 | use Google\Model;
|
---|
| 23 | use Google\Utils\UriTemplate;
|
---|
| 24 | use GuzzleHttp\Psr7\Request;
|
---|
| 25 |
|
---|
| 26 | /**
|
---|
| 27 | * Implements the actual methods/resources of the discovered Google API using magic function
|
---|
| 28 | * calling overloading (__call()), which on call will see if the method name (plus.activities.list)
|
---|
| 29 | * is available in this service, and if so construct an apiHttpRequest representing it.
|
---|
| 30 | *
|
---|
| 31 | */
|
---|
| 32 | class Resource
|
---|
| 33 | {
|
---|
| 34 | // Valid query parameters that work, but don't appear in discovery.
|
---|
| 35 | private $stackParameters = [
|
---|
| 36 | 'alt' => ['type' => 'string', 'location' => 'query'],
|
---|
| 37 | 'fields' => ['type' => 'string', 'location' => 'query'],
|
---|
| 38 | 'trace' => ['type' => 'string', 'location' => 'query'],
|
---|
| 39 | 'userIp' => ['type' => 'string', 'location' => 'query'],
|
---|
| 40 | 'quotaUser' => ['type' => 'string', 'location' => 'query'],
|
---|
| 41 | 'data' => ['type' => 'string', 'location' => 'body'],
|
---|
| 42 | 'mimeType' => ['type' => 'string', 'location' => 'header'],
|
---|
| 43 | 'uploadType' => ['type' => 'string', 'location' => 'query'],
|
---|
| 44 | 'mediaUpload' => ['type' => 'complex', 'location' => 'query'],
|
---|
| 45 | 'prettyPrint' => ['type' => 'string', 'location' => 'query'],
|
---|
| 46 | ];
|
---|
| 47 |
|
---|
| 48 | /** @var string $rootUrlTemplate */
|
---|
| 49 | private $rootUrlTemplate;
|
---|
| 50 |
|
---|
| 51 | /** @var string $apiVersion */
|
---|
| 52 | protected $apiVersion;
|
---|
| 53 |
|
---|
| 54 | /** @var \Google\Client $client */
|
---|
| 55 | private $client;
|
---|
| 56 |
|
---|
| 57 | /** @var string $serviceName */
|
---|
| 58 | private $serviceName;
|
---|
| 59 |
|
---|
| 60 | /** @var string $servicePath */
|
---|
| 61 | private $servicePath;
|
---|
| 62 |
|
---|
| 63 | /** @var string $resourceName */
|
---|
| 64 | private $resourceName;
|
---|
| 65 |
|
---|
| 66 | /** @var array $methods */
|
---|
| 67 | private $methods;
|
---|
| 68 |
|
---|
| 69 | public function __construct($service, $serviceName, $resourceName, $resource)
|
---|
| 70 | {
|
---|
| 71 | $this->rootUrlTemplate = $service->rootUrlTemplate ?? $service->rootUrl;
|
---|
| 72 | $this->client = $service->getClient();
|
---|
| 73 | $this->servicePath = $service->servicePath;
|
---|
| 74 | $this->serviceName = $serviceName;
|
---|
| 75 | $this->resourceName = $resourceName;
|
---|
| 76 | $this->methods = is_array($resource) && isset($resource['methods']) ?
|
---|
| 77 | $resource['methods'] :
|
---|
| 78 | [$resourceName => $resource];
|
---|
| 79 | }
|
---|
| 80 |
|
---|
| 81 | /**
|
---|
| 82 | * TODO: This function needs simplifying.
|
---|
| 83 | *
|
---|
| 84 | * @template T
|
---|
| 85 | * @param string $name
|
---|
| 86 | * @param array $arguments
|
---|
| 87 | * @param class-string<T> $expectedClass - optional, the expected class name
|
---|
| 88 | * @return mixed|T|ResponseInterface|RequestInterface
|
---|
| 89 | * @throws \Google\Exception
|
---|
| 90 | */
|
---|
| 91 | public function call($name, $arguments, $expectedClass = null)
|
---|
| 92 | {
|
---|
| 93 | if (! isset($this->methods[$name])) {
|
---|
| 94 | $this->client->getLogger()->error(
|
---|
| 95 | 'Service method unknown',
|
---|
| 96 | [
|
---|
| 97 | 'service' => $this->serviceName,
|
---|
| 98 | 'resource' => $this->resourceName,
|
---|
| 99 | 'method' => $name
|
---|
| 100 | ]
|
---|
| 101 | );
|
---|
| 102 |
|
---|
| 103 | throw new GoogleException(
|
---|
| 104 | "Unknown function: " .
|
---|
| 105 | "{$this->serviceName}->{$this->resourceName}->{$name}()"
|
---|
| 106 | );
|
---|
| 107 | }
|
---|
| 108 | $method = $this->methods[$name];
|
---|
| 109 | $parameters = $arguments[0];
|
---|
| 110 |
|
---|
| 111 | // postBody is a special case since it's not defined in the discovery
|
---|
| 112 | // document as parameter, but we abuse the param entry for storing it.
|
---|
| 113 | $postBody = null;
|
---|
| 114 | if (isset($parameters['postBody'])) {
|
---|
| 115 | if ($parameters['postBody'] instanceof Model) {
|
---|
| 116 | // In the cases the post body is an existing object, we want
|
---|
| 117 | // to use the smart method to create a simple object for
|
---|
| 118 | // for JSONification.
|
---|
| 119 | $parameters['postBody'] = $parameters['postBody']->toSimpleObject();
|
---|
| 120 | } elseif (is_object($parameters['postBody'])) {
|
---|
| 121 | // If the post body is another kind of object, we will try and
|
---|
| 122 | // wrangle it into a sensible format.
|
---|
| 123 | $parameters['postBody'] =
|
---|
| 124 | $this->convertToArrayAndStripNulls($parameters['postBody']);
|
---|
| 125 | }
|
---|
| 126 | $postBody = (array) $parameters['postBody'];
|
---|
| 127 | unset($parameters['postBody']);
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 | // TODO: optParams here probably should have been
|
---|
| 131 | // handled already - this may well be redundant code.
|
---|
| 132 | if (isset($parameters['optParams'])) {
|
---|
| 133 | $optParams = $parameters['optParams'];
|
---|
| 134 | unset($parameters['optParams']);
|
---|
| 135 | $parameters = array_merge($parameters, $optParams);
|
---|
| 136 | }
|
---|
| 137 |
|
---|
| 138 | if (!isset($method['parameters'])) {
|
---|
| 139 | $method['parameters'] = [];
|
---|
| 140 | }
|
---|
| 141 |
|
---|
| 142 | $method['parameters'] = array_merge(
|
---|
| 143 | $this->stackParameters,
|
---|
| 144 | $method['parameters']
|
---|
| 145 | );
|
---|
| 146 |
|
---|
| 147 | foreach ($parameters as $key => $val) {
|
---|
| 148 | if ($key != 'postBody' && !isset($method['parameters'][$key])) {
|
---|
| 149 | $this->client->getLogger()->error(
|
---|
| 150 | 'Service parameter unknown',
|
---|
| 151 | [
|
---|
| 152 | 'service' => $this->serviceName,
|
---|
| 153 | 'resource' => $this->resourceName,
|
---|
| 154 | 'method' => $name,
|
---|
| 155 | 'parameter' => $key
|
---|
| 156 | ]
|
---|
| 157 | );
|
---|
| 158 | throw new GoogleException("($name) unknown parameter: '$key'");
|
---|
| 159 | }
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | foreach ($method['parameters'] as $paramName => $paramSpec) {
|
---|
| 163 | if (
|
---|
| 164 | isset($paramSpec['required']) &&
|
---|
| 165 | $paramSpec['required'] &&
|
---|
| 166 | ! isset($parameters[$paramName])
|
---|
| 167 | ) {
|
---|
| 168 | $this->client->getLogger()->error(
|
---|
| 169 | 'Service parameter missing',
|
---|
| 170 | [
|
---|
| 171 | 'service' => $this->serviceName,
|
---|
| 172 | 'resource' => $this->resourceName,
|
---|
| 173 | 'method' => $name,
|
---|
| 174 | 'parameter' => $paramName
|
---|
| 175 | ]
|
---|
| 176 | );
|
---|
| 177 | throw new GoogleException("($name) missing required param: '$paramName'");
|
---|
| 178 | }
|
---|
| 179 | if (isset($parameters[$paramName])) {
|
---|
| 180 | $value = $parameters[$paramName];
|
---|
| 181 | $parameters[$paramName] = $paramSpec;
|
---|
| 182 | $parameters[$paramName]['value'] = $value;
|
---|
| 183 | unset($parameters[$paramName]['required']);
|
---|
| 184 | } else {
|
---|
| 185 | // Ensure we don't pass nulls.
|
---|
| 186 | unset($parameters[$paramName]);
|
---|
| 187 | }
|
---|
| 188 | }
|
---|
| 189 |
|
---|
| 190 | $this->client->getLogger()->info(
|
---|
| 191 | 'Service Call',
|
---|
| 192 | [
|
---|
| 193 | 'service' => $this->serviceName,
|
---|
| 194 | 'resource' => $this->resourceName,
|
---|
| 195 | 'method' => $name,
|
---|
| 196 | 'arguments' => $parameters,
|
---|
| 197 | ]
|
---|
| 198 | );
|
---|
| 199 |
|
---|
| 200 | // build the service uri
|
---|
| 201 | $url = $this->createRequestUri($method['path'], $parameters);
|
---|
| 202 |
|
---|
| 203 | // NOTE: because we're creating the request by hand,
|
---|
| 204 | // and because the service has a rootUrl property
|
---|
| 205 | // the "base_uri" of the Http Client is not accounted for
|
---|
| 206 | $request = new Request(
|
---|
| 207 | $method['httpMethod'],
|
---|
| 208 | $url,
|
---|
| 209 | $postBody ? ['content-type' => 'application/json'] : [],
|
---|
| 210 | $postBody ? json_encode($postBody) : ''
|
---|
| 211 | );
|
---|
| 212 |
|
---|
| 213 | // support uploads
|
---|
| 214 | if (isset($parameters['data'])) {
|
---|
| 215 | $mimeType = isset($parameters['mimeType'])
|
---|
| 216 | ? $parameters['mimeType']['value']
|
---|
| 217 | : 'application/octet-stream';
|
---|
| 218 | $data = $parameters['data']['value'];
|
---|
| 219 | $upload = new MediaFileUpload($this->client, $request, $mimeType, $data);
|
---|
| 220 |
|
---|
| 221 | // pull down the modified request
|
---|
| 222 | $request = $upload->getRequest();
|
---|
| 223 | }
|
---|
| 224 |
|
---|
| 225 | // if this is a media type, we will return the raw response
|
---|
| 226 | // rather than using an expected class
|
---|
| 227 | if (isset($parameters['alt']) && $parameters['alt']['value'] == 'media') {
|
---|
| 228 | $expectedClass = null;
|
---|
| 229 | }
|
---|
| 230 |
|
---|
| 231 | // If the class which is extending from this one contains
|
---|
| 232 | // an Api Version, add it to the header
|
---|
| 233 | if ($this->apiVersion) {
|
---|
| 234 | $request = $request
|
---|
| 235 | ->withHeader('X-Goog-Api-Version', $this->apiVersion);
|
---|
| 236 | }
|
---|
| 237 |
|
---|
| 238 | // if the client is marked for deferring, rather than
|
---|
| 239 | // execute the request, return the response
|
---|
| 240 | if ($this->client->shouldDefer()) {
|
---|
| 241 | // @TODO find a better way to do this
|
---|
| 242 | $request = $request
|
---|
| 243 | ->withHeader('X-Php-Expected-Class', $expectedClass);
|
---|
| 244 |
|
---|
| 245 | return $request;
|
---|
| 246 | }
|
---|
| 247 |
|
---|
| 248 | return $this->client->execute($request, $expectedClass);
|
---|
| 249 | }
|
---|
| 250 |
|
---|
| 251 | protected function convertToArrayAndStripNulls($o)
|
---|
| 252 | {
|
---|
| 253 | $o = (array) $o;
|
---|
| 254 | foreach ($o as $k => $v) {
|
---|
| 255 | if ($v === null) {
|
---|
| 256 | unset($o[$k]);
|
---|
| 257 | } elseif (is_object($v) || is_array($v)) {
|
---|
| 258 | $o[$k] = $this->convertToArrayAndStripNulls($o[$k]);
|
---|
| 259 | }
|
---|
| 260 | }
|
---|
| 261 | return $o;
|
---|
| 262 | }
|
---|
| 263 |
|
---|
| 264 | /**
|
---|
| 265 | * Parse/expand request parameters and create a fully qualified
|
---|
| 266 | * request uri.
|
---|
| 267 | * @static
|
---|
| 268 | * @param string $restPath
|
---|
| 269 | * @param array $params
|
---|
| 270 | * @return string $requestUrl
|
---|
| 271 | */
|
---|
| 272 | public function createRequestUri($restPath, $params)
|
---|
| 273 | {
|
---|
| 274 | // Override the default servicePath address if the $restPath use a /
|
---|
| 275 | if ('/' == substr($restPath, 0, 1)) {
|
---|
| 276 | $requestUrl = substr($restPath, 1);
|
---|
| 277 | } else {
|
---|
| 278 | $requestUrl = $this->servicePath . $restPath;
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 | if ($this->rootUrlTemplate) {
|
---|
| 282 | // code for universe domain
|
---|
| 283 | $rootUrl = str_replace('UNIVERSE_DOMAIN', $this->client->getUniverseDomain(), $this->rootUrlTemplate);
|
---|
| 284 | // code for leading slash
|
---|
| 285 | if ('/' !== substr($rootUrl, -1) && '/' !== substr($requestUrl, 0, 1)) {
|
---|
| 286 | $requestUrl = '/' . $requestUrl;
|
---|
| 287 | }
|
---|
| 288 | $requestUrl = $rootUrl . $requestUrl;
|
---|
| 289 | }
|
---|
| 290 | $uriTemplateVars = [];
|
---|
| 291 | $queryVars = [];
|
---|
| 292 | foreach ($params as $paramName => $paramSpec) {
|
---|
| 293 | if ($paramSpec['type'] == 'boolean') {
|
---|
| 294 | $paramSpec['value'] = $paramSpec['value'] ? 'true' : 'false';
|
---|
| 295 | }
|
---|
| 296 | if ($paramSpec['location'] == 'path') {
|
---|
| 297 | $uriTemplateVars[$paramName] = $paramSpec['value'];
|
---|
| 298 | } elseif ($paramSpec['location'] == 'query') {
|
---|
| 299 | if (is_array($paramSpec['value'])) {
|
---|
| 300 | foreach ($paramSpec['value'] as $value) {
|
---|
| 301 | $queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($value));
|
---|
| 302 | }
|
---|
| 303 | } else {
|
---|
| 304 | $queryVars[] = $paramName . '=' . rawurlencode(rawurldecode($paramSpec['value']));
|
---|
| 305 | }
|
---|
| 306 | }
|
---|
| 307 | }
|
---|
| 308 |
|
---|
| 309 | if (count($uriTemplateVars)) {
|
---|
| 310 | $uriTemplateParser = new UriTemplate();
|
---|
| 311 | $requestUrl = $uriTemplateParser->parse($requestUrl, $uriTemplateVars);
|
---|
| 312 | }
|
---|
| 313 |
|
---|
| 314 | if (count($queryVars)) {
|
---|
| 315 | $requestUrl .= '?' . implode('&', $queryVars);
|
---|
| 316 | }
|
---|
| 317 |
|
---|
| 318 | return $requestUrl;
|
---|
| 319 | }
|
---|
| 320 | }
|
---|