Skip to content

Commit d96eb89

Browse files
committed
Merge branch '1.x' into merge-1-x-2-x
2 parents c06e200 + 3ba1dde commit d96eb89

40 files changed

+1240
-102
lines changed

composer.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Connection/Connector.php

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,23 @@ class Connector implements ConnectorInterface
5858
* @param array $config
5959
* Possible configuration keys are:
6060
* - api_url (string): The API base URL.
61-
* - token_url (string): The OAuth 2.0 token URL.
62-
* - revoke_url (string): The OAuth 2.0 revocation URL.
63-
* - certifier_url (string): The SSH certificate issuer URL.
61+
* - auth_url (string): The Auth API URL.
62+
* - centralized_permissions_enabled (bool): Whether the Centralized User Management API is enabled.
63+
* - strict_project_references (bool): Whether to throw an exception if project references cannot be resolved.
64+
* - token_url (string): The OAuth 2.0 token URL. Can be empty if auth_url is set.
65+
* - revoke_url (string): The OAuth 2.0 revocation URL. Can be empty if auth_url is set.
66+
* - certifier_url (string): The SSH certificate issuer URL. Can be empty if auth_url is set.
6467
* - client_id (string): The OAuth2 client ID for this client.
6568
* - debug (bool): Whether or not Guzzle debugging should be enabled
6669
* (default: false).
6770
* - verify (bool): Whether or not SSL verification should be enabled
6871
* (default: true).
6972
* - user_agent (string): The HTTP User-Agent for API requests.
73+
* - headers (array<string, string>): Additional headers to send to the API (an associative array of header names and values).
74+
* - subscribers (\SubscriberInterface[]): Additional Guzzle subscribers (an array of SubscriberInterface objects).
75+
* - cache (array|bool): Caching. Set to true to enable in-memory
76+
* caching, to false (the default) to disable caching, or to an array
77+
* of options as expected by the Guzzle cache subscriber.
7078
* - proxy (array|string): A proxy setting, passed to Guzzle directly.
7179
* Use a string to specify an HTTP proxy, or an array to specify
7280
* different proxies for different protocols.
@@ -77,6 +85,9 @@ class Connector implements ConnectorInterface
7785
* - on_refresh_error: A callback to run when a refresh token error is
7886
* received. It will be passed a Guzzle BadResponseException, and
7987
* should return an AccessToken or null.
88+
* - on_step_up_auth_response: A callback to run when a refresh token error is
89+
* received. It will be passed a Guzzle ResponseInterface, and
90+
* should return an AccessToken or null.
8091
* @param SessionInterface $session
8192
*/
8293
public function __construct(array $config = [], SessionInterface $session = null)
@@ -93,24 +104,44 @@ public function __construct(array $config = [], SessionInterface $session = null
93104
'debug' => false,
94105
'verify' => true,
95106
'user_agent' => null,
107+
'headers' => [],
108+
'subscribers' => [],
96109
'cache' => false,
97-
'revoke_url' => 'https://auth.api.platform.sh/oauth2/revoke',
98-
'token_url' => 'https://auth.api.platform.sh/oauth2/token',
99-
'certifier_url' => 'https://ssh.api.platform.sh',
110+
'auth_url' => 'https://auth.api.platform.sh',
111+
'revoke_url' => '',
112+
'token_url' => '',
113+
'certifier_url' => '',
114+
'centralized_permissions_enabled' => false,
115+
'strict_project_references' => false,
100116
'proxy' => null,
101117
'timeout' => 60.0,
102118
'connect_timeout' => 60.0,
103119
'api_token' => null,
104120
'api_token_type' => 'exchange',
105121
'gzip' => extension_loaded('zlib'),
122+
'on_refresh_start' => null,
123+
'on_refresh_end' => null,
106124
'on_refresh_error' => null,
125+
'on_step_up_auth_response' => null,
107126
];
108127
$this->config = $config + $defaults;
109128

110129
if (!isset($this->config['user_agent'])) {
111130
$this->config['user_agent'] = $this->defaultUserAgent();
112131
}
113132

133+
if (!empty($this->config['auth_url'])) {
134+
if (empty($this->config['token_url'])) {
135+
$this->config['token_url'] = rtrim($this->config['auth_url'], '/') . '/oauth2/token';
136+
}
137+
if (empty($this->config['revoke_url'])) {
138+
$this->config['revoke_url'] = rtrim($this->config['auth_url'], '/') . '/oauth2/revoke';
139+
}
140+
if (empty($this->config['certifier_url'])) {
141+
$this->config['certifier_url'] = $this->config['auth_url'];
142+
}
143+
}
144+
114145
if (isset($session)) {
115146
$this->session = $session;
116147
} else {
@@ -255,6 +286,16 @@ public function getSession()
255286
return $this->session;
256287
}
257288

289+
/**
290+
* Returns the access token saved in the session, if any.
291+
*
292+
* @return false|string
293+
*/
294+
public function getAccessToken()
295+
{
296+
return $this->session->get('accessToken');
297+
}
298+
258299
/**
259300
* {@inheritdoc}
260301
*
@@ -382,13 +423,18 @@ protected function getOauthMiddleware()
382423
$this->oauthMiddleware->setAccessToken($accessToken);
383424
}
384425

385-
$this->oauthMiddleware->setTokenSaveCallback(function (AccessToken $token) {
386-
$this->saveToken($token);
387-
});
388-
389426
// @todo
427+
// if ($this->config['on_refresh_start'] !== null) {
428+
// $this->oauth2Plugin->setOnRefreshStart($this->config['on_refresh_start']);
429+
// }
430+
// if ($this->config['on_refresh_end'] !== null) {
431+
// $this->oauth2Plugin->setOnRefreshEnd($this->config['on_refresh_end']);
432+
// }
390433
// if ($this->config['on_refresh_error'] !== null) {
391-
// $this->oauthMiddleware->setOnRefreshError($this->config['on_refresh_error']);
434+
// $this->oauth2Plugin->setOnRefreshError($this->config['on_refresh_error']);
435+
// }
436+
// if ($this->config['on_step_up_auth_response'] !== null) {
437+
// $this->oauth2Plugin->setOnStepUpAuthResponse($this->config['on_step_up_auth_response']);
392438
// }
393439
}
394440

@@ -430,6 +476,14 @@ public function getClient()
430476
'auth' => 'oauth2',
431477
];
432478

479+
if (!empty($this->config['headers'])) {
480+
$options['defaults']['headers'] += $this->config['headers'];
481+
}
482+
483+
if (!empty($this->config['subscribers'])) {
484+
$options['defaults']['subscribers'] = array_merge($options['defaults']['subscribers'], $this->config['subscribers']);
485+
}
486+
433487
if ($this->config['gzip']) {
434488
$config['decode_content'] = true;
435489
$config['headers']['Accept-Encoding'] = 'gzip';

src/DataStructure/ReadOnlyStructureTrait.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,26 @@ public function getProperties()
9797
{
9898
return $this->data;
9999
}
100+
101+
/**
102+
* Gets a single property.
103+
*
104+
* @param bool $required
105+
*
106+
* @throws \InvalidArgumentException if $required is true and the property is not set
107+
*
108+
* @return mixed|null
109+
* Returns the property value, or null if $required is false and the property is not set.
110+
*/
111+
public function getProperty($property, $required = true)
112+
{
113+
if (!array_key_exists($property, $this->data)) {
114+
if ($required) {
115+
throw new \InvalidArgumentException("Property not found: $property");
116+
}
117+
return null;
118+
}
119+
120+
return $this->data[$property];
121+
}
100122
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Platformsh\Client\Exception;
4+
5+
/**
6+
* An exception thrown when a project reference cannot be resolved.
7+
*/
8+
class ProjectReferenceException extends \RuntimeException
9+
{
10+
protected $projectId;
11+
12+
/**
13+
* @param string $projectId
14+
* @param string|null $message
15+
* @param \Exception|null $previous
16+
*/
17+
public function __construct($projectId, $message = null, \Exception $previous = null)
18+
{
19+
$this->projectId = $projectId;
20+
$message = $message ?: 'Cannot resolve reference for project: ' . $projectId;
21+
parent::__construct($message, 0, $previous);
22+
}
23+
24+
public function getProjectId()
25+
{
26+
return $this->projectId;
27+
}
28+
}

src/Model/ActivityLog/LogItem.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ public static function multipleFromJsonStream($str)
6464

6565
/**
6666
* @param string $str
67-
* @return array
67+
* @return array|null
6868
*/
6969
private static function decode($str)
7070
{
7171
$data = json_decode($str, true);
7272
if ($data === null) {
73-
throw new \RuntimeException('Failed to decode JSON with message: ' . json_last_error_msg() . ':' . "\n" . $data);
73+
trigger_error(sprintf('Failed to decode JSON line with message: %s: %s', json_last_error_msg(), $str), E_USER_WARNING);
7474
}
7575
return $data;
7676
}

src/Model/ApiResourceBase.php

Lines changed: 64 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ abstract class ApiResourceBase implements \ArrayAccess
3838
/** @var bool */
3939
protected $isFull = false;
4040

41+
/** @var Collection|null */
42+
protected $parentCollection;
43+
4144
/**
4245
* Resource constructor.
4346
*
@@ -228,7 +231,6 @@ public static function create(array $body, $collectionUrl, ClientInterface $clie
228231
*/
229232
public static function send(RequestInterface $request, ClientInterface $client, array $options = [])
230233
{
231-
$response = null;
232234
try {
233235
$response = $client->send($request, $options);
234236
$body = $response->getBody()->getContents();
@@ -306,7 +308,7 @@ protected static function checkProperty($property, $value)
306308
}
307309

308310
/**
309-
* Get a collection of resources.
311+
* Returns a list of resources (a collection).
310312
*
311313
* @param string $url The collection URL.
312314
* @param int $limit A limit on the number of resources to
@@ -319,41 +321,61 @@ protected static function checkProperty($property, $value)
319321
*/
320322
public static function getCollection($url, $limit, array $options, ClientInterface $client)
321323
{
322-
// @todo uncomment this when the API implements a 'count' parameter
323-
// if ($limit) {
324-
// $options['query']['count'] = $limit;
325-
// }
326-
$request = new Request('get', $url);
327-
$data = self::send($request, $client, $options);
324+
$items = static::getCollectionWithParent($url, $client, $options)['items'];
328325

329-
// @todo remove this when the API implements a 'count' parameter
330-
if (!empty($limit) && count($data) > $limit) {
331-
$data = array_slice($data, 0, $limit);
326+
if (!empty($limit) && count($items) > $limit) {
327+
$items = array_slice($items, 0, $limit);
332328
}
333329

334-
return static::wrapCollection($data, $url, $client);
330+
return $items;
331+
}
332+
333+
/**
334+
* Returns a list of resources and the Collection that contained them.
335+
*
336+
* @param string $url The collection URL.
337+
* @param ClientInterface $client A suitably configured Guzzle client.
338+
* @param array $options An array of additional Guzzle request
339+
* options.
340+
*
341+
* @return array{items: static[], collection: Collection}
342+
*/
343+
public static function getCollectionWithParent($url, ClientInterface $client, array $options = [])
344+
{
345+
$request = $client->createRequest('GET', $url, $options);
346+
$data = self::send($request, $client);
347+
$collection = new Collection($data, $client, $url);
348+
return ['items' => static::wrapCollection($collection, $url, $client), 'collection' => $collection];
335349
}
336350

337351
/**
338352
* Create an array of resource instances from a collection's JSON data.
339353
*
340-
* @param array $data The deserialized JSON from the
341-
* collection (i.e. a list of resources,
342-
* each of which is an array of data).
343-
* @param string $baseUrl The URL to the collection.
344-
* @param ClientInterface $client A suitably configured Guzzle client.
354+
* @param array|Collection $data The deserialized JSON from the
355+
* collection (i.e. a list of resources,
356+
* each of which is an array of data).
357+
* @param string $baseUrl The URL to the collection.
358+
* @param ClientInterface $client A suitably configured Guzzle client.
345359
*
346360
* @return static[]
347361
*/
348-
public static function wrapCollection(array $data, $baseUrl, ClientInterface $client)
362+
public static function wrapCollection($data, $baseUrl, ClientInterface $client)
349363
{
364+
if ($data instanceof Collection) {
365+
$parent = $data;
366+
$data = $data->getData();
367+
} else {
368+
$parent = new Collection($data, $client, $baseUrl);
369+
}
350370
$resources = [];
351371
$items = $data;
352372
if (isset(static::$collectionItemsKey)) {
353373
$items = $items[static::$collectionItemsKey];
354374
}
355375
foreach ($items as $item) {
356-
$resources[] = new static($item, $baseUrl, $client);
376+
$resource = new static($item, $baseUrl, $client);
377+
$resource->setParentCollection($parent);
378+
$resources[] = $resource;
357379
}
358380

359381
return $resources;
@@ -667,4 +689,27 @@ protected function isProperty($key)
667689
{
668690
return $key !== '_links' && $key !== '_embedded';
669691
}
692+
693+
/**
694+
* Returns the wrapping collection, if this resource's data was fetched via one.
695+
*
696+
* Useful for pagination.
697+
*
698+
* @return Collection|null
699+
*/
700+
public function getParentCollection()
701+
{
702+
return $this->parentCollection;
703+
}
704+
705+
/**
706+
* Sets a parent collection for this resource.
707+
*
708+
* @param Collection $parent
709+
* @return void
710+
*/
711+
protected function setParentCollection(Collection $parent)
712+
{
713+
$this->parentCollection = $parent;
714+
}
670715
}

src/Model/Backup.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
namespace Platformsh\Client\Model;
44

5+
use Platformsh\Client\Model\Backups\RestoreOptions;
6+
57
/**
68
* An environment backup.
79
*
@@ -19,4 +21,16 @@ class Backup extends ApiResourceBase
1921
{
2022
const STATUS_CREATED = 'CREATED';
2123
const STATUS_DELETED = 'DELETED';
24+
25+
/**
26+
* Restores a backup.
27+
*
28+
* @param RestoreOptions|null $options
29+
*
30+
* @return Result
31+
*/
32+
public function restore(RestoreOptions $options = null)
33+
{
34+
return $this->runOperation('restore', 'POST', $options ? $options->toArray() : []);
35+
}
2236
}

0 commit comments

Comments
 (0)