Blame view

sources/3rdparty/rackspace/php-opencloud/lib/OpenCloud/Common/PersistentObject.php 21.5 KB
31b7f2792   Kload   Upgrade to ownclo...
1
2
  <?php
  /**
6d9380f96   Cédric Dupont   Update sources OC...
3
4
5
6
7
8
   * PHP OpenCloud library
   * 
   * @copyright 2014 Rackspace Hosting, Inc. See LICENSE for information.
   * @license   https://www.apache.org/licenses/LICENSE-2.0
   * @author    Glen Campbell <glen.campbell@rackspace.com>
   * @author    Jamie Hannaford <jamie.hannaford@rackspace.com>
31b7f2792   Kload   Upgrade to ownclo...
9
10
11
   */
  
  namespace OpenCloud\Common;
6d9380f96   Cédric Dupont   Update sources OC...
12
13
14
15
16
17
18
  use Guzzle\Http\Exception\BadResponseException;
  use Guzzle\Http\Message\Response;
  use Guzzle\Http\Url;
  use OpenCloud\Common\Constants\State as StateConst;
  use OpenCloud\Common\Service\ServiceInterface;
  use OpenCloud\Common\Exceptions\RuntimeException;
  use OpenCloud\Common\Http\Message\Formatter;
31b7f2792   Kload   Upgrade to ownclo...
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  /**
   * Represents an object that can be retrieved, created, updated and deleted.
   *
   * This class abstracts much of the common functionality between: 
   *  
   *  * Nova servers;
   *  * Swift containers and objects;
   *  * DBAAS instances;
   *  * Cinder volumes;
   *  * and various other objects that:
   *    * have a URL;
   *    * can be created, updated, deleted, or retrieved;
   *    * use a standard JSON format with a top-level element followed by 
   *      a child object with attributes.
   *
   * In general, you can create a persistent object class by subclassing this
   * class and defining some protected, static variables:
   * 
   *  * $url_resource - the sub-resource value in the URL of the parent. For
   *    example, if the parent URL is `http://something/parent`, then setting this
   *    value to "another" would result in a URL for the persistent object of 
   *    `http://something/parent/another`.
   *
   *  * $json_name - the top-level JSON object name. For example, if the
   *    persistent object is represented by `{"foo": {"attr":value, ...}}`, then
   *    set $json_name to "foo".
   *
   *  * $json_collection_name - optional; this value is the name of a collection
   *    of the persistent objects. If not provided, it defaults to `json_name`
   *    with an appended "s" (e.g., if `json_name` is "foo", then
   *    `json_collection_name` would be "foos"). Set this value if the collection 
   *    name doesn't follow this pattern.
   *
   *  * $json_collection_element - the common pattern for a collection is:
   *    `{"collection": [{"attr":"value",...}, {"attr":"value",...}, ...]}`
   *    That is, each element of the array is a \stdClass object containing the
   *    object's attributes. In rare instances, the objects in the array
   *    are named, and `json_collection_element` contains the name of the
   *    collection objects. For example, in this JSON response:
   *    `{"allowedDomain":[{"allowedDomain":{"name":"foo"}}]}`,
   *    `json_collection_element` would be set to "allowedDomain".
   *
   * The PersistentObject class supports the standard CRUD methods; if these are 
   * not needed (i.e. not supported by  the service), the subclass should redefine 
   * these to call the `noCreate`, `noUpdate`, or `noDelete` methods, which will 
   * trigger an appropriate exception. For example, if an object cannot be created:
   *
   *    function create($params = array()) 
   *    { 
   *       $this->noCreate(); 
   *    }
   */
  abstract class PersistentObject extends Base
  {
31b7f2792   Kload   Upgrade to ownclo...
73
      private $service;
31b7f2792   Kload   Upgrade to ownclo...
74
      private $parent;
6d9380f96   Cédric Dupont   Update sources OC...
75
      protected $metadata;
31b7f2792   Kload   Upgrade to ownclo...
76
77
78
79
80
81
82
83
84
  
      /**
       * Retrieves the instance from persistent storage
       *
       * @param mixed $service The service object for this resource
       * @param mixed $info    The ID or array/object of data
       */
      public function __construct($service = null, $info = null)
      {
6d9380f96   Cédric Dupont   Update sources OC...
85
          if ($service instanceof ServiceInterface) {
31b7f2792   Kload   Upgrade to ownclo...
86
87
88
              $this->setService($service);
          }
          
6d9380f96   Cédric Dupont   Update sources OC...
89
          $this->metadata = new Metadata;
31b7f2792   Kload   Upgrade to ownclo...
90
91
          $this->populate($info);
      }
6d9380f96   Cédric Dupont   Update sources OC...
92
              
31b7f2792   Kload   Upgrade to ownclo...
93
94
95
      /**
       * Sets the service associated with this resource object.
       * 
6d9380f96   Cédric Dupont   Update sources OC...
96
97
       * @param \OpenCloud\Common\Service\ServiceInterface $service
       * @return \OpenCloud\Common\PersistentObject
31b7f2792   Kload   Upgrade to ownclo...
98
       */
6d9380f96   Cédric Dupont   Update sources OC...
99
      public function setService(ServiceInterface $service)
31b7f2792   Kload   Upgrade to ownclo...
100
101
102
103
104
105
106
107
108
      {
          $this->service = $service;
          return $this;
      }
      
      /**
       * Returns the service object for this resource; required for making
       * requests, etc. because it has direct access to the Connection.
       * 
6d9380f96   Cédric Dupont   Update sources OC...
109
110
       * @return \OpenCloud\Common\Service\ServiceInterface
       * @throws \OpenCloud\Common\Exceptions\ServiceException
31b7f2792   Kload   Upgrade to ownclo...
111
112
113
114
       */
      public function getService()
      {
          if (null === $this->service) {
6d9380f96   Cédric Dupont   Update sources OC...
115
              throw new Exceptions\ServiceException(
31b7f2792   Kload   Upgrade to ownclo...
116
117
118
119
120
121
122
                  'No service defined'
              );
          }
          return $this->service;
      }
      
      /**
31b7f2792   Kload   Upgrade to ownclo...
123
124
125
       * Set the parent object for this resource.
       * 
       * @param \OpenCloud\Common\PersistentObject $parent
6d9380f96   Cédric Dupont   Update sources OC...
126
       * @return \OpenCloud\Common\PersistentObject
31b7f2792   Kload   Upgrade to ownclo...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
       */
      public function setParent(PersistentObject $parent)
      {
          $this->parent = $parent;
          return $this;
      }
      
      /**
       * Returns the parent.
       * 
       * @return \OpenCloud\Common\PersistentObject
       */
      public function getParent()
      {
          if (null === $this->parent) {
              $this->parent = $this->getService();
          }
          return $this->parent;
      }
6d9380f96   Cédric Dupont   Update sources OC...
146
147
          
      public function getClient()
31b7f2792   Kload   Upgrade to ownclo...
148
      {
6d9380f96   Cédric Dupont   Update sources OC...
149
          return $this->getService()->getClient();
31b7f2792   Kload   Upgrade to ownclo...
150
151
      }
      
6d9380f96   Cédric Dupont   Update sources OC...
152
153
154
155
156
157
      public function setMetadata($metadata)
      {
          $this->metadata = $metadata;
          
          return $this;
      }
31b7f2792   Kload   Upgrade to ownclo...
158
      
6d9380f96   Cédric Dupont   Update sources OC...
159
160
161
162
      public function getMetadata()
      {
          return $this->metadata;
      }
31b7f2792   Kload   Upgrade to ownclo...
163

31b7f2792   Kload   Upgrade to ownclo...
164
165
166
      /**
       * Creates a new object
       *
31b7f2792   Kload   Upgrade to ownclo...
167
168
169
170
171
172
173
174
175
176
       * @param array $params array of values to set when creating the object
       * @return HttpResponse
       * @throws VolumeCreateError if HTTP status is not Success
       */
      public function create($params = array())
      {
          // set parameters
          if (!empty($params)) {
              $this->populate($params, false);
          }
31b7f2792   Kload   Upgrade to ownclo...
177
          // construct the JSON
6d9380f96   Cédric Dupont   Update sources OC...
178
          $json = json_encode($this->createJson());
31b7f2792   Kload   Upgrade to ownclo...
179
          $this->checkJsonError();
6d9380f96   Cédric Dupont   Update sources OC...
180
          $createUrl = $this->createUrl();
31b7f2792   Kload   Upgrade to ownclo...
181

6d9380f96   Cédric Dupont   Update sources OC...
182
183
184
185
186
187
188
189
190
          $response = $this->getClient()->post($createUrl, self::getJsonHeader(), $json)->send();
  
          // We have to try to parse the response body first because it should have precedence over a Location refresh.
          // I'd like to reverse the order, but Nova instances return ephemeral properties on creation which are not
          // available when you follow the Location link...
          if (null !== ($decoded = $this->parseResponse($response))) {
              $this->populate($decoded);
          } elseif ($location = $response->getHeader('Location')) {
              $this->refreshFromLocationUrl($location);
31b7f2792   Kload   Upgrade to ownclo...
191
          }
31b7f2792   Kload   Upgrade to ownclo...
192
193
194
  
          return $response;
      }
6d9380f96   Cédric Dupont   Update sources OC...
195
196
197
198
199
200
201
202
203
204
      public function refreshFromLocationUrl($url)
      {
          $fullUrl = Url::factory($url);
  
          $response = $this->getClient()->get($fullUrl)->send();
  
          if (null !== ($decoded = $this->parseResponse($response))) {
              $this->populate($decoded);
          }
      }
31b7f2792   Kload   Upgrade to ownclo...
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
      /**
       * Updates an existing object
       *
       * @api
       * @param array $params array of values to set when updating the object
       * @return HttpResponse
       * @throws VolumeCreateError if HTTP status is not Success
       */
      public function update($params = array())
      {
          // set parameters
          if (!empty($params)) {
              $this->populate($params);
          }
  
          // debug
          $this->getLogger()->info('{class}::Update({name})', array(
              'class' => get_class($this),
6d9380f96   Cédric Dupont   Update sources OC...
223
              'name'  => $this->getProperty($this->primaryKeyField())
31b7f2792   Kload   Upgrade to ownclo...
224
225
226
          ));
  
          // construct the JSON
6d9380f96   Cédric Dupont   Update sources OC...
227
          $json = json_encode($this->updateJson($params));
31b7f2792   Kload   Upgrade to ownclo...
228
          $this->checkJsonError();
31b7f2792   Kload   Upgrade to ownclo...
229
          // send the request
6d9380f96   Cédric Dupont   Update sources OC...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
          return $this->getClient()->put($this->getUrl(), self::getJsonHeader(), $json)->send();
      }
  
      /**
       * Refreshes the object from the origin (useful when the server is
       * changing states)
       *
       * @param string|null $id
       * @param string|null $url
       *
       * @return void
       * @throws Exceptions\IdRequiredError
       */
      public function refresh($id = null, $url = null)
      {
          $primaryKey = $this->primaryKeyField();
          $primaryKeyVal = $this->getProperty($primaryKey);
          
          if (!$url) {
              
              if (!$id = $id ?: $primaryKeyVal) {
                  throw new Exceptions\IdRequiredError(sprintf(
                      Lang::translate("%s has no %s; cannot be refreshed"),
                      get_class($this),
                      $primaryKey
                  ));
              }
              
              if ($primaryKeyVal != $id) {
                  $this->setProperty($primaryKey, $id);
              }
              
              $url = $this->getUrl();
          }
          
          // reset status, if available
          if ($this->getProperty('status')) {
              $this->setProperty('status', null);
31b7f2792   Kload   Upgrade to ownclo...
268
          }
31b7f2792   Kload   Upgrade to ownclo...
269

6d9380f96   Cédric Dupont   Update sources OC...
270
271
272
273
274
275
          $response = $this->getClient()->get($url)->send();
    
          if (null !== ($decoded = $this->parseResponse($response))) {
              $this->populate($decoded);
          }
          
31b7f2792   Kload   Upgrade to ownclo...
276
277
          return $response;
      }
6d9380f96   Cédric Dupont   Update sources OC...
278
      
31b7f2792   Kload   Upgrade to ownclo...
279
280
281
282
283
284
285
286
287
288
289
290
      /**
       * Deletes an object
       *
       * @api
       * @return HttpResponse
       * @throws DeleteError if HTTP status is not Success
       */
      public function delete()
      {
          $this->getLogger()->info('{class}::Delete()', array('class' => get_class($this)));
  
          // send the request
6d9380f96   Cédric Dupont   Update sources OC...
291
          return $this->getClient()->delete($this->getUrl())->send();
31b7f2792   Kload   Upgrade to ownclo...
292
293
294
      }
  
      /**
6d9380f96   Cédric Dupont   Update sources OC...
295
       * @deprecated
31b7f2792   Kload   Upgrade to ownclo...
296
       */
6d9380f96   Cédric Dupont   Update sources OC...
297
      public function url($path = null, array $query = array())
31b7f2792   Kload   Upgrade to ownclo...
298
      {
6d9380f96   Cédric Dupont   Update sources OC...
299
          return $this->getUrl($path, $query);
31b7f2792   Kload   Upgrade to ownclo...
300
301
302
      }
  
      /**
31b7f2792   Kload   Upgrade to ownclo...
303
304
305
306
307
308
309
       * Returns the default URL of the object
       *
       * This may have to be overridden in subclasses.
       *
       * @param string $subresource optional sub-resource string
       * @param array $qstr optional k/v pairs for query strings
       * @return string
31b7f2792   Kload   Upgrade to ownclo...
310
       */
6d9380f96   Cédric Dupont   Update sources OC...
311
      public function getUrl($path = null, array $query = array())
31b7f2792   Kload   Upgrade to ownclo...
312
      {
6d9380f96   Cédric Dupont   Update sources OC...
313
          if (!$url = $this->findLink('self')) {
31b7f2792   Kload   Upgrade to ownclo...
314

6d9380f96   Cédric Dupont   Update sources OC...
315
316
317
              // ...otherwise construct a URL from parent and this resource's
              // "URL name". If no name is set, resourceName() throws an error.
              $url = $this->getParent()->getUrl($this->resourceName());
31b7f2792   Kload   Upgrade to ownclo...
318

6d9380f96   Cédric Dupont   Update sources OC...
319
320
321
322
              // Does it have a primary key?
              if (null !== ($primaryKey = $this->getProperty($this->primaryKeyField()))) {
                  $url->addPath((string) $primaryKey);
              }
31b7f2792   Kload   Upgrade to ownclo...
323
          }
6d9380f96   Cédric Dupont   Update sources OC...
324
325
          if (!$url instanceof Url) {
              $url = Url::factory($url);
31b7f2792   Kload   Upgrade to ownclo...
326
          }
6d9380f96   Cédric Dupont   Update sources OC...
327
          return $url->addPath((string) $path)->setQuery($query);
31b7f2792   Kload   Upgrade to ownclo...
328
329
330
331
332
333
334
335
336
      }
  
      /**
       * Waits for the server/instance status to change
       *
       * This function repeatedly polls the system for a change in server
       * status. Once the status reaches the `$terminal` value (or 'ERROR'),
       * then the function returns.
       *
31b7f2792   Kload   Upgrade to ownclo...
337
338
339
340
341
342
343
344
       * @api
       * @param string $terminal the terminal state to wait for
       * @param integer $timeout the max time (in seconds) to wait
       * @param callable $callback a callback function that is invoked with
       *      each repetition of the polling sequence. This can be used, for
       *      example, to update a status display or to permit other operations
       *      to continue
       * @return void
6d9380f96   Cédric Dupont   Update sources OC...
345
       * @codeCoverageIgnore
31b7f2792   Kload   Upgrade to ownclo...
346
       */
6d9380f96   Cédric Dupont   Update sources OC...
347
348
349
350
351
      public function waitFor($state = null, $timeout = null, $callback = null, $interval = null)
      {
          $state    = $state ?: StateConst::ACTIVE;
          $timeout  = $timeout ?: StateConst::DEFAULT_TIMEOUT;
          $interval = $interval ?: StateConst::DEFAULT_INTERVAL;
31b7f2792   Kload   Upgrade to ownclo...
352
353
354
355
  
          // save stats
          $startTime = time();
          
6d9380f96   Cédric Dupont   Update sources OC...
356
          $states = array('ERROR', $state);
31b7f2792   Kload   Upgrade to ownclo...
357
358
359
          
          while (true) {
              
6d9380f96   Cédric Dupont   Update sources OC...
360
              $this->refresh($this->getProperty($this->primaryKeyField()));
31b7f2792   Kload   Upgrade to ownclo...
361
362
363
364
365
366
367
368
              
              if ($callback) {
                  call_user_func($callback, $this);
              }
              
              if (in_array($this->status(), $states) || (time() - $startTime) > $timeout) {
                  return;
              }
31b7f2792   Kload   Upgrade to ownclo...
369
              
6d9380f96   Cédric Dupont   Update sources OC...
370
              sleep($interval);
31b7f2792   Kload   Upgrade to ownclo...
371
          }
31b7f2792   Kload   Upgrade to ownclo...
372
      }
31b7f2792   Kload   Upgrade to ownclo...
373
374
      
      /**
31b7f2792   Kload   Upgrade to ownclo...
375
376
377
378
379
380
381
382
       * Sends the json string to the /action resource
       *
       * This is used for many purposes, such as rebooting the server,
       * setting the root password, creating images, etc.
       * Since it can only be used on a live server, it checks for a valid ID.
       *
       * @param $object - this will be encoded as json, and we handle all the JSON
       *     error-checking in one place
6d9380f96   Cédric Dupont   Update sources OC...
383
384
       * @throws Exceptions\IdRequiredError if server ID is not defined
       * @throws Exceptions\ServerActionError on other errors
31b7f2792   Kload   Upgrade to ownclo...
385
386
387
388
       * @returns boolean; TRUE if successful, FALSE otherwise
       */
      protected function action($object)
      {
6d9380f96   Cédric Dupont   Update sources OC...
389
          if (!$this->getProperty($this->primaryKeyField())) {
31b7f2792   Kload   Upgrade to ownclo...
390
391
              throw new Exceptions\IdRequiredError(sprintf(
                  Lang::translate('%s is not defined'),
6d9380f96   Cédric Dupont   Update sources OC...
392
393
                  get_class($this),
                  $this->primaryKeyField()
31b7f2792   Kload   Upgrade to ownclo...
394
395
396
397
398
399
400
401
402
403
404
405
              ));
          }
  
          if (!is_object($object)) {
              throw new Exceptions\ServerActionError(sprintf(
                  Lang::translate('%s::Action() requires an object as its parameter'),
                  get_class($this)
              ));
          }
  
          // convert the object to json
          $json = json_encode($object);
31b7f2792   Kload   Upgrade to ownclo...
406
407
408
409
410
411
412
413
414
415
416
417
          $this->checkJsonError();
  
          // debug - save the request
          $this->getLogger()->info(Lang::translate('{class}::action [{json}]'), array(
              'class' => get_class($this), 
              'json'  => $json
          ));
  
          // get the URL for the POST message
          $url = $this->url('action');
  
          // POST the message
6d9380f96   Cédric Dupont   Update sources OC...
418
419
          return $this->getClient()->post($url, self::getJsonHeader(), $json)->send();
      }
31b7f2792   Kload   Upgrade to ownclo...
420

6d9380f96   Cédric Dupont   Update sources OC...
421
422
423
424
425
426
427
428
429
430
431
       /**
       * Returns an object for the Create() method JSON
       * Must be overridden in a child class.
       *
       * @throws CreateError if not overridden
       */
      protected function createJson()
      {
          if (!isset($this->createKeys)) {
              throw new RuntimeException(sprintf(
                  'This resource object [%s] must have a visible createKeys array',
31b7f2792   Kload   Upgrade to ownclo...
432
433
434
435
                  get_class($this)
              ));
          }
          
6d9380f96   Cédric Dupont   Update sources OC...
436
437
438
439
440
441
          $element = (object) array();
  
          foreach ($this->createKeys as $key) {
              if (null !== ($property = $this->getProperty($key))) {
                  $element->$key = $property;
              }
31b7f2792   Kload   Upgrade to ownclo...
442
          }
31b7f2792   Kload   Upgrade to ownclo...
443

6d9380f96   Cédric Dupont   Update sources OC...
444
445
446
447
448
          if (isset($this->metadata) && count($this->metadata)) {
              $element->metadata = (object) $this->metadata->toArray();
          }
  
          return (object) array($this->jsonName() => (object) $element);
31b7f2792   Kload   Upgrade to ownclo...
449
      }
6d9380f96   Cédric Dupont   Update sources OC...
450

31b7f2792   Kload   Upgrade to ownclo...
451
      /**
6d9380f96   Cédric Dupont   Update sources OC...
452
453
454
455
       * Returns an object for the Update() method JSON
       * Must be overridden in a child class.
       *
       * @throws Exceptions\UpdateError if not overridden
31b7f2792   Kload   Upgrade to ownclo...
456
       */
6d9380f96   Cédric Dupont   Update sources OC...
457
      protected function updateJson($params = array())
31b7f2792   Kload   Upgrade to ownclo...
458
      {
6d9380f96   Cédric Dupont   Update sources OC...
459
460
461
462
463
          throw new Exceptions\UpdateError(sprintf(
              Lang::translate('[%s] UpdateJson() must be overridden'),
              get_class($this)
          ));
      }
31b7f2792   Kload   Upgrade to ownclo...
464

6d9380f96   Cédric Dupont   Update sources OC...
465
466
467
468
469
470
471
472
473
474
475
476
      /**
       * throws a CreateError for subclasses that don't support Create
       *
       * @throws Exceptions\CreateError
       */
      protected function noCreate()
      {
          throw new Exceptions\CreateError(sprintf(
              Lang::translate('[%s] does not support Create()'),
              get_class($this)
          ));
      }
31b7f2792   Kload   Upgrade to ownclo...
477

6d9380f96   Cédric Dupont   Update sources OC...
478
479
480
481
482
483
484
485
486
487
488
489
      /**
       * throws a DeleteError for subclasses that don't support Delete
       *
       * @throws Exceptions\DeleteError
       */
      protected function noDelete()
      {
          throw new Exceptions\DeleteError(sprintf(
              Lang::translate('[%s] does not support Delete()'),
              get_class($this)
          ));
      }
31b7f2792   Kload   Upgrade to ownclo...
490

6d9380f96   Cédric Dupont   Update sources OC...
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
      /**
       * throws a UpdateError for subclasses that don't support Update
       *
       * @throws Exceptions\UpdateError
       */
      protected function noUpdate()
      {
          throw new Exceptions\UpdateError(sprintf(
              Lang::translate('[%s] does not support Update()'),
              get_class($this)
          ));
      }
          
      /**
       * Returns the displayable name of the object
       *
       * Can be overridden by child objects; *must* be overridden by child
       * objects if the object does not have a `name` attribute defined.
       *
       * @api
       * @return string
       * @throws Exceptions\NameError if attribute 'name' is not defined
       */
      public function name()
      {
          if (null !== ($name = $this->getProperty('name'))) {
              return $name;
          } else {
              throw new Exceptions\NameError(sprintf(
                  Lang::translate('Name attribute does not exist for [%s]'),
                  get_class($this)
31b7f2792   Kload   Upgrade to ownclo...
522
523
              ));
          }
31b7f2792   Kload   Upgrade to ownclo...
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
      }
  
      /**
       * returns the object's status or `N/A` if not available
       *
       * @api
       * @return string
       */
      public function status()
      {
          return (isset($this->status)) ? $this->status : 'N/A';
      }
  
      /**
       * returns the object's identifier
       *
       * Can be overridden by a child class if the identifier is not in the
       * `$id` property. Use of this function permits the `$id` attribute to
       * be protected or private to prevent unauthorized overwriting for
       * security.
       *
       * @api
       * @return string
       */
      public function id()
      {
          return $this->id;
      }
  
      /**
       * checks for `$alias` in extensions and throws an error if not present
       *
6d9380f96   Cédric Dupont   Update sources OC...
556
       * @throws Exceptions\UnsupportedExtensionError
31b7f2792   Kload   Upgrade to ownclo...
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
       */
      public function checkExtension($alias)
      {
          if (!in_array($alias, $this->getService()->namespaces())) {
              throw new Exceptions\UnsupportedExtensionError(sprintf(
                  Lang::translate('Extension [%s] is not installed'),
                  $alias
              ));
          }
          
          return true;
      }
  
      /**
       * returns the region associated with the object
       *
       * navigates to the parent service to determine the region.
       *
       * @api
       */
      public function region()
      {
          return $this->getService()->Region();
      }
      
      /**
       * Since each server can have multiple links, this returns the desired one
       *
       * @param string $type - 'self' is most common; use 'bookmark' for
       *      the version-independent one
       * @return string the URL from the links block
       */
      public function findLink($type = 'self')
      {
          if (empty($this->links)) {
              return false;
          }
  
          foreach ($this->links as $link) {
              if ($link->rel == $type) {
                  return $link->href;
              }
          }
  
          return false;
      }
  
      /**
       * returns the URL used for Create
       *
       * @return string
       */
6d9380f96   Cédric Dupont   Update sources OC...
609
      public function createUrl()
31b7f2792   Kload   Upgrade to ownclo...
610
      {
6d9380f96   Cédric Dupont   Update sources OC...
611
          return $this->getParent()->getUrl($this->resourceName());
31b7f2792   Kload   Upgrade to ownclo...
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
      }
  
      /**
       * Returns the primary key field for the object
       *
       * The primary key is usually 'id', but this function is provided so that
       * (in rare cases where it is not 'id'), it can be overridden.
       *
       * @return string
       */
      protected function primaryKeyField()
      {
          return 'id';
      }
  
      /**
       * Returns the top-level document identifier for the returned response
       * JSON document; must be overridden in child classes
       *
       * For example, a server document is (JSON) `{"server": ...}` and an
       * Instance document is `{"instance": ...}` - this function must return
       * the top level document name (either "server" or "instance", in
       * these examples).
       *
6d9380f96   Cédric Dupont   Update sources OC...
636
       * @throws Exceptions\DocumentError if not overridden
31b7f2792   Kload   Upgrade to ownclo...
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
       */
      public static function jsonName()
      {
          if (isset(static::$json_name)) {
              return static::$json_name;
          }
  
          throw new Exceptions\DocumentError(sprintf(
              Lang::translate('No JSON object defined for class [%s] in JsonName()'),
              get_class()
          ));
      }
  
      /**
       * returns the collection JSON element name
       *
       * When an object is returned in a collection, it usually has a top-level
       * object that is an array holding child objects of the object types.
       * This static function returns the name of the top-level element. Usually,
       * that top-level element is simply the JSON name of the resource.'s';
       * however, it can be overridden by specifying the $json_collection_name
       * attribute.
       *
       * @return string
       */
      public static function jsonCollectionName()
      {
          if (isset(static::$json_collection_name)) {
              return static::$json_collection_name;
          } else {
              return static::$json_name . 's';
          }
      }
  
      /**
       * returns the JSON name for each element in a collection
       *
       * Usually, elements in a collection are anonymous; this function, however,
       * provides for an element level name:
       *
       *  `{ "collection" : [ { "element" : ... } ] }`
       *
       * @return string
       */
      public static function jsonCollectionElement()
      {
          if (isset(static::$json_collection_element)) {
              return static::$json_collection_element;
          }
      }
  
      /**
       * Returns the resource name for the URL of the object; must be overridden
       * in child classes
       *
       * For example, a server is `/servers/`, a database instance is
       * `/instances/`. Must be overridden in child classes.
       *
6d9380f96   Cédric Dupont   Update sources OC...
695
       * @throws Exceptions\UrlError
31b7f2792   Kload   Upgrade to ownclo...
696
697
698
699
700
701
702
703
704
705
706
707
       */
      public static function resourceName()
      {
          if (isset(static::$url_resource)) {
              return static::$url_resource;
          }
  
          throw new Exceptions\UrlError(sprintf(
              Lang::translate('No URL resource defined for class [%s] in ResourceName()'),
              get_class()
          ));
      }
6d9380f96   Cédric Dupont   Update sources OC...
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
      
      public function parseResponse(Response $response)
      {
          $body = Formatter::decode($response);
          
          $top = $this->jsonName();
              
          if ($top && isset($body->$top)) {
              $content = $body->$top;
          } else {
              $content = $body;
          }
          
          return $content;
      }
31b7f2792   Kload   Upgrade to ownclo...
723

6d9380f96   Cédric Dupont   Update sources OC...
724
  }