Skip to content

Commit a7db4aa

Browse files
committed
Merge php-opencloud/common
# Conflicts: # .travis.yml # composer.json # tests/integration/Utils.php # tests/unit/Fixtures/ComputeV2Api.php # tests/unit/Fixtures/IdentityV2Api.php # tests/unit/Fixtures/IdentityV3Api.php
2 parents f14f84d + 078508c commit a7db4aa

File tree

85 files changed

+5882
-4
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+5882
-4
lines changed

composer.json

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@
99
],
1010
"autoload": {
1111
"psr-4": {
12-
"OpenStack\\": "src/"
12+
"OpenStack\\": "src/",
13+
"OpenCloud\\": "src/"
1314
}
1415
},
1516
"autoload-dev": {
1617
"psr-4": {
1718
"OpenStack\\Test\\": "tests/unit/",
18-
"OpenStack\\Integration\\": "tests/integration/"
19+
"OpenStack\\Integration\\": "tests/integration/",
20+
"OpenCloud\\Test\\": "tests/unit/",
21+
"OpenCloud\\Integration\\": "tests/integration/"
1922
}
2023
},
2124
"repositories": [
@@ -25,7 +28,9 @@
2528
}
2629
],
2730
"require": {
28-
"php-opencloud/common": "^1.0.5"
31+
"php": "~7.0",
32+
"guzzlehttp/guzzle": "~6.1",
33+
"justinrainbow/json-schema": "~1.3"
2934
},
3035
"require-dev": {
3136
"phpunit/phpunit": "~4.0",

src/Common/Api/AbstractApi.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php declare (strict_types = 1);
2+
3+
namespace OpenCloud\Common\Api;
4+
5+
abstract class AbstractApi implements ApiInterface
6+
{
7+
protected $params;
8+
9+
protected function isRequired(array $param): array
10+
{
11+
return array_merge($param, ['required' => true]);
12+
}
13+
14+
protected function notRequired(array $param): array
15+
{
16+
return array_merge($param, ['required' => false]);
17+
}
18+
19+
protected function query(array $param): array
20+
{
21+
return array_merge($param, ['location' => AbstractParams::QUERY]);
22+
}
23+
24+
protected function url(array $param): array
25+
{
26+
return array_merge($param, ['location' => AbstractParams::URL]);
27+
}
28+
29+
public function documented(array $param): array
30+
{
31+
return array_merge($param, ['required' => true]);
32+
}
33+
}

src/Common/Api/AbstractParams.php

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php declare (strict_types=1);
2+
3+
namespace OpenCloud\Common\Api;
4+
5+
abstract class AbstractParams
6+
{
7+
// locations
8+
const QUERY = 'query';
9+
const HEADER = 'header';
10+
const URL = 'url';
11+
const JSON = 'json';
12+
const RAW = 'raw';
13+
14+
// types
15+
const STRING_TYPE = "string";
16+
const BOOL_TYPE = "boolean";
17+
const BOOLEAN_TYPE = self::BOOL_TYPE;
18+
const OBJECT_TYPE = "object";
19+
const ARRAY_TYPE = "array";
20+
const NULL_TYPE = "NULL";
21+
const INT_TYPE = 'integer';
22+
const INTEGER_TYPE = self::INT_TYPE;
23+
24+
public static function isSupportedLocation(string $val): bool
25+
{
26+
return in_array($val, [self::QUERY, self::HEADER, self::URL, self::JSON, self::RAW]);
27+
}
28+
29+
public function limit(): array
30+
{
31+
return [
32+
'type' => self::INT_TYPE,
33+
'location' => 'query',
34+
'description' => <<<DESC
35+
This will limit the total amount of elements returned in a list up to the number specified. For example, specifying a
36+
limit of 10 will return 10 elements, regardless of the actual count.
37+
DESC
38+
];
39+
}
40+
41+
public function marker(): array
42+
{
43+
return [
44+
'type' => 'string',
45+
'location' => 'query',
46+
'description' => <<<DESC
47+
Specifying a marker will begin the list from the value specified. Elements will have a particular attribute that
48+
identifies them, such as a name or ID. The marker value will search for an element whose identifying attribute matches
49+
the marker value, and begin the list from there.
50+
DESC
51+
];
52+
}
53+
54+
public function id(string $type): array
55+
{
56+
return [
57+
'description' => sprintf("The unique ID, or identifier, for the %s", $type),
58+
'type' => self::STRING_TYPE,
59+
'location' => self::JSON,
60+
];
61+
}
62+
63+
public function idPath(): array
64+
{
65+
return [
66+
'type' => self::STRING_TYPE,
67+
'location' => self::URL,
68+
'description' => 'The unique ID of the resource',
69+
];
70+
}
71+
72+
public function name(string $resource): array
73+
{
74+
return [
75+
'description' => sprintf("The name of the %s", $resource),
76+
'type' => self::STRING_TYPE,
77+
'location' => self::JSON,
78+
];
79+
}
80+
81+
82+
public function sortDir(): array
83+
{
84+
return [
85+
'type' => self::STRING_TYPE,
86+
'location' => self::QUERY,
87+
'description' => "Sorts by one or more sets of attribute and sort direction combinations.",
88+
'enum' => ['asc', 'desc']
89+
];
90+
}
91+
92+
public function sortKey(): array
93+
{
94+
return [
95+
'type' => self::STRING_TYPE,
96+
'location' => self::QUERY,
97+
'description' => "Sorts by one or more sets of attribute and sort direction combinations.",
98+
];
99+
}
100+
}

src/Common/Api/ApiInterface.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php declare (strict_types=1);
2+
3+
namespace OpenCloud\Common\Api;
4+
5+
/**
6+
* All classes which implement this interface are a data representation of a remote OpenCloud API.
7+
* They do not execute functionality, but instead return data for each API operation for other parts
8+
* of the SDK to use. Usually, the data is injected into {@see OpenCloud\Common\Api\Operation} objects.
9+
* The operation is then serialized into a {@see GuzzleHttp\Message\Request} and sent to the API.
10+
*
11+
* The reason for storing all the API-specific data is to decouple service information from client
12+
* HTTP functionality. Too often it is mixed all across different layers, leading to duplication and
13+
* no separation of concerns. The choice was made for storage in PHP classes, rather than YAML or JSON
14+
* syntax, due to performance concerns.
15+
*
16+
* @package OpenCloud\Common\Api
17+
*/
18+
interface ApiInterface
19+
{
20+
}

src/Common/Api/Operation.php

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
<?php declare (strict_types=1);
2+
3+
namespace OpenCloud\Common\Api;
4+
5+
/**
6+
* This class represents an OpenCloud API operation. It encapsulates most aspects of the REST operation: its HTTP
7+
* method, the URL path, its top-level JSON key, and all of its {@see Parameter} objects.
8+
*
9+
* An operation not only represents a remote operation, but it also provides the mechanism for executing it
10+
* over HTTP. To do this, it uses a {@see ClientInterface} that allows a {@see GuzzleHttp\Message\Request}
11+
* to be created from the user values provided. Once this request is assembled, it is then sent to the
12+
* remote API and the response is returned to whoever first invoked the Operation class.
13+
*
14+
* @package OpenCloud\Common\Api
15+
*/
16+
class Operation
17+
{
18+
/** @var string The HTTP method */
19+
private $method;
20+
21+
/** @var string The URL path */
22+
private $path;
23+
24+
/** @var string The top-level JSON key */
25+
private $jsonKey;
26+
27+
/** @var []Parameter The parameters of this operation */
28+
private $params;
29+
30+
/**
31+
* @param array $definition The data definition (in array form) that will populate this
32+
* operation. Usually this is retrieved from an {@see ApiInterface}
33+
* object method.
34+
*/
35+
public function __construct(array $definition)
36+
{
37+
$this->method = $definition['method'];
38+
$this->path = $definition['path'];
39+
40+
if (isset($definition['jsonKey'])) {
41+
$this->jsonKey = $definition['jsonKey'];
42+
}
43+
44+
$this->params = self::toParamArray($definition['params']);
45+
}
46+
47+
/**
48+
* @return string
49+
*/
50+
public function getPath(): string
51+
{
52+
return $this->path;
53+
}
54+
55+
/**
56+
* @return string
57+
*/
58+
public function getMethod(): string
59+
{
60+
return $this->method;
61+
}
62+
63+
/**
64+
* Indicates whether this operation supports a parameter.
65+
*
66+
* @param $key The name of a parameter
67+
*
68+
* @return bool
69+
*/
70+
public function hasParam(string $key): bool
71+
{
72+
return isset($this->params[$key]);
73+
}
74+
75+
/**
76+
* @param $name
77+
*
78+
* @return Parameter
79+
*/
80+
public function getParam(string $name)
81+
{
82+
return isset($this->params[$name]) ? $this->params[$name] : null;
83+
}
84+
85+
/**
86+
* @return string
87+
*/
88+
public function getJsonKey(): string
89+
{
90+
return $this->jsonKey ?: '';
91+
}
92+
93+
/**
94+
* A convenience method that will take a generic array of data and convert it into an array of
95+
* {@see Parameter} objects.
96+
*
97+
* @param array $data A generic data array
98+
*
99+
* @return array
100+
*/
101+
public static function toParamArray(array $data): array
102+
{
103+
$params = [];
104+
105+
foreach ($data as $name => $param) {
106+
$params[$name] = new Parameter($param + ['name' => $name]);
107+
}
108+
109+
return $params;
110+
}
111+
112+
/**
113+
* This method will validate all of the user-provided values and throw an exception if any
114+
* failures are detected. This is useful for basic sanity-checking before a request is
115+
* serialized and sent to the API.
116+
*
117+
* @param array $userValues The user-defined values
118+
*
119+
* @return bool TRUE if validation passes
120+
* @throws \Exception If validate fails
121+
*/
122+
public function validate(array $userValues): bool
123+
{
124+
foreach ($this->params as $paramName => $param) {
125+
if (array_key_exists($paramName, $userValues)) {
126+
$param->validate($userValues[$paramName]);
127+
} elseif ($param->isRequired()) {
128+
throw new \Exception(sprintf('"%s" is a required option, but it was not provided', $paramName));
129+
}
130+
}
131+
132+
return true;
133+
}
134+
}

src/Common/Api/OperatorInterface.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php declare (strict_types=1);
2+
3+
namespace OpenCloud\Common\Api;
4+
5+
use GuzzleHttp\ClientInterface;
6+
use GuzzleHttp\Promise\PromiseInterface;
7+
use OpenCloud\Common\Resource\ResourceInterface;
8+
use Psr\Http\Message\ResponseInterface;
9+
10+
/**
11+
* An operator is any resource or service that can invoke and send REST operations. In other words, it
12+
* is any class that can send requests and receive responses with a HTTP client. To do this
13+
* it needs two things: a {@see ClientInterface} for handling HTTP transactions and an {@see ApiInterface}
14+
* for handling how operations are created.
15+
*
16+
* @package OpenCloud\Common\Api
17+
*/
18+
interface OperatorInterface
19+
{
20+
/**
21+
* @param ClientInterface $client The HTTP client responsible for handling HTTP transactions
22+
* @param ApiInterface $api The data API class that dictates how REST operations are structured
23+
*/
24+
public function __construct(ClientInterface $client, ApiInterface $api);
25+
26+
/**
27+
* A convenience method that assembles an operation and sends it to the remote API
28+
*
29+
* @param array $definition The data that dictates how the operation works
30+
* @param array $userValues The user-defined values that populate the request
31+
*
32+
* @return \Psr\Http\Message\ResponseInterface
33+
*/
34+
public function execute(array $definition, array $userValues = []): ResponseInterface;
35+
36+
/**
37+
* A convenience method that assembles an operation and asynchronously sends it to the remote API
38+
*
39+
* @param array $definition The data that dictates how the operation works
40+
* @param array $userValues The user-defined values that populate the request
41+
*
42+
* @return \GuzzleHttp\Promise\PromiseInterface
43+
*/
44+
public function executeAsync(array $definition, array $userValues = []): PromiseInterface;
45+
46+
/**
47+
* Retrieves a populated Operation according to the definition and values provided. A
48+
* HTTP client is also injected into the object to allow it to communicate with the remote API.
49+
*
50+
* @param array $definition The data that dictates how the operation works
51+
*
52+
* @return Operation
53+
*/
54+
public function getOperation(array $definition): Operation;
55+
56+
/**
57+
* @param string $class The name of the model class.
58+
* @param mixed $data Either a {@see ResponseInterface} or data array that will populate the newly
59+
* created model class.
60+
*
61+
* @return \OpenCloud\Common\Resource\ResourceInterface
62+
*/
63+
public function model(string $class, $data = null): ResourceInterface;
64+
}

0 commit comments

Comments
 (0)