1 <?php
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
17
18 namespace OpenCloud\Common\Resource;
19
20 use Guzzle\Http\Url;
21 use OpenCloud\Common\Constants\State;
22 use OpenCloud\Common\Exceptions\CreateError;
23 use OpenCloud\Common\Exceptions\DeleteError;
24 use OpenCloud\Common\Exceptions\IdRequiredError;
25 use OpenCloud\Common\Exceptions\NameError;
26 use OpenCloud\Common\Exceptions\UnsupportedExtensionError;
27 use OpenCloud\Common\Exceptions\UpdateError;
28
29 abstract class PersistentResource extends BaseResource
30 {
31 32 33 34 35 36
37 public function create($params = array())
38 {
39
40 if (!empty($params)) {
41 $this->populate($params, false);
42 }
43
44
45 $json = json_encode($this->createJson());
46 $this->checkJsonError();
47
48 $createUrl = $this->createUrl();
49
50 $response = $this->getClient()->post($createUrl, self::getJsonHeader(), $json)->send();
51
52
53
54
55 if (null !== ($decoded = $this->parseResponse($response))) {
56 $this->populate($decoded);
57 } elseif ($location = $response->getHeader('Location')) {
58 $this->refreshFromLocationUrl($location);
59 }
60
61 return $response;
62 }
63
64 65 66 67 68 69
70 public function update($params = array())
71 {
72
73 if (!empty($params)) {
74 $this->populate($params);
75 }
76
77
78 $json = json_encode($this->updateJson($params));
79 $this->checkJsonError();
80
81
82 return $this->getClient()->put($this->getUrl(), self::getJsonHeader(), $json)->send();
83 }
84
85 86 87 88 89
90 public function delete()
91 {
92 return $this->getClient()->delete($this->getUrl())->send();
93 }
94
95 96 97 98 99 100 101 102
103 public function refresh($id = null, $url = null)
104 {
105 $primaryKey = $this->primaryKeyField();
106 $primaryKeyVal = $this->getProperty($primaryKey);
107
108 if (!$url) {
109 if (!$id = $id ?: $primaryKeyVal) {
110 $message = sprintf("This resource cannot be refreshed because it has no %s", $primaryKey);
111 throw new IdRequiredError($message);
112 }
113
114 if ($primaryKeyVal != $id) {
115 $this->setProperty($primaryKey, $id);
116 }
117
118 $url = $this->getUrl();
119 }
120
121
122 if ($this->getProperty('status')) {
123 $this->setProperty('status', null);
124 }
125
126 $response = $this->getClient()->get($url)->send();
127
128 if (null !== ($decoded = $this->parseResponse($response))) {
129 $this->populate($decoded);
130 }
131
132 return $response;
133 }
134
135
136 137 138
139 protected function refreshFromParent()
140 {
141 $url = clone $this->getParent()->getUrl();
142 $url->addPath($this->resourceName());
143
144 $response = $this->getClient()->get($url)->send();
145
146 if (null !== ($decoded = $this->parseResponse($response))) {
147 $this->populate($decoded);
148 }
149 }
150
151 152 153 154 155
156 public function refreshFromLocationUrl($url)
157 {
158 $fullUrl = Url::factory($url);
159
160 $response = $this->getClient()->get($fullUrl)->send();
161
162 if (null !== ($decoded = $this->parseResponse($response))) {
163 $this->populate($decoded);
164 }
165 }
166
167 168 169 170 171 172 173 174
175 public function waitFor($state = null, $timeout = null, $callback = null, $interval = null)
176 {
177 $state = $state ?: State::ACTIVE;
178 $timeout = $timeout ?: State::DEFAULT_TIMEOUT;
179 $interval = $interval ?: State::DEFAULT_INTERVAL;
180
181
182 $startTime = time();
183
184 $states = array('ERROR', $state);
185
186 while (true) {
187 $this->refresh($this->getProperty($this->primaryKeyField()));
188
189 if ($callback) {
190 call_user_func($callback, $this);
191 }
192
193 if (in_array($this->status(), $states) || (time() - $startTime) > $timeout) {
194 return;
195 }
196
197 sleep($interval);
198 }
199 }
200
201 202 203 204 205 206
207 protected function createJson()
208 {
209 if (!isset($this->createKeys)) {
210 throw new \RuntimeException(sprintf(
211 'This resource object [%s] must have a visible createKeys array',
212 get_class($this)
213 ));
214 }
215
216 $element = (object) array();
217
218 foreach ($this->createKeys as $key) {
219 if (null !== ($property = $this->getProperty($key))) {
220 $element->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($property);
221 }
222 }
223
224 if (isset($this->metadata) && count($this->metadata)) {
225 $element->metadata = (object) $this->metadata->toArray();
226 }
227
228 return (object) array($this->jsonName() => (object) $element);
229 }
230
231 232 233 234 235 236 237
238 protected function getAlias($key)
239 {
240 if (false !== ($alias = array_search($key, $this->aliases))) {
241 return $alias;
242 }
243
244 return $key;
245 }
246
247 248 249 250 251 252 253 254
255 protected function recursivelyAliasPropertyValue($propertyValue)
256 {
257 if (is_array($propertyValue)) {
258 foreach ($propertyValue as $key => $subValue) {
259 $aliasedSubValue = $this->recursivelyAliasPropertyValue($subValue);
260 if (is_numeric($key)) {
261 $propertyValue[$key] = $aliasedSubValue;
262 } else {
263 unset($propertyValue[$key]);
264 $propertyValue[$this->getAlias($key)] = $aliasedSubValue;
265 }
266 }
267 } elseif (is_object($propertyValue) && ($propertyValue instanceof \stdClass)) {
268 foreach ($propertyValue as $key => $subValue) {
269 unset($propertyValue->$key);
270 $propertyValue->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($subValue);
271 }
272 }
273
274 return $propertyValue;
275 }
276
277 278 279
280 protected function updateJson($params = array())
281 {
282 if (!isset($this->updateKeys)) {
283 throw new \RuntimeException(sprintf(
284 'This resource object [%s] must have a visible updateKeys array',
285 get_class($this)
286 ));
287 }
288
289 $element = (object) array();
290
291 foreach ($this->updateKeys as $key) {
292 if (null !== ($property = $this->getProperty($key))) {
293 $element->{$this->getAlias($key)} = $this->recursivelyAliasPropertyValue($property);
294 }
295 }
296
297 return (object) array($this->jsonName() => (object) $element);
298 }
299
300 301 302
303 protected function noCreate()
304 {
305 throw new CreateError('This resource does not support the create operation');
306 }
307
308 309 310
311 protected function noDelete()
312 {
313 throw new DeleteError('This resource does not support the delete operation');
314 }
315
316 317 318
319 protected function noUpdate()
320 {
321 throw new UpdateError('his resource does not support the update operation');
322 }
323
324 325 326 327 328 329 330
331 public function checkExtension($alias)
332 {
333 if (!in_array($alias, $this->getService()->namespaces())) {
334 throw new UnsupportedExtensionError(sprintf("%s extension is not installed", $alias));
335 }
336
337 return true;
338 }
339
340
341
342 343 344 345 346
347 public function name()
348 {
349 if (null !== ($name = $this->getProperty('name'))) {
350 return $name;
351 } else {
352 throw new NameError('Name attribute does not exist for this resource');
353 }
354 }
355
356 357 358 359
360 public function id()
361 {
362 return $this->id;
363 }
364
365 366 367 368
369 public function status()
370 {
371 return (isset($this->status)) ? $this->status : 'N/A';
372 }
373
374 375 376 377
378 public function region()
379 {
380 return $this->getService()->region();
381 }
382
383 384 385 386
387 public function createUrl()
388 {
389 return $this->getParent()->getUrl($this->resourceName());
390 }
391 }
392