diff --git a/client/index.html b/client/index.html
index 48ea4a0..a06994c 100644
--- a/client/index.html
+++ b/client/index.html
@@ -89,6 +89,7 @@
+
diff --git a/client/js/app.js b/client/js/app.js
index e2d5b9b..1602e65 100644
--- a/client/js/app.js
+++ b/client/js/app.js
@@ -13,12 +13,16 @@ mainApp.config(['$routeProvider', function($routeProvider){
$routeProvider.
when('/home',{
- templateUrl: 'partials/home/main.html',
+ templateUrl: 'partials/home/home.html',
controller: 'homeCtrl'
}).
when('/network',{
- templateUrl: 'partials/network/main.html',
+ templateUrl: 'partials/network/network.html',
controller: 'networkCtrl'
+ }).
+ when('/image',{
+ templateUrl: 'partials/image/image.html',
+ controller: 'imageCtrl'
}).otherwise({
redirectTo: '/home'
});
diff --git a/client/js/controllers/home/home.js b/client/js/controllers/home/home.js
index f84f625..4402e62 100644
--- a/client/js/controllers/home/home.js
+++ b/client/js/controllers/home/home.js
@@ -3,7 +3,7 @@
*
* @param {$scope} $scope The $scope service from angular
*/
-mainApp.controller('homeCtrl', [ '$scope', 'Compute', '$rootScope', 'Loading','Identity', function ($scope, Compute, $rootScope, Loading, Identity)
+mainApp.controller('homeCtrl', [ '$scope', 'Compute', '$rootScope', 'Loading','Identity', 'Image', function ($scope, Compute, $rootScope, Loading, Identity, Image)
{
var callMeAfterPullData=function(data){
@@ -16,8 +16,14 @@ mainApp.controller('homeCtrl', [ '$scope', 'Compute', '$rootScope', 'Loading','I
Loading.start();
Compute.pullData(callMeAfterPullData);
}
+ else{
+ if(Identity.isAlreadyLogin()){
+ callMeAfterPullData();
+ }
+ }
-
+
+ Image.getImages(function(){});
$scope.raiseShowMachineDetailsEvent=function(id){
diff --git a/client/js/controllers/image/image.js b/client/js/controllers/image/image.js
new file mode 100644
index 0000000..e298fcc
--- /dev/null
+++ b/client/js/controllers/image/image.js
@@ -0,0 +1,21 @@
+/**
+ * The image controller
+ *
+ * @param {$scope} $scope The $scope service from angular
+ */
+mainApp.controller('imageCtrl', ['$scope', 'Image', 'Loading', function ($scope, Image, Loading)
+{
+ var callbackTest=function(){
+ $scope.images=Image.getData().images;
+ Loading.stop();
+ };
+
+ if(Image.getData().images==null){
+ Loading.start();
+ Image.getImages(callbackTest);
+ }
+ else{
+ callbackTest();
+ }
+
+}]);
diff --git a/client/js/services/Image.js b/client/js/services/Image.js
index 23b33a8..2e8c56f 100644
--- a/client/js/services/Image.js
+++ b/client/js/services/Image.js
@@ -1,27 +1,58 @@
mainApp.factory('Image',[ '$http', 'Identity', function($http, Identity){
+ var data={};
+ data.images=null;
var parseUploadImageAnswer=function(response, failedToSendRequest){
+
+ // Defined return object
+ var requestParserResult={};
+ requestParserResult.status=1;
+ requestParserResult.failReason=null;
+
+ if (typeof response.data.Images !== 'undefined') {
+ // Set status code
+ requestParserResult.status=0;
+ data.images=response.data.Images;
+
+ }
+ else if(failedToSendRequest){
+ requestParserResult.failReason="Failed to send request";
+ }
+ else{
+ requestParserResult.failReason="Error";
+ }
+ return requestParserResult;
};
var getImages=function(callback){
var result=$http.post('../server/index.php',
- $.param({"token" : Identity.profile.token, "task" : "Image"}));
+ $.param({"token" : Identity.getToken(), "task" : "image", 'action':'listImage'}));
+
+ // Wait and handle the response
+ result.then(function (response){
+ callback(parseUploadImageAnswer(response, false));
+ },function(response){
+ callback(parseUploadImageAnswer(response, true));
+ });
-
};
+ var getData=function(response){
+ return data;
+ };
// Return services objects
return {
- uploadImage: uploadImage
+ getImages:getImages,
+ getData:getData
};
diff --git a/client/partials/home/main.html b/client/partials/home/home.html
similarity index 100%
rename from client/partials/home/main.html
rename to client/partials/home/home.html
diff --git a/client/partials/image/image.html b/client/partials/image/image.html
new file mode 100644
index 0000000..8af8af5
--- /dev/null
+++ b/client/partials/image/image.html
@@ -0,0 +1,10 @@
+
+
+ Images disponibles
+
+
+
diff --git a/client/partials/menu.html b/client/partials/menu.html
index dfd5842..a473fd8 100644
--- a/client/partials/menu.html
+++ b/client/partials/menu.html
@@ -1,4 +1,5 @@
\ No newline at end of file
+ Images
+
diff --git a/client/partials/network/main.html b/client/partials/network/network.html
similarity index 100%
rename from client/partials/network/main.html
rename to client/partials/network/network.html
diff --git a/server/Test/AppTestClass.php b/server/Test/AppTestClass.php
index 6685ec7..b398088 100755
--- a/server/Test/AppTestClass.php
+++ b/server/Test/AppTestClass.php
@@ -1,6 +1,7 @@
>>>>>> Stashed changes
+=======
+include_once("../core/ErrorManagement.php");
+
+use OpenStack\Common\Error\BadResponseError;
+use OpenStack\Common\Error\BaseError;
+use OpenStack\Common\Error\NotImplementedError;
+use OpenStack\Common\Error\UserInputError;
+>>>>>>> develop
class AppTest{
@@ -19,6 +28,7 @@ class AppTest{
protected $tokenClass;
protected $tokenPost;
protected $output;
+ protected $errorClass;
public function __construct($args){
@@ -28,6 +38,9 @@ class AppTest{
$this->pluginsApi = plugin_api::getInstance();
$this->errorClass = new errorManagement($this);
$this->output = array();
+
+ $this->errorClass = new errorManagement($this);
+
$this->postParams = $_POST;
}
@@ -103,4 +116,12 @@ class AppTest{
return json_encode($this->output);
}
+
+ public function getErrorInstance(){
+
+ return $this->errorClass;
+
+ }
}
+
+
diff --git a/server/composer.json b/server/composer.json
index d5e2dbc..9d44d6e 100644
--- a/server/composer.json
+++ b/server/composer.json
@@ -1,5 +1,6 @@
{
"require": {
- "php-opencloud/openstack": "dev-master"
+ "php-opencloud/openstack": "dev-master",
+ "php-opencloud/common": "dev-master"
}
}
diff --git a/server/composer.lock b/server/composer.lock
index 198bbed..6ab8a7f 100644
--- a/server/composer.lock
+++ b/server/composer.lock
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "e7cbd5d3df36fb1a3f3378b837068196",
- "content-hash": "125e4702f7a417475a4150c889ac6c3d",
+ "hash": "7647ed348aee68f2db6b71e4d2fb1fe7",
+ "content-hash": "82731500d050a65f09e92b70c1f96ea9",
"packages": [
{
"name": "guzzlehttp/guzzle",
@@ -244,6 +244,51 @@
],
"time": "2016-01-25 15:43:01"
},
+ {
+ "name": "php-opencloud/common",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-opencloud/common.git",
+ "reference": "fd027b817c3dd8f83b0c8d9fb1df34ebf25f8c62"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-opencloud/common/zipball/fd027b817c3dd8f83b0c8d9fb1df34ebf25f8c62",
+ "reference": "fd027b817c3dd8f83b0c8d9fb1df34ebf25f8c62",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/guzzle": "~6.1",
+ "justinrainbow/json-schema": "~1.3",
+ "php": "~7.0"
+ },
+ "require-dev": {
+ "fabpot/php-cs-fixer": "~1.0",
+ "jakub-onderka/php-parallel-lint": "0.*",
+ "phpunit/phpunit": "~4.0",
+ "psr/log": "~1.0",
+ "sami/sami": "dev-master",
+ "satooshi/php-coveralls": "~1.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "OpenCloud\\": "src/",
+ "OpenCloud\\Test\\": "tests/unit/",
+ "OpenCloud\\Integration\\": "tests/integration/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "authors": [
+ {
+ "name": "Jamie Hannaford",
+ "email": "jamie.hannaford@rackspace.com",
+ "homepage": "https://github.com/jamiehannaford"
+ }
+ ],
+ "time": "2016-03-08 12:56:34"
+ },
{
"name": "php-opencloud/openstack",
"version": "dev-master",
@@ -254,8 +299,12 @@
},
"dist": {
"type": "zip",
+<<<<<<< HEAD
<<<<<<< Updated upstream
"url": "https://api.github.com/repos/php-opencloud/openstack/zipball/15aca73f423166c7ef8337ba08615c103c66e931",
+=======
+ "url": "https://api.github.com/repos/php-opencloud/openstack/zipball/f2ee77024843659d970817a9e7055bb40a3724f9",
+>>>>>>> develop
"reference": "15aca73f423166c7ef8337ba08615c103c66e931",
=======
"url": "https://api.github.com/repos/php-opencloud/openstack/zipball/f2ee77024843659d970817a9e7055bb40a3724f9",
@@ -344,7 +393,8 @@
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
- "php-opencloud/openstack": 20
+ "php-opencloud/openstack": 20,
+ "php-opencloud/common": 20
},
"prefer-stable": false,
"prefer-lowest": false,
diff --git a/server/core/App.php b/server/core/App.php
index de886a6..d35ccc0 100755
--- a/server/core/App.php
+++ b/server/core/App.php
@@ -3,10 +3,10 @@ include_once("core/Plugin_Api.php");
include_once("core/LibOverride/genTokenOptions.php");
include_once("core/ErrorManagement.php");
-use OpenStack\Common\Error\BadResponseError;
-use OpenStack\Common\Error\BaseError;
-use OpenStack\Common\Error\NotImplementedError;
-use OpenStack\Common\Error\UserInputError;
+use OpenCloud\Common\Error\BadResponseError;
+use OpenCloud\Common\Error\BaseError;
+use OpenCloud\Common\Error\NotImplementedError;
+use OpenCloud\Common\Error\UserInputError;
class App{
diff --git a/server/core/Compute.php b/server/core/Compute.php
index a5b8375..f02eb8b 100644
--- a/server/core/Compute.php
+++ b/server/core/Compute.php
@@ -12,11 +12,11 @@ class compute
protected $libClass;
- public function __construct($app)
- {
- $this->app = $app;
- $this->libClass = $app->getLibClass("Compute");
- }
+ public function __construct($app)
+ {
+ $this->app = $app;
+ $this->libClass = $app->getLibClass("Compute");
+ }
/**
* Execute an action
*
@@ -41,13 +41,26 @@ class compute
$servers[$server->id] = Array();
$server->flavor->retrieve();
$server->image->retrieve();
+ $server->retrieve();
$servers[$server->id]["id"] = $server->id;
$servers[$server->id]["name"] = $server->name;
- $servers[$server->id]["imageId"] = $server->image->id;
- $servers[$server->id]["flavorId"] = $server->flavor->id;
- $servers[$server->id]["status"] = $server->status;
+ $servers[$server->id]["image"] = $server->image;
$servers[$server->id]["ram"] = $server->flavor->ram;
$servers[$server->id]["disk"] = $server->flavor->disk;
+ $servers[$server->id]["flavor"] = $server->flavor;
+ $servers[$server->id]["status"] = $server->status;
+ $servers[$server->id]["created"] = $server->created;
+ $servers[$server->id]["updated"] = $server->updated;
+ $servers[$server->id]["ipv4"] = $server->ipv4;
+ $servers[$server->id]["ipv6"] = $server->ipv6;
+ $servers[$server->id]["progress"] = $server->progress;
+ $servers[$server->id]["hostId"] = $server->hostId;
+ $servers[$server->id]["tenantId"] = $server->tenantId;
+ $servers[$server->id]["userId"] = $server->userId;
+ $servers[$server->id]["taskState"] = $server->taskState;
+ $servers[$server->id]["addresses"] = $server->addresses;
+ $servers[$server->id]["links"] = $server->links;
+ $servers[$server->id]["metadata"] = $server->metadata;
}
$this->app->setOutput("Servers", $servers);
return;
@@ -62,8 +75,13 @@ class compute
$flavors = Array();
foreach($flavorList as $flavor){
$flavors[$flavor->id] = Array();
+ $flavor->retrieve();
$flavors[$flavor->id]["id"] = $flavor->id;
$flavors[$flavor->id]["name"] = $flavor->name;
+ $flavors[$flavor->id]["ram"] = $flavor->ram;
+ $flavors[$flavor->id]["disk"] = $flavor->disk;
+ $flavors[$flavor->id]["vcpus"] = $flavor->vcpus;
+ $flavors[$flavor->id]["links"] = $flavor->links;
}
$this->app->setOutput("Flavors", $flavors);
return;
@@ -78,29 +96,32 @@ class compute
$images = Array();
foreach($imageList as $image){
$images[$image->id] = Array();
+ $image->retrieve();
$images[$image->id]["id"] = $image->id;
$images[$image->id]["name"] = $image->name;
+ $images[$image->id]["status"] = $image->status;
+ $images[$image->id]["created"] = $image->created;
+ $images[$image->id]["updated"] = $image->updated;
+ $images[$image->id]["minDisk"] = $image->minDisk;
+ $images[$image->id]["minRam"] = $image->minRam;
+ $images[$image->id]["progress"] = $image->progress;
+ $images[$image->id]["links"] = $image->links;
+ $images[$image->id]["metadata"] = $image->metadata;
}
$this->app->setOutput("Images", $images);
return;
}
- /**
- * Create server.
- * @return array
- *
- public function createServer()
- {
-
- $server = $this->libClass->createServer();
- }
- */
- /**
+ /**
* Get server details.
* @return array
*/
public function getServer()
{
$serverId = $this->app->getPostParam("serverId");
+ if(!isset($serverId)){
+ $this->app->setOutput("Error", "Server ID is missing, son!");
+ return;
+ }
$opt = array('id' => $serverId);
$server = $this->libClass->getServer($opt);
$server->retrieve();
@@ -114,6 +135,10 @@ class compute
public function getFlavor()
{
$flavorId = $this->app->getPostParam("flavorId");
+ if(!isset($serverId)){
+ $this->app->setOutput("Error", "Flavor ID is missing, son!");
+ return;
+ }
$opt = array('id' => $flavorId);
$flavor = $this->libClass->getFlavor($opt);
$flavor->retrieve();
@@ -127,23 +152,80 @@ class compute
public function getImage()
{
$imageId = $this->app->getPostParam("imageId");
+ if(!isset($serverId)){
+ $this->app->setOutput("Error", "Image ID is missing, son!");
+ return;
+ }
$opt = array('id' => $imageId);
$image = $this->libClass->getImage($opt);
$image->retrieve();
$this->app->setOutput("MyImage", $image);
return;
}
- /* working on tests
-
- public function update()
+ /**
+ * Create server.
+ * @return array
+ */
+ public function createServer()
{
- $image = $this->app->getServer(array $options = []);
-
+ $name = $this->app->getPostParam("name");
+ $imageId = $this->app->getPostParam("imageId");
+ $flavorId = $this->app->getPostParam("flavorId");
+ if(!isset($name) || !isset($imageId) || !isset($flavorId)){
+ $this->app->setOutput("Error", "No, we don't let you create a server without a name OR image ID OR flavor ID.");
+ return;
+ }
+ $opt = array('name' => $name, 'imageId' => $imageId, 'flavorId' => $flavorId);
+ $server = $this->libClass->createServer($opt);
}
- public function delete()
+
+ /**
+ * update a server
+ * @return void
+ */
+ public function updateServer()
{
- //TODO
+ $serverId = $this->app->getPostParam("serverId");
+ $newName = $this->app->getPostParam("newName");
+ $newIpv4 = $this->app->getPostParam("newIpv4");
+ $newIpv6 = $this->app->getPostParam("newIpv6");
+ if(!isset($serverId)|| !(isset($newName) || isset($newIpv4) || isset($newIpv6)) ){
+ $this->app->setOutput("Error", "You'll have to provide server ID and the new attribute(IP(v4/v6)/Name) you desire to update!");
+ return;
+ }
+ $opt = array('id' => $serverId);
+ $server = $this->libClass->getServer($opt);
+ if (isset($newName)){
+ if(isset($newIpv4)){
+ if(isset($newIpv6)){
+ $attr = array('name' => $newName, 'accessIPv4' => $newIPv4, 'accessIPv6' => $newIpv6);
+ }
+ else $attr = array('name' => $newName, 'accessIPv4' => $newIPv4);
+ }
+ else $attr = array('name' => $newName);
+ }
+ $server->update($attr);
+ $this->app->setOutput("Success", $serverId." has been updated successfully.");
+ return;
}
+ /**
+ * Delete a server
+ * @return void
+ */
+ public function deleteServer()
+ {
+ $serverId = $this->app->getPostParam("serverId");
+ if(!isset($serverId)){
+ $this->app->setOutput("Error", "Server ID is missing, son!");
+ return;
+ }
+ $opt = array('id' => $serverId);
+ $server = $this->libClass->getServer($opt);
+ $server->delete();
+ $this->app->setOutput("Success", $serverId." has been deleted successfully.");
+ return;
+ }
+ /*
public function changePassword($newPassword)
{
//TODO
diff --git a/server/core/ErrorManagement.php b/server/core/ErrorManagement.php
index 6bff61f..4ba3493 100755
--- a/server/core/ErrorManagement.php
+++ b/server/core/ErrorManagement.php
@@ -1,9 +1,9 @@
\ No newline at end of file
+?>
diff --git a/server/core/Identity.php b/server/core/Identity.php
index 2638985..7100c9f 100755
--- a/server/core/Identity.php
+++ b/server/core/Identity.php
@@ -9,7 +9,7 @@
*
* @todo Complete the functions and finish the descriptions
*/
-use OpenStack\Common\Error;
+use OpenCloud\Common\Error;
/**
* Identity Class of the back-end application
diff --git a/server/core/Image.php b/server/core/Image.php
index b192716..f2af5cc 100644
--- a/server/core/Image.php
+++ b/server/core/Image.php
@@ -45,18 +45,8 @@ class image implements Core{
if(!isset($app)){
$this->app->setOutput("Error", "Incorrect parameter");
}
- try{
- $this->app = $app;
- $this->libClass = $app->getLibClass("Image");
- }catch(BadResponseError $e){
- $this->app->getErrorInstance()->BadResponseHandler($e);
- }catch(UserInputError $e){
- $this->app->getErrorInstance->UserInputHandler($e);
- }catch(BaseError $e){
- $this->app->getErrorInstance->BaseErrorHandler($e);
- }catch(NotImplementedError $e){
- $this->app->getErrorInstance->NotImplementedHandler($e);
- }
+ $this->app = $app;
+ $this->libClass = $app->getLibClass("Image");
}
@@ -80,8 +70,12 @@ class image implements Core{
* options for the image creation
*
**/
+<<<<<<< HEAD
private function createImage(array $opt){
+=======
+ private function createImage(){
+>>>>>>> develop
$opt = $this->app->getPostParam("opt");
@@ -377,7 +371,7 @@ class image implements Core{
* @param string $file_name
* path of the image
**/
- private function uploadImage($id, $file_name){
+ private function uploadImage(){
$id = $this->app->getPostParam("id");
$file_name = $this->app->getPostParam("file_name");
@@ -414,9 +408,13 @@ class image implements Core{
* @param string $id
* identifier of the image
**/
+<<<<<<< HEAD
private function downloadImage($id){
<<<<<<< HEAD
=======
+=======
+ private function downloadImage(){
+>>>>>>> develop
$id = $this->app->getPostParam("id");
>>>>>>> develop
if(!isset($id)){
@@ -620,7 +618,7 @@ class image implements Core{
* @param string $status
* new status for the member
**/
- private function updateMemberImage($image_id, $member_id, $status){
+ private function updateMemberImage(){
$image_id = $this->app->getPostParam("image_id");
$member_id = $this->app->getPostParam("member_id");
$status = $this->app->getPostParam("status");
diff --git a/server/core/LibOverride/genTokenOptions.php b/server/core/LibOverride/genTokenOptions.php
index 47f026a..bdae8a6 100755
--- a/server/core/LibOverride/genTokenOptions.php
+++ b/server/core/LibOverride/genTokenOptions.php
@@ -1,12 +1,12 @@
array($vendorDir . '/psr/http-message/src'),
- 'OpenStack\\Test\\' => array($vendorDir . '/php-opencloud/openstack/tests/unit'),
- 'OpenStack\\Integration\\' => array($vendorDir . '/php-opencloud/openstack/tests/integration'),
'OpenStack\\' => array($vendorDir . '/php-opencloud/openstack/src'),
+ 'OpenCloud\\Test\\' => array($vendorDir . '/php-opencloud/common/tests/unit'),
+ 'OpenCloud\\Integration\\' => array($vendorDir . '/php-opencloud/common/tests/integration'),
+ 'OpenCloud\\' => array($vendorDir . '/php-opencloud/common/src'),
'JsonSchema\\' => array($vendorDir . '/justinrainbow/json-schema/src/JsonSchema'),
'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'),
'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'),
diff --git a/server/vendor/composer/installed.json b/server/vendor/composer/installed.json
index 1dfb8d2..8f06408 100644
--- a/server/vendor/composer/installed.json
+++ b/server/vendor/composer/installed.json
@@ -1,57 +1,4 @@
[
- {
- "name": "guzzlehttp/promises",
- "version": "1.0.3",
- "version_normalized": "1.0.3.0",
- "source": {
- "type": "git",
- "url": "https://github.com/guzzle/promises.git",
- "reference": "b1e1c0d55f8083c71eda2c28c12a228d708294ea"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/guzzle/promises/zipball/b1e1c0d55f8083c71eda2c28c12a228d708294ea",
- "reference": "b1e1c0d55f8083c71eda2c28c12a228d708294ea",
- "shasum": ""
- },
- "require": {
- "php": ">=5.5.0"
- },
- "require-dev": {
- "phpunit/phpunit": "~4.0"
- },
- "time": "2015-10-15 22:28:00",
- "type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "1.0-dev"
- }
- },
- "installation-source": "dist",
- "autoload": {
- "psr-4": {
- "GuzzleHttp\\Promise\\": "src/"
- },
- "files": [
- "src/functions_include.php"
- ]
- },
- "notification-url": "https://packagist.org/downloads/",
- "license": [
- "MIT"
- ],
- "authors": [
- {
- "name": "Michael Dowling",
- "email": "mtdowling@gmail.com",
- "homepage": "https://github.com/mtdowling"
- }
- ],
- "description": "Guzzle promises library",
- "keywords": [
- "promise"
- ]
- },
{
"name": "psr/http-message",
"version": "1.0",
@@ -167,54 +114,6 @@
"web service"
]
},
- {
- "name": "php-opencloud/openstack",
- "version": "dev-master",
- "version_normalized": "9999999-dev",
- "source": {
- "type": "git",
- "url": "https://github.com/php-opencloud/openstack.git",
- "reference": "15aca73f423166c7ef8337ba08615c103c66e931"
- },
- "dist": {
- "type": "zip",
- "url": "https://api.github.com/repos/php-opencloud/openstack/zipball/15aca73f423166c7ef8337ba08615c103c66e931",
- "reference": "15aca73f423166c7ef8337ba08615c103c66e931",
- "shasum": ""
- },
- "require": {
- "guzzlehttp/guzzle": "~6.1",
- "justinrainbow/json-schema": "~1.3",
- "php": ">=5.6"
- },
- "require-dev": {
- "fabpot/php-cs-fixer": "~1.0",
- "jakub-onderka/php-parallel-lint": "0.*",
- "phpspec/prophecy-phpunit": "~1.0",
- "phpunit/phpunit": "~4.0",
- "psr/log": "~1.0",
- "sami/sami": "dev-master",
- "satooshi/php-coveralls": "~1.0"
- },
- "time": "2016-01-25 10:35:10",
- "type": "library",
- "installation-source": "source",
- "autoload": {
- "psr-4": {
- "OpenStack\\": "src/",
- "OpenStack\\Test\\": "tests/unit/",
- "OpenStack\\Integration\\": "tests/integration/"
- }
- },
- "notification-url": "https://packagist.org/downloads/",
- "authors": [
- {
- "name": "Jamie Hannaford",
- "email": "jamie.hannaford@rackspace.com",
- "homepage": "https://github.com/jamiehannaford"
- }
- ]
- },
{
"name": "justinrainbow/json-schema",
"version": "1.6.1",
@@ -284,18 +183,118 @@
]
},
{
- "name": "guzzlehttp/psr7",
- "version": "1.2.2",
- "version_normalized": "1.2.2.0",
+ "name": "php-opencloud/common",
+ "version": "dev-master",
+ "version_normalized": "9999999-dev",
"source": {
"type": "git",
- "url": "https://github.com/guzzle/psr7.git",
- "reference": "f5d04bdd2881ac89abde1fb78cc234bce24327bb"
+ "url": "https://github.com/php-opencloud/common.git",
+ "reference": "fd027b817c3dd8f83b0c8d9fb1df34ebf25f8c62"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5d04bdd2881ac89abde1fb78cc234bce24327bb",
- "reference": "f5d04bdd2881ac89abde1fb78cc234bce24327bb",
+ "url": "https://api.github.com/repos/php-opencloud/common/zipball/fd027b817c3dd8f83b0c8d9fb1df34ebf25f8c62",
+ "reference": "fd027b817c3dd8f83b0c8d9fb1df34ebf25f8c62",
+ "shasum": ""
+ },
+ "require": {
+ "guzzlehttp/guzzle": "~6.1",
+ "justinrainbow/json-schema": "~1.3",
+ "php": "~7.0"
+ },
+ "require-dev": {
+ "fabpot/php-cs-fixer": "~1.0",
+ "jakub-onderka/php-parallel-lint": "0.*",
+ "phpunit/phpunit": "~4.0",
+ "psr/log": "~1.0",
+ "sami/sami": "dev-master",
+ "satooshi/php-coveralls": "~1.0"
+ },
+ "time": "2016-03-08 12:56:34",
+ "type": "library",
+ "installation-source": "source",
+ "autoload": {
+ "psr-4": {
+ "OpenCloud\\": "src/",
+ "OpenCloud\\Test\\": "tests/unit/",
+ "OpenCloud\\Integration\\": "tests/integration/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "authors": [
+ {
+ "name": "Jamie Hannaford",
+ "email": "jamie.hannaford@rackspace.com",
+ "homepage": "https://github.com/jamiehannaford"
+ }
+ ]
+ },
+ {
+ "name": "guzzlehttp/promises",
+ "version": "1.1.0",
+ "version_normalized": "1.1.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/promises.git",
+ "reference": "bb9024c526b22f3fe6ae55a561fd70653d470aa8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/promises/zipball/bb9024c526b22f3fe6ae55a561fd70653d470aa8",
+ "reference": "bb9024c526b22f3fe6ae55a561fd70653d470aa8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.5.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "time": "2016-03-08 01:15:46",
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.0-dev"
+ }
+ },
+ "installation-source": "dist",
+ "autoload": {
+ "psr-4": {
+ "GuzzleHttp\\Promise\\": "src/"
+ },
+ "files": [
+ "src/functions_include.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ }
+ ],
+ "description": "Guzzle promises library",
+ "keywords": [
+ "promise"
+ ]
+ },
+ {
+ "name": "guzzlehttp/psr7",
+ "version": "1.2.3",
+ "version_normalized": "1.2.3.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/psr7.git",
+ "reference": "2e89629ff057ebb49492ba08e6995d3a6a80021b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/psr7/zipball/2e89629ff057ebb49492ba08e6995d3a6a80021b",
+ "reference": "2e89629ff057ebb49492ba08e6995d3a6a80021b",
"shasum": ""
},
"require": {
@@ -308,7 +307,7 @@
"require-dev": {
"phpunit/phpunit": "~4.0"
},
- "time": "2016-01-23 01:23:02",
+ "time": "2016-02-18 21:54:00",
"type": "library",
"extra": {
"branch-alias": {
@@ -342,5 +341,48 @@
"stream",
"uri"
]
+ },
+ {
+ "name": "php-opencloud/openstack",
+ "version": "dev-master",
+ "version_normalized": "9999999-dev",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-opencloud/openstack.git",
+ "reference": "f2ee77024843659d970817a9e7055bb40a3724f9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-opencloud/openstack/zipball/f2ee77024843659d970817a9e7055bb40a3724f9",
+ "reference": "f2ee77024843659d970817a9e7055bb40a3724f9",
+ "shasum": ""
+ },
+ "require": {
+ "php-opencloud/common": "dev-master"
+ },
+ "require-dev": {
+ "fabpot/php-cs-fixer": "~1.0",
+ "jakub-onderka/php-parallel-lint": "0.*",
+ "phpunit/phpunit": "~4.0",
+ "psr/log": "~1.0",
+ "sami/sami": "dev-master",
+ "satooshi/php-coveralls": "~1.0"
+ },
+ "time": "2016-03-08 14:37:14",
+ "type": "library",
+ "installation-source": "source",
+ "autoload": {
+ "psr-4": {
+ "OpenStack\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "authors": [
+ {
+ "name": "Jamie Hannaford",
+ "email": "jamie.hannaford@rackspace.com",
+ "homepage": "https://github.com/jamiehannaford"
+ }
+ ]
}
]
diff --git a/server/vendor/guzzlehttp/promises/CHANGELOG.md b/server/vendor/guzzlehttp/promises/CHANGELOG.md
index 4031cb8..3871ac7 100644
--- a/server/vendor/guzzlehttp/promises/CHANGELOG.md
+++ b/server/vendor/guzzlehttp/promises/CHANGELOG.md
@@ -1,5 +1,11 @@
# CHANGELOG
+## 1.1.0 - 2016-03-07
+
+* Update EachPromise to prevent recurring on a iterator when advancing, as this
+ could trigger fatal generator errors.
+* Update Promise to allow recursive waiting without unwrapping exceptions.
+
## 1.0.3 - 2015-10-15
* Update EachPromise to immediately resolve when the underlying promise iterator
diff --git a/server/vendor/guzzlehttp/promises/src/EachPromise.php b/server/vendor/guzzlehttp/promises/src/EachPromise.php
index 5918429..0344686 100644
--- a/server/vendor/guzzlehttp/promises/src/EachPromise.php
+++ b/server/vendor/guzzlehttp/promises/src/EachPromise.php
@@ -24,6 +24,9 @@ class EachPromise implements PromisorInterface
/** @var Promise */
private $aggregate;
+ /** @var bool */
+ private $mutex;
+
/**
* Configuration hash can include the following key value pairs:
*
@@ -81,6 +84,7 @@ class EachPromise implements PromisorInterface
private function createPromise()
{
+ $this->mutex = false;
$this->aggregate = new Promise(function () {
reset($this->pending);
if (empty($this->pending) && !$this->iterable->valid()) {
@@ -169,11 +173,21 @@ class EachPromise implements PromisorInterface
private function advanceIterator()
{
+ // Place a lock on the iterator so that we ensure to not recurse,
+ // preventing fatal generator errors.
+ if ($this->mutex) {
+ return false;
+ }
+
+ $this->mutex = true;
+
try {
$this->iterable->next();
+ $this->mutex = false;
return true;
} catch (\Exception $e) {
$this->aggregate->reject($e);
+ $this->mutex = false;
return false;
}
}
@@ -186,9 +200,11 @@ class EachPromise implements PromisorInterface
}
unset($this->pending[$idx]);
- $this->advanceIterator();
- if (!$this->checkIfFinished()) {
+ // Only refill pending promises if we are not locked, preventing the
+ // EachPromise to recursively invoke the provided iterator, which
+ // cause a fatal error: "Cannot resume an already running generator"
+ if ($this->advanceIterator() && !$this->checkIfFinished()) {
// Add more pending promises if possible.
$this->refillPending();
}
diff --git a/server/vendor/guzzlehttp/promises/src/Promise.php b/server/vendor/guzzlehttp/promises/src/Promise.php
index c2cf969..86820b2 100644
--- a/server/vendor/guzzlehttp/promises/src/Promise.php
+++ b/server/vendor/guzzlehttp/promises/src/Promise.php
@@ -61,17 +61,19 @@ class Promise implements PromiseInterface
{
$this->waitIfPending();
- if (!$unwrap) {
- return null;
- }
+ $inner = $this->result instanceof PromiseInterface
+ ? $this->result->wait($unwrap)
+ : $this->result;
- if ($this->result instanceof PromiseInterface) {
- return $this->result->wait($unwrap);
- } elseif ($this->state === self::FULFILLED) {
- return $this->result;
- } else {
- // It's rejected so "unwrap" and throw an exception.
- throw exception_for($this->result);
+ if ($unwrap) {
+ if ($this->result instanceof PromiseInterface
+ || $this->state === self::FULFILLED
+ ) {
+ return $inner;
+ } else {
+ // It's rejected so "unwrap" and throw an exception.
+ throw exception_for($inner);
+ }
}
}
@@ -257,11 +259,10 @@ class Promise implements PromiseInterface
$this->waitList = null;
foreach ($waitList as $result) {
- descend:
$result->waitIfPending();
- if ($result->result instanceof Promise) {
+ while ($result->result instanceof Promise) {
$result = $result->result;
- goto descend;
+ $result->waitIfPending();
}
}
}
diff --git a/server/vendor/guzzlehttp/promises/src/TaskQueue.php b/server/vendor/guzzlehttp/promises/src/TaskQueue.php
index 5026363..39fe5bb 100644
--- a/server/vendor/guzzlehttp/promises/src/TaskQueue.php
+++ b/server/vendor/guzzlehttp/promises/src/TaskQueue.php
@@ -56,6 +56,7 @@ class TaskQueue
*/
public function run()
{
+ /** @var callable $task */
while ($task = array_shift($this->queue)) {
$task();
}
diff --git a/server/vendor/guzzlehttp/promises/src/functions.php b/server/vendor/guzzlehttp/promises/src/functions.php
index 89c6569..2fe61b7 100644
--- a/server/vendor/guzzlehttp/promises/src/functions.php
+++ b/server/vendor/guzzlehttp/promises/src/functions.php
@@ -146,9 +146,9 @@ function inspect(PromiseInterface $promise)
'value' => $promise->wait()
];
} catch (RejectionException $e) {
- return ['state' => 'rejected', 'reason' => $e->getReason()];
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e->getReason()];
} catch (\Exception $e) {
- return ['state' => 'rejected', 'reason' => $e];
+ return ['state' => PromiseInterface::REJECTED, 'reason' => $e];
}
}
@@ -304,10 +304,10 @@ function settle($promises)
return each(
$promises,
function ($value, $idx) use (&$results) {
- $results[$idx] = ['state' => 'fulfilled', 'value' => $value];
+ $results[$idx] = ['state' => PromiseInterface::FULFILLED, 'value' => $value];
},
function ($reason, $idx) use (&$results) {
- $results[$idx] = ['state' => 'rejected', 'reason' => $reason];
+ $results[$idx] = ['state' => PromiseInterface::REJECTED, 'reason' => $reason];
}
)->then(function () use (&$results) {
ksort($results);
diff --git a/server/vendor/guzzlehttp/promises/tests/EachPromiseTest.php b/server/vendor/guzzlehttp/promises/tests/EachPromiseTest.php
index 0a0a851..08af2a0 100644
--- a/server/vendor/guzzlehttp/promises/tests/EachPromiseTest.php
+++ b/server/vendor/guzzlehttp/promises/tests/EachPromiseTest.php
@@ -39,8 +39,8 @@ class EachPromiseTest extends \PHPUnit_Framework_TestCase
public function testIsWaitable()
{
- $a = new Promise(function () use (&$a) { $a->resolve('a'); });
- $b = new Promise(function () use (&$b) { $b->resolve('b'); });
+ $a = $this->createSelfResolvingPromise('a');
+ $b = $this->createSelfResolvingPromise('b');
$called = [];
$each = new EachPromise([$a, $b], [
'fulfilled' => function ($value) use (&$called) { $called[] = $value; }
@@ -54,7 +54,7 @@ class EachPromiseTest extends \PHPUnit_Framework_TestCase
public function testCanResolveBeforeConsumingAll()
{
$called = 0;
- $a = new Promise(function () use (&$a) { $a->resolve('a'); });
+ $a = $this->createSelfResolvingPromise('a');
$b = new Promise(function () { $this->fail(); });
$each = new EachPromise([$a, $b], [
'fulfilled' => function ($value, $idx, Promise $aggregate) use (&$called) {
@@ -291,4 +291,46 @@ class EachPromiseTest extends \PHPUnit_Framework_TestCase
}
$this->assertEquals(range(0, 9), $results);
}
+
+ private function createSelfResolvingPromise($value)
+ {
+ $p = new Promise(function () use (&$p, $value) {
+ $p->resolve($value);
+ });
+
+ return $p;
+ }
+
+ public function testMutexPreventsGeneratorRecursion()
+ {
+ $results = $promises = [];
+ for ($i = 0; $i < 20; $i++) {
+ $p = $this->createSelfResolvingPromise($i);
+ $pending[] = $p;
+ $promises[] = $p;
+ }
+
+ $iter = function () use (&$promises, &$pending) {
+ foreach ($promises as $promise) {
+ // Resolve a promises, which will trigger the then() function,
+ // which would cause the EachPromise to try to add more
+ // promises to the queue. Without a lock, this would trigger
+ // a "Cannot resume an already running generator" fatal error.
+ if ($p = array_pop($pending)) {
+ $p->wait();
+ }
+ yield $promise;
+ }
+ };
+
+ $each = new EachPromise($iter(), [
+ 'concurrency' => 5,
+ 'fulfilled' => function ($r) use (&$results, &$pending) {
+ $results[] = $r;
+ }
+ ]);
+
+ $each->promise()->wait();
+ $this->assertCount(20, $results);
+ }
}
diff --git a/server/vendor/guzzlehttp/promises/tests/PromiseTest.php b/server/vendor/guzzlehttp/promises/tests/PromiseTest.php
index 946c627..599d8ae 100644
--- a/server/vendor/guzzlehttp/promises/tests/PromiseTest.php
+++ b/server/vendor/guzzlehttp/promises/tests/PromiseTest.php
@@ -172,6 +172,18 @@ class PromiseTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('Whoop', $p->wait());
}
+ public function testWaitsOnAPromiseChainEvenWhenNotUnwrapped()
+ {
+ $p2 = new Promise(function () use (&$p2) {
+ $p2->reject('Fail');
+ });
+ $p = new Promise(function () use ($p2, &$p) {
+ $p->resolve($p2);
+ });
+ $p->wait(false);
+ $this->assertSame(Promise::REJECTED, $p2->getState());
+ }
+
public function testCannotCancelNonPending()
{
$p = new Promise();
diff --git a/server/vendor/guzzlehttp/psr7/CHANGELOG.md b/server/vendor/guzzlehttp/psr7/CHANGELOG.md
index d875aa3..0e278bd 100644
--- a/server/vendor/guzzlehttp/psr7/CHANGELOG.md
+++ b/server/vendor/guzzlehttp/psr7/CHANGELOG.md
@@ -1,5 +1,11 @@
# CHANGELOG
+## 1.2.3 - 2016-02-18
+
+* Fixed support in `GuzzleHttp\Psr7\CachingStream` for seeking forward on remote
+ streams, which can sometimes return fewer bytes than requested with `fread`.
+* Fixed handling of gzipped responses with FNAME headers.
+
## 1.2.2 - 2016-01-22
* Added support for URIs without any authority.
diff --git a/server/vendor/guzzlehttp/psr7/README.md b/server/vendor/guzzlehttp/psr7/README.md
index 0a4c341..c15ddd3 100644
--- a/server/vendor/guzzlehttp/psr7/README.md
+++ b/server/vendor/guzzlehttp/psr7/README.md
@@ -38,7 +38,7 @@ echo $composed(); // abc, 123. Above all listen to me.
`GuzzleHttp\Psr7\BufferStream`
-Provides a buffer stream that can be written to to fill a buffer, and read
+Provides a buffer stream that can be written to fill a buffer, and read
from to remove bytes from the buffer.
This stream returns a "hwm" metadata value that tells upstream consumers
@@ -106,7 +106,7 @@ echo $stream; // 0123456789
Compose stream implementations based on a hash of functions.
-Allows for easy testing and extension of a provided stream without needing to
+Allows for easy testing and extension of a provided stream without needing
to create a concrete class for a simple extension point.
```php
diff --git a/server/vendor/guzzlehttp/psr7/src/CachingStream.php b/server/vendor/guzzlehttp/psr7/src/CachingStream.php
index 420d5b0..ed68f08 100644
--- a/server/vendor/guzzlehttp/psr7/src/CachingStream.php
+++ b/server/vendor/guzzlehttp/psr7/src/CachingStream.php
@@ -60,9 +60,12 @@ class CachingStream implements StreamInterface
$diff = $byte - $this->stream->getSize();
if ($diff > 0) {
- // If the seek byte is greater the number of read bytes, then read
- // the difference of bytes to cache the bytes and inherently seek.
- $this->read($diff);
+ // Read the remoteStream until we have read in at least the amount
+ // of bytes requested, or we reach the end of the file.
+ while ($diff > 0 && !$this->remoteStream->eof()) {
+ $this->read($diff);
+ $diff = $byte - $this->stream->getSize();
+ }
} else {
// We can just do a normal seek since we've already seen this byte.
$this->stream->seek($byte);
diff --git a/server/vendor/guzzlehttp/psr7/src/InflateStream.php b/server/vendor/guzzlehttp/psr7/src/InflateStream.php
index 2c8628b..0051d3f 100644
--- a/server/vendor/guzzlehttp/psr7/src/InflateStream.php
+++ b/server/vendor/guzzlehttp/psr7/src/InflateStream.php
@@ -20,10 +20,33 @@ class InflateStream implements StreamInterface
public function __construct(StreamInterface $stream)
{
- // Skip the first 10 bytes
- $stream = new LimitStream($stream, -1, 10);
+ // read the first 10 bytes, ie. gzip header
+ $header = $stream->read(10);
+ $filenameHeaderLength = $this->getLengthOfPossibleFilenameHeader($stream, $header);
+ // Skip the header, that is 10 + length of filename + 1 (nil) bytes
+ $stream = new LimitStream($stream, -1, 10 + $filenameHeaderLength);
$resource = StreamWrapper::getResource($stream);
stream_filter_append($resource, 'zlib.inflate', STREAM_FILTER_READ);
$this->stream = new Stream($resource);
}
+
+ /**
+ * @param StreamInterface $stream
+ * @param $header
+ * @return int
+ */
+ private function getLengthOfPossibleFilenameHeader(StreamInterface $stream, $header)
+ {
+ $filename_header_length = 0;
+
+ if (substr(bin2hex($header), 6, 2) === '08') {
+ // we have a filename, read until nil
+ $filename_header_length = 1;
+ while ($stream->read(1) !== chr(0)) {
+ $filename_header_length++;
+ }
+ }
+
+ return $filename_header_length;
+ }
}
diff --git a/server/vendor/guzzlehttp/psr7/tests/CachingStreamTest.php b/server/vendor/guzzlehttp/psr7/tests/CachingStreamTest.php
index 326f754..60a2636 100644
--- a/server/vendor/guzzlehttp/psr7/tests/CachingStreamTest.php
+++ b/server/vendor/guzzlehttp/psr7/tests/CachingStreamTest.php
@@ -98,6 +98,33 @@ class CachingStreamTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('ing', $this->body->read(3));
}
+ public function testCanSeekToReadBytesWithPartialBodyReturned()
+ {
+ $stream = fopen('php://temp', 'r+');
+ fwrite($stream, 'testing');
+ fseek($stream, 0);
+
+ $this->decorated = $this->getMockBuilder('\GuzzleHttp\Psr7\Stream')
+ ->setConstructorArgs([$stream])
+ ->setMethods(['read'])
+ ->getMock();
+
+ $this->decorated->expects($this->exactly(2))
+ ->method('read')
+ ->willReturnCallback(function($length) use ($stream){
+ return fread($stream, 2);
+ });
+
+ $this->body = new CachingStream($this->decorated);
+
+ $this->assertEquals(0, $this->body->tell());
+ $this->body->seek(4, SEEK_SET);
+ $this->assertEquals(4, $this->body->tell());
+
+ $this->body->seek(0);
+ $this->assertEquals('test', $this->body->read(4));
+ }
+
public function testWritesToBufferStream()
{
$this->body->read(2);
diff --git a/server/vendor/guzzlehttp/psr7/tests/InflateStreamTest.php b/server/vendor/guzzlehttp/psr7/tests/InflateStreamTest.php
index 927fc0b..0e4b586 100644
--- a/server/vendor/guzzlehttp/psr7/tests/InflateStreamTest.php
+++ b/server/vendor/guzzlehttp/psr7/tests/InflateStreamTest.php
@@ -13,4 +13,27 @@ class InflateStreamtest extends \PHPUnit_Framework_TestCase
$b = new InflateStream($a);
$this->assertEquals('test', (string) $b);
}
+
+ public function testInflatesStreamsWithFilename()
+ {
+ $content = $this->getGzipStringWithFilename('test');
+ $a = Psr7\stream_for($content);
+ $b = new InflateStream($a);
+ $this->assertEquals('test', (string) $b);
+ }
+
+ private function getGzipStringWithFilename($original_string)
+ {
+ $gzipped = bin2hex(gzencode($original_string));
+
+ $header = substr($gzipped, 0, 20);
+ // set FNAME flag
+ $header[6]=0;
+ $header[7]=8;
+ // make a dummy filename
+ $filename = "64756d6d7900";
+ $rest = substr($gzipped, 20);
+
+ return hex2bin($header . $filename . $rest);
+ }
}
diff --git a/server/vendor/guzzlehttp/psr7/tests/UriTest.php b/server/vendor/guzzlehttp/psr7/tests/UriTest.php
index a63293c..357ee25 100644
--- a/server/vendor/guzzlehttp/psr7/tests/UriTest.php
+++ b/server/vendor/guzzlehttp/psr7/tests/UriTest.php
@@ -154,7 +154,7 @@ class UriTest extends \PHPUnit_Framework_TestCase
[self::RFC3986_BASE, 'g;x=1/../y', 'http://a/b/c/y'],
['http://u@a/b/c/d;p?q', '.', 'http://u@a/b/c/'],
['http://u:p@a/b/c/d;p?q', '.', 'http://u:p@a/b/c/'],
- //[self::RFC3986_BASE, 'http:g', 'http:g'],
+ ['http://a/b/c/d/', 'e', 'http://a/b/c/d/e'],
];
}
diff --git a/server/vendor/php-opencloud/common/.gitignore b/server/vendor/php-opencloud/common/.gitignore
new file mode 100644
index 0000000..7ad09a8
--- /dev/null
+++ b/server/vendor/php-opencloud/common/.gitignore
@@ -0,0 +1,10 @@
+.idea/
+.test/
+coverage/
+vendor/
+
+*.pyc
+
+phpunit.xml
+coverage.xml
+composer.lock
diff --git a/server/vendor/php-opencloud/common/composer.json b/server/vendor/php-opencloud/common/composer.json
new file mode 100644
index 0000000..f2520ad
--- /dev/null
+++ b/server/vendor/php-opencloud/common/composer.json
@@ -0,0 +1,36 @@
+{
+ "name": "php-opencloud/common",
+ "authors": [
+ {
+ "name": "Jamie Hannaford",
+ "email": "jamie.hannaford@rackspace.com",
+ "homepage" : "https://github.com/jamiehannaford"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "OpenCloud\\": "src/",
+ "OpenCloud\\Test\\": "tests/unit/",
+ "OpenCloud\\Integration\\": "tests/integration/"
+ }
+ },
+ "repositories": [
+ {
+ "type": "vcs",
+ "url": "https://github.com/php-opencloud/Sami"
+ }
+ ],
+ "require": {
+ "php": "~7.0",
+ "guzzlehttp/guzzle": "~6.1",
+ "justinrainbow/json-schema": "~1.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0",
+ "sami/sami": "dev-master",
+ "psr/log": "~1.0",
+ "satooshi/php-coveralls": "~1.0",
+ "jakub-onderka/php-parallel-lint": "0.*",
+ "fabpot/php-cs-fixer": "~1.0"
+ }
+}
diff --git a/server/vendor/php-opencloud/common/phpunit.xml.dist b/server/vendor/php-opencloud/common/phpunit.xml.dist
new file mode 100644
index 0000000..cf698d5
--- /dev/null
+++ b/server/vendor/php-opencloud/common/phpunit.xml.dist
@@ -0,0 +1,21 @@
+
+
+
+
+
+ tests/unit
+
+
+
+
+
+ ./src
+
+ ./src
+ ./src
+ ./src
+
+
+
+
+
\ No newline at end of file
diff --git a/server/vendor/php-opencloud/common/src/Common/Api/AbstractApi.php b/server/vendor/php-opencloud/common/src/Common/Api/AbstractApi.php
new file mode 100644
index 0000000..beee5e8
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Api/AbstractApi.php
@@ -0,0 +1,33 @@
+ true]);
+ }
+
+ protected function notRequired(array $param)
+ {
+ return array_merge($param, ['required' => false]);
+ }
+
+ protected function query(array $param)
+ {
+ return array_merge($param, ['location' => AbstractParams::QUERY]);
+ }
+
+ protected function url(array $param)
+ {
+ return array_merge($param, ['location' => AbstractParams::URL]);
+ }
+
+ public function documented(array $param)
+ {
+ return array_merge($param, ['required' => true]);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Api/AbstractParams.php b/server/vendor/php-opencloud/common/src/Common/Api/AbstractParams.php
new file mode 100644
index 0000000..225e025
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Api/AbstractParams.php
@@ -0,0 +1,100 @@
+ self::INT_TYPE,
+ 'location' => 'query',
+ 'description' => << 'string',
+ 'location' => 'query',
+ 'description' => << sprintf("The unique ID, or identifier, for the %s", $type),
+ 'type' => self::STRING_TYPE,
+ 'location' => self::JSON,
+ ];
+ }
+
+ public function idPath()
+ {
+ return [
+ 'type' => self::STRING_TYPE,
+ 'location' => self::URL,
+ 'description' => 'The unique ID of the resource',
+ ];
+ }
+
+ public function name($resource)
+ {
+ return [
+ 'description' => sprintf("The name of the %s", $resource),
+ 'type' => self::STRING_TYPE,
+ 'location' => self::JSON,
+ ];
+ }
+
+
+ public function sortDir()
+ {
+ return [
+ 'type' => self::STRING_TYPE,
+ 'location' => self::QUERY,
+ 'description' => "Sorts by one or more sets of attribute and sort direction combinations.",
+ 'enum' => ['asc', 'desc']
+ ];
+ }
+
+ public function sortKey()
+ {
+ return [
+ 'type' => self::STRING_TYPE,
+ 'location' => self::QUERY,
+ 'description' => "Sorts by one or more sets of attribute and sort direction combinations.",
+ ];
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Api/ApiInterface.php b/server/vendor/php-opencloud/common/src/Common/Api/ApiInterface.php
new file mode 100644
index 0000000..d5d26a0
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Api/ApiInterface.php
@@ -0,0 +1,20 @@
+method = $definition['method'];
+ $this->path = $definition['path'];
+
+ if (isset($definition['jsonKey'])) {
+ $this->jsonKey = $definition['jsonKey'];
+ }
+
+ $this->params = self::toParamArray($definition['params']);
+ }
+
+ /**
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * @return string
+ */
+ public function getMethod()
+ {
+ return $this->method;
+ }
+
+ /**
+ * Indicates whether this operation supports a parameter.
+ *
+ * @param $key The name of a parameter
+ *
+ * @return bool
+ */
+ public function hasParam($key)
+ {
+ return isset($this->params[$key]);
+ }
+
+ /**
+ * @param $name
+ *
+ * @return Parameter
+ */
+ public function getParam($name)
+ {
+ return isset($this->params[$name]) ? $this->params[$name] : null;
+ }
+
+ /**
+ * @return string
+ */
+ public function getJsonKey()
+ {
+ return $this->jsonKey;
+ }
+
+ /**
+ * A convenience method that will take a generic array of data and convert it into an array of
+ * {@see Parameter} objects.
+ *
+ * @param array $data A generic data array
+ *
+ * @return array
+ */
+ public static function toParamArray(array $data)
+ {
+ $params = [];
+
+ foreach ($data as $name => $param) {
+ $params[$name] = new Parameter($param + ['name' => $name]);
+ }
+
+ return $params;
+ }
+
+ /**
+ * This method will validate all of the user-provided values and throw an exception if any
+ * failures are detected. This is useful for basic sanity-checking before a request is
+ * serialized and sent to the API.
+ *
+ * @param array $userValues The user-defined values
+ *
+ * @return bool TRUE if validation passes
+ * @throws \Exception If validate fails
+ */
+ public function validate(array $userValues)
+ {
+ foreach ($this->params as $paramName => $param) {
+ if (array_key_exists($paramName, $userValues)) {
+ $param->validate($userValues[$paramName]);
+ } elseif ($param->isRequired()) {
+ throw new \Exception(sprintf('"%s" is a required option, but it was not provided', $paramName));
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Api/Operator.php b/server/vendor/php-opencloud/common/src/Common/Api/Operator.php
new file mode 100644
index 0000000..4325b69
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Api/Operator.php
@@ -0,0 +1,173 @@
+client = $client;
+ $this->api = $api;
+ }
+
+ /**
+ * Magic method for dictating how objects are rendered when var_dump is called.
+ * For the benefit of users, extremely verbose and heavy properties (such as HTTP clients) are
+ * removed to provide easier access to normal state, such as resource attributes.
+ *
+ * @codeCoverageIgnore
+ * @return array
+ */
+ public function __debugInfo()
+ {
+ $excludedVars = ['client', 'errorBuilder', 'api'];
+
+ $output = [];
+
+ foreach (get_object_vars($this) as $key => $val) {
+ if (!in_array($key, $excludedVars)) {
+ $output[$key] = $val;
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * Retrieves a populated Operation according to the definition and values provided. A
+ * HTTP client is also injected into the object to allow it to communicate with the remote API.
+ *
+ * @param array $definition The data that dictates how the operation works
+ *
+ * @return Operation
+ */
+ public function getOperation(array $definition)
+ {
+ return new Operation($definition);
+ }
+
+ protected function sendRequest(Operation $operation, array $userValues = [], $async = false)
+ {
+ $operation->validate($userValues);
+
+ $options = (new RequestSerializer)->serializeOptions($operation, $userValues);
+ $method = $async ? 'requestAsync' : 'request';
+ $uri = uri_template($operation->getPath(), $userValues);
+
+ return $this->client->$method($operation->getMethod(), $uri, $options);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function execute(array $definition, array $userValues = [])
+ {
+ return $this->sendRequest($this->getOperation($definition), $userValues);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function executeAsync(array $definition, array $userValues = [])
+ {
+ return $this->sendRequest($this->getOperation($definition), $userValues, true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function model($class, $data = null)
+ {
+ $model = new $class($this->client, $this->api);
+
+ // @codeCoverageIgnoreStart
+ if (!$model instanceof ResourceInterface) {
+ throw new \RuntimeException(sprintf('%s does not implement %s', $class, ResourceInterface::class));
+ }
+ // @codeCoverageIgnoreEnd
+
+ if ($data instanceof ResponseInterface) {
+ $model->populateFromResponse($data);
+ } elseif (is_array($data)) {
+ $model->populateFromArray($data);
+ }
+
+ return $model;
+ }
+
+ /**
+ * Will create a new instance of this class with the current HTTP client and API injected in. This
+ * is useful when enumerating over a collection since multiple copies of the same resource class
+ * are needed.
+ *
+ * @return static
+ */
+ public function newInstance()
+ {
+ return new static($this->client, $this->api);
+ }
+
+ /**
+ * @return \GuzzleHttp\Psr7\Uri
+ */
+ protected function getHttpBaseUrl()
+ {
+ return $this->client->getConfig('base_uri');
+ }
+
+ /**
+ * Magic method which intercepts async calls, finds the sequential version, and wraps it in a
+ * {@see Promise} object. In order for this to happen, the called methods need to be in the
+ * following format: `createAsync`, where `create` is the sequential method being wrapped.
+ *
+ * @param $methodName The name of the method being invoked.
+ * @param $args The arguments to be passed to the sequential method.
+ *
+ * @throws \RuntimeException If method does not exist
+ *
+ * @return Promise
+ */
+ public function __call($methodName, $args)
+ {
+ $e = function ($name) {
+ return new \RuntimeException(sprintf('%s::%s is not defined', get_class($this), $name));
+ };
+
+ if (substr($methodName, -5) === 'Async') {
+ $realMethod = substr($methodName, 0, -5);
+ if (!method_exists($this, $realMethod)) {
+ throw $e($realMethod);
+ }
+
+ $promise = new Promise(
+ function () use (&$promise, $realMethod, $args) {
+ $value = call_user_func_array([$this, $realMethod], $args);
+ $promise->resolve($value);
+ }
+ );
+
+ return $promise;
+ }
+
+ throw $e($methodName);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Api/OperatorInterface.php b/server/vendor/php-opencloud/common/src/Common/Api/OperatorInterface.php
new file mode 100644
index 0000000..43c6ce2
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Api/OperatorInterface.php
@@ -0,0 +1,51 @@
+hydrate($data);
+
+ $this->required = (bool)$this->required;
+
+ $this->stockLocation($data);
+ $this->stockItemSchema($data);
+ $this->stockProperties($data);
+ }
+
+ private function stockLocation(array $data)
+ {
+ $this->location = isset($data['location']) ? $data['location'] : self::DEFAULT_LOCATION;
+
+ if (!AbstractParams::isSupportedLocation($this->location)) {
+ throw new \RuntimeException(sprintf("%s is not a permitted location", $this->location));
+ }
+ }
+
+ private function stockItemSchema(array $data)
+ {
+ if (isset($data['items'])) {
+ $this->itemSchema = new Parameter($data['items']);
+ }
+ }
+
+ private function stockProperties(array $data)
+ {
+ if (isset($data['properties'])) {
+ if (stripos($this->name, 'metadata') !== false) {
+ $this->properties = new Parameter($data['properties']);
+ } else {
+ foreach ($data['properties'] as $name => $property) {
+ $this->properties[$name] = new Parameter($property + ['name' => $name]);
+ }
+ }
+ }
+ }
+
+ /**
+ * Retrieve the name that will be used over the wire.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return $this->sentAs ?: $this->name;
+ }
+
+ /**
+ * Indicates whether the user must provide a value for this parameter.
+ *
+ * @return bool
+ */
+ public function isRequired()
+ {
+ return $this->required === true;
+ }
+
+ /**
+ * Validates a given user value and checks whether it passes basic sanity checking, such as types.
+ *
+ * @param $userValues The value provided by the user
+ *
+ * @return bool TRUE if the validation passes
+ * @throws \Exception If validation fails
+ */
+ public function validate($userValues)
+ {
+ $this->validateEnums($userValues);
+ $this->validateType($userValues);
+
+ if ($this->isArray()) {
+ $this->validateArray($userValues);
+ } elseif ($this->isObject()) {
+ $this->validateObject($userValues);
+ }
+
+ return true;
+ }
+
+ private function validateEnums($userValues)
+ {
+ if (!empty($this->enum) && $this->type == 'string' && !in_array($userValues, $this->enum)) {
+ throw new \Exception(sprintf(
+ 'The only permitted values are %s. You provided %s', implode(', ', $this->enum), print_r($userValues, true)
+ ));
+ }
+ }
+
+ private function validateType($userValues)
+ {
+ if (!$this->hasCorrectType($userValues)) {
+ throw new \Exception(sprintf(
+ 'The key provided "%s" has the wrong value type. You provided %s (%s) but was expecting %s',
+ $this->name, print_r($userValues, true), gettype($userValues), $this->type
+ ));
+ }
+ }
+
+ private function validateArray($userValues)
+ {
+ foreach ($userValues as $userValue) {
+ $this->itemSchema->validate($userValue);
+ }
+ }
+
+ private function validateObject($userValues)
+ {
+ foreach ($userValues as $key => $userValue) {
+ $property = $this->getNestedProperty($key);
+ $property->validate($userValue);
+ }
+ }
+
+ /**
+ * Internal method which retrieves a nested property for object parameters.
+ *
+ * @param $key The name of the child parameter
+ *
+ * @returns Parameter
+ * @throws \Exception
+ */
+ private function getNestedProperty($key)
+ {
+ if (stripos($this->name, 'metadata') !== false && $this->properties instanceof Parameter) {
+ return $this->properties;
+ } elseif (isset($this->properties[$key])) {
+ return $this->properties[$key];
+ } else {
+ throw new \Exception(sprintf('The key provided "%s" is not defined', $key));
+ }
+ }
+
+ /**
+ * Internal method which indicates whether the user value is of the same type as the one expected
+ * by this parameter.
+ *
+ * @param $userValue The value being checked
+ *
+ * @return bool
+ */
+ private function hasCorrectType($userValue)
+ {
+ // Helper fn to see whether an array is associative (i.e. a JSON object)
+ $isAssociative = function ($value) {
+ return is_array($value) && array_keys($value) !== range(0, count($value) - 1);
+ };
+
+ // For params defined as objects, we'll let the user get away with
+ // passing in an associative array - since it's effectively a hash
+ if ($this->type == 'object' && $isAssociative($userValue)) {
+ return true;
+ }
+
+ if (class_exists($this->type) || interface_exists($this->type)) {
+ return is_a($userValue, $this->type);
+ }
+
+ if (!$this->type) {
+ return true;
+ }
+
+ return gettype($userValue) == $this->type;
+ }
+
+ /**
+ * Indicates whether this parameter represents an array type
+ *
+ * @return bool
+ */
+ public function isArray()
+ {
+ return $this->type == 'array' && $this->itemSchema instanceof Parameter;
+ }
+
+ /**
+ * Indicates whether this parameter represents an object type
+ *
+ * @return bool
+ */
+ public function isObject()
+ {
+ return $this->type == 'object' && !empty($this->properties);
+ }
+
+ public function getLocation()
+ {
+ return $this->location;
+ }
+
+ /**
+ * Verifies whether the given location matches the parameter's location.
+ *
+ * @param $value
+ *
+ * @return bool
+ */
+ public function hasLocation($value)
+ {
+ return $this->location == $value;
+ }
+
+ /**
+ * Retrieves the parameter's path.
+ *
+ * @return string|null
+ */
+ public function getPath()
+ {
+ return $this->path;
+ }
+
+ /**
+ * Retrieves the common schema that an array parameter applies to all its child elements.
+ *
+ * @return Parameter
+ */
+ public function getItemSchema()
+ {
+ return $this->itemSchema;
+ }
+
+ /**
+ * Sets the name of the parameter to a new value
+ *
+ * @param string $name
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * Retrieves the child parameter for an object parameter.
+ *
+ * @param string $name The name of the child property
+ *
+ * @return null|Parameter
+ */
+ public function getProperty($name)
+ {
+ if ($this->properties instanceof Parameter) {
+ $this->properties->setName($name);
+ return $this->properties;
+ }
+
+ return isset($this->properties[$name]) ? $this->properties[$name] : null;
+ }
+
+ /**
+ * Retrieves the prefix for a parameter, if any.
+ *
+ * @return string|null
+ */
+ public function getPrefix()
+ {
+ return $this->prefix;
+ }
+
+ public function getPrefixedName()
+ {
+ return $this->prefix . $this->getName();
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/ArrayAccessTrait.php b/server/vendor/php-opencloud/common/src/Common/ArrayAccessTrait.php
new file mode 100644
index 0000000..72bcfce
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/ArrayAccessTrait.php
@@ -0,0 +1,67 @@
+internalState[] = $value;
+ } else {
+ $this->internalState[$offset] = $value;
+ }
+ }
+
+ /**
+ * Checks whether an internal key exists.
+ *
+ * @param string $offset
+ *
+ * @return bool
+ */
+ public function offsetExists($offset)
+ {
+ return isset($this->internalState[$offset]);
+ }
+
+ /**
+ * Unsets an internal key.
+ *
+ * @param string $offset
+ */
+ public function offsetUnset($offset)
+ {
+ unset($this->internalState[$offset]);
+ }
+
+ /**
+ * Retrieves an internal key.
+ *
+ * @param string $offset
+ *
+ * @return mixed|null
+ */
+ public function offsetGet($offset)
+ {
+ return $this->offsetExists($offset) ? $this->internalState[$offset] : null;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Auth/AuthHandler.php b/server/vendor/php-opencloud/common/src/Common/Auth/AuthHandler.php
new file mode 100644
index 0000000..1a36cc0
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Auth/AuthHandler.php
@@ -0,0 +1,73 @@
+nextHandler = $nextHandler;
+ $this->tokenGenerator = $tokenGenerator;
+ $this->token = $token;
+ }
+
+ /**
+ * This method is invoked before every HTTP request is sent to the API. When this happens, it
+ * checks to see whether a token is set and valid, and then sets the ``X-Auth-Token`` header
+ * for the HTTP request before letting it continue on its merry way.
+ *
+ * @param RequestInterface $request
+ * @param array $options
+ *
+ * @return mixed|void
+ */
+ public function __invoke(RequestInterface $request, array $options)
+ {
+ $fn = $this->nextHandler;
+
+ if ($this->shouldIgnore($request)) {
+ return $fn($request, $options);
+ }
+
+ if (!$this->token || $this->token->hasExpired()) {
+ $this->token = call_user_func($this->tokenGenerator);
+ }
+
+ $modify = ['set_headers' => ['X-Auth-Token' => $this->token->getId()]];
+
+ return $fn(modify_request($request, $modify), $options);
+ }
+
+ /**
+ * Internal method which prevents infinite recursion. For certain requests, like the initial
+ * auth call itself, we do NOT want to send a token.
+ *
+ * @param RequestInterface $request
+ *
+ * @return bool
+ */
+ private function shouldIgnore(RequestInterface $request)
+ {
+ return strpos((string) $request->getUri(), 'tokens') !== false && $request->getMethod() == 'POST';
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Auth/Catalog.php b/server/vendor/php-opencloud/common/src/Common/Auth/Catalog.php
new file mode 100644
index 0000000..b4ab381
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Auth/Catalog.php
@@ -0,0 +1,20 @@
+request = $request;
+ }
+
+ public function setResponse(ResponseInterface $response)
+ {
+ $this->response = $response;
+ }
+
+ public function getRequest()
+ {
+ return $this->request;
+ }
+
+ public function getResponse()
+ {
+ return $this->response;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Error/BaseError.php b/server/vendor/php-opencloud/common/src/Common/Error/BaseError.php
new file mode 100644
index 0000000..a7cb26e
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Error/BaseError.php
@@ -0,0 +1,12 @@
+client = $client ?: new Client();
+ }
+
+ /**
+ * Internal method used when outputting headers in the error description.
+ *
+ * @param $name
+ *
+ * @return string
+ */
+ private function header($name)
+ {
+ return sprintf("%s\n%s\n", $name, str_repeat('~', strlen($name)));
+ }
+
+ /**
+ * Before outputting custom links, it is validated to ensure that the user is not
+ * directed off to a broken link. If a 404 is detected, it is hidden.
+ *
+ * @param $link The proposed link
+ *
+ * @return bool
+ */
+ private function linkIsValid($link)
+ {
+ $link = $this->docDomain . $link;
+
+ try {
+ return $this->client->request('HEAD', $link)->getStatusCode() < 400;
+ } catch (ClientException $e) {
+ return false;
+ }
+ }
+
+ /**
+ * @param MessageInterface $message
+ *
+ * @codeCoverageIgnore
+ * @return string
+ */
+ public function str(MessageInterface $message)
+ {
+ if ($message instanceof RequestInterface) {
+ $msg = trim($message->getMethod() . ' '
+ . $message->getRequestTarget())
+ . ' HTTP/' . $message->getProtocolVersion();
+ if (!$message->hasHeader('host')) {
+ $msg .= "\r\nHost: " . $message->getUri()->getHost();
+ }
+ } elseif ($message instanceof ResponseInterface) {
+ $msg = 'HTTP/' . $message->getProtocolVersion() . ' '
+ . $message->getStatusCode() . ' '
+ . $message->getReasonPhrase();
+ }
+
+ foreach ($message->getHeaders() as $name => $values) {
+ $msg .= "\r\n{$name}: " . implode(', ', $values);
+ }
+
+ if ($message->getBody()->getSize() < ini_get('memory_limit')) {
+ $msg .= "\r\n\r\n" . $message->getBody();
+ }
+
+ return $msg;
+ }
+
+ /**
+ * Helper method responsible for constructing and returning {@see BadResponseError} exceptions.
+ *
+ * @param RequestInterface $request The faulty request
+ * @param ResponseInterface $response The error-filled response
+ *
+ * @return BadResponseError
+ */
+ public function httpError(RequestInterface $request, ResponseInterface $response)
+ {
+ $message = $this->header('HTTP Error');
+
+ $message .= sprintf("The remote server returned a \"%d %s\" error for the following transaction:\n\n",
+ $response->getStatusCode(), $response->getReasonPhrase());
+
+ $message .= $this->header('Request');
+ $message .= trim($this->str($request)) . PHP_EOL . PHP_EOL;
+
+ $message .= $this->header('Response');
+ $message .= trim($this->str($response)) . PHP_EOL . PHP_EOL;
+
+ $message .= $this->header('Further information');
+ $message .= $this->getStatusCodeMessage($response->getStatusCode());
+
+ $message .= "Visit http://docs.php-opencloud.com/en/latest/http-codes for more information about debugging "
+ . "HTTP status codes, or file a support issue on https://github.com/php-opencloud/openstack/issues.";
+
+ $e = new BadResponseError($message);
+ $e->setRequest($request);
+ $e->setResponse($response);
+
+ return $e;
+ }
+
+ private function getStatusCodeMessage($statusCode)
+ {
+ $errors = [
+ 400 => 'Please ensure that your input values are valid and well-formed. ',
+ 401 => 'Please ensure that your authentication credentials are valid. ',
+ 404 => "Please ensure that the resource you're trying to access actually exists. ",
+ 500 => 'Please try this operation again once you know the remote server is operational. ',
+ ];
+
+ return isset($errors[$statusCode]) ? $errors[$statusCode] : '';
+ }
+
+ /**
+ * Helper method responsible for constructing and returning {@see UserInputError} exceptions.
+ *
+ * @param string $expectedType The type that was expected from the user
+ * @param mixed $userValue The incorrect value the user actually provided
+ * @param string|null $furtherLink A link to further information if necessary (optional).
+ *
+ * @return UserInputError
+ */
+ public function userInputError($expectedType, $userValue, $furtherLink = null)
+ {
+ $message = $this->header('User Input Error');
+
+ $message .= sprintf("%s was expected, but the following value was passed in:\n\n%s\n",
+ $expectedType, print_r($userValue, true));
+
+ $message .= "Please ensure that the value adheres to the expectation above. ";
+
+ if ($furtherLink && $this->linkIsValid($furtherLink)) {
+ $message .= sprintf("Visit %s for more information about input arguments. ", $this->docDomain . $furtherLink);
+ }
+
+ $message .= 'If you run into trouble, please open a support issue on https://github.com/php-opencloud/openstack/issues.';
+
+ return new UserInputError($message);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Error/NotImplementedError.php b/server/vendor/php-opencloud/common/src/Common/Error/NotImplementedError.php
new file mode 100644
index 0000000..3e01d74
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Error/NotImplementedError.php
@@ -0,0 +1,12 @@
+ $val) {
+ $key = isset($aliases[$key]) ? $aliases[$key] : $key;
+ if (property_exists($this, $key)) {
+ $this->$key = $val;
+ }
+ }
+ }
+
+ private function set($key, $property, array $data, callable $fn = null)
+ {
+ if (isset($data[$key]) && property_exists($this, $property)) {
+ $value = $fn ? call_user_func($fn, $data[$key]) : $data[$key];
+ $this->$property = $value;
+ }
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/JsonPath.php b/server/vendor/php-opencloud/common/src/Common/JsonPath.php
new file mode 100644
index 0000000..0a6372e
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/JsonPath.php
@@ -0,0 +1,119 @@
+['foo' => ['bar' => ['baz' => 'some_value']]]
+ *
+ * and you wanted to insert or extract an element. Usually, you would use:
+ *
+ * $array['foo']['bar']['baz'] = 'new_value';
+ *
+ * but sometimes you do not have access to the variable - so a string representation is needed. Using
+ * XPath-like syntax, this class allows you to do this:
+ *
+ * $jsonPath = new JsonPath($array);
+ * $jsonPath->set('foo.bar.baz', 'new_value');
+ * $val = $jsonPath->get('foo.bar.baz');
+ *
+ *
+ * @package OpenCloud\Common
+ */
+class JsonPath
+{
+ /** @var array */
+ private $jsonStructure;
+
+ /**
+ * @param $structure The initial data structure to extract from and insert into. Typically this will be a
+ * multidimensional associative array; but well-formed JSON strings are also acceptable.
+ */
+ public function __construct($structure)
+ {
+ $this->jsonStructure = is_string($structure) ? json_decode($structure, true) : $structure;
+ }
+
+ /**
+ * Set a node in the structure
+ *
+ * @param $path The XPath to use
+ * @param $value The new value of the node
+ */
+ public function set($path, $value)
+ {
+ $this->jsonStructure = $this->setPath($path, $value, $this->jsonStructure);
+ }
+
+ /**
+ * Internal method for recursive calls.
+ *
+ * @param $path
+ * @param $value
+ * @param $json
+ * @return mixed
+ */
+ private function setPath($path, $value, $json)
+ {
+ $nodes = explode('.', $path);
+ $point = array_shift($nodes);
+
+ if (!isset($json[$point])) {
+ $json[$point] = [];
+ }
+
+ if (!empty($nodes)) {
+ $json[$point] = $this->setPath(implode('.', $nodes), $value, $json[$point]);
+ } else {
+ $json[$point] = $value;
+ }
+
+ return $json;
+ }
+
+ /**
+ * Return the updated structure.
+ *
+ * @return mixed
+ */
+ public function getStructure()
+ {
+ return $this->jsonStructure;
+ }
+
+ /**
+ * Get a path's value. If no path can be matched, NULL is returned.
+ *
+ * @param $path
+ * @return mixed|null
+ */
+ public function get($path)
+ {
+ return $this->getPath($path, $this->jsonStructure);
+ }
+
+ /**
+ * Internal method for recursion.
+ *
+ * @param $path
+ * @param $json
+ * @return null
+ */
+ private function getPath($path, $json)
+ {
+ $nodes = explode('.', $path);
+ $point = array_shift($nodes);
+
+ if (!isset($json[$point])) {
+ return null;
+ }
+
+ if (empty($nodes)) {
+ return $json[$point];
+ } else {
+ return $this->getPath(implode('.', $nodes), $json[$point]);
+ }
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/JsonSchema/JsonPatch.php b/server/vendor/php-opencloud/common/src/Common/JsonSchema/JsonPatch.php
new file mode 100644
index 0000000..2c12ec0
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/JsonSchema/JsonPatch.php
@@ -0,0 +1,115 @@
+makeDiff($src, $dest);
+ }
+
+ public function makeDiff($srcStruct, $desStruct, $path = '')
+ {
+ $changes = [];
+
+ if (is_object($srcStruct)) {
+ $changes = $this->handleObject($srcStruct, $desStruct, $path);
+ } elseif (is_array($srcStruct)) {
+ $changes = $this->handleArray($srcStruct, $desStruct, $path);
+ } elseif ($srcStruct != $desStruct) {
+ $changes[] = $this->makePatch(self::OP_REPLACE, $path, $desStruct);
+ }
+
+ return $changes;
+ }
+
+ protected function handleArray($srcStruct, $desStruct, $path)
+ {
+ $changes = [];
+
+ if ($diff = $this->arrayDiff($desStruct, $srcStruct)) {
+ foreach ($diff as $key => $val) {
+ if (is_object($val)) {
+ $changes = array_merge($changes, $this->makeDiff($srcStruct[$key], $val, $this->path($path, $key)));
+ } else {
+ $op = array_key_exists($key, $srcStruct) && !in_array($srcStruct[$key], $desStruct, true)
+ ? self::OP_REPLACE : self::OP_ADD;
+ $changes[] = $this->makePatch($op, $this->path($path, $key), $val);
+ }
+ }
+ } elseif ($srcStruct != $desStruct) {
+ foreach ($srcStruct as $key => $val) {
+ if (!in_array($val, $desStruct, true)) {
+ $changes[] = $this->makePatch(self::OP_REMOVE, $this->path($path, $key));
+ }
+ }
+ }
+
+ return $changes;
+ }
+
+ protected function handleObject($srcStruct, $desStruct, $path)
+ {
+ $changes = [];
+
+ if ($this->shouldPartiallyReplace($srcStruct, $desStruct)) {
+ foreach ($desStruct as $key => $val) {
+ if (!property_exists($srcStruct, $key)) {
+ $changes[] = $this->makePatch(self::OP_ADD, $this->path($path, $key), $val);
+ } elseif ($srcStruct->$key != $val) {
+ $changes = array_merge($changes, $this->makeDiff($srcStruct->$key, $val, $this->path($path, $key)));
+ }
+ }
+ } elseif ($this->shouldPartiallyReplace($desStruct, $srcStruct)) {
+ foreach ($srcStruct as $key => $val) {
+ if (!property_exists($desStruct, $key)) {
+ $changes[] = $this->makePatch(self::OP_REMOVE, $this->path($path, $key));
+ }
+ }
+ }
+
+ return $changes;
+ }
+
+ protected function shouldPartiallyReplace($o1, $o2)
+ {
+ return count(array_diff_key((array) $o1, (array) $o2)) < count($o1);
+ }
+
+ protected function arrayDiff(array $a1, array $a2)
+ {
+ $result = [];
+
+ foreach ($a1 as $key => $val) {
+ if (!in_array($val, $a2, true)) {
+ $result[$key] = $val;
+ }
+ }
+
+ return $result;
+ }
+
+ protected function path($root, $path)
+ {
+ if ($path === '_empty_') {
+ $path = '';
+ }
+
+ return rtrim($root, '/') . '/' . ltrim($path, '/');
+ }
+
+ protected function makePatch($op, $path, $val = null)
+ {
+ switch ($op) {
+ default:
+ return ['op' => $op, 'path' => $path, 'value' => $val];
+ case self::OP_REMOVE:
+ return ['op' => $op, 'path' => $path];
+ }
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/JsonSchema/Schema.php b/server/vendor/php-opencloud/common/src/Common/JsonSchema/Schema.php
new file mode 100644
index 0000000..a1cd380
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/JsonSchema/Schema.php
@@ -0,0 +1,72 @@
+body = (object) $body;
+ $this->validator = $validator ?: new Validator();
+ }
+
+ public function getPropertyPaths()
+ {
+ $paths = [];
+
+ foreach ($this->body->properties as $propertyName => $property) {
+ $paths[] = sprintf("/%s", $propertyName);
+ }
+
+ return $paths;
+ }
+
+ public function normalizeObject($subject, array $aliases)
+ {
+ $out = new \stdClass;
+
+ foreach ($this->body->properties as $propertyName => $property) {
+ $name = isset($aliases[$propertyName]) ? $aliases[$propertyName] : $propertyName;
+ if (isset($property->readOnly) && $property->readOnly === true) {
+ continue;
+ } elseif (property_exists($subject, $name)) {
+ $out->$propertyName = $subject->$name;
+ } elseif (property_exists($subject, $propertyName)) {
+ $out->$propertyName = $subject->$propertyName;
+ }
+ }
+
+ return $out;
+ }
+
+ public function validate($data)
+ {
+ $this->validator->check($data, $this->body);
+ }
+
+ public function isValid()
+ {
+ return $this->validator->isValid();
+ }
+
+ public function getErrors()
+ {
+ return $this->validator->getErrors();
+ }
+
+ public function getErrorString()
+ {
+ $msg = "Provided values do not validate. Errors:\n";
+
+ foreach ($this->getErrors() as $error) {
+ $msg .= sprintf("[%s] %s\n", $error['property'], $error['message']);
+ }
+
+ return $msg;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Resource/AbstractResource.php b/server/vendor/php-opencloud/common/src/Common/Resource/AbstractResource.php
new file mode 100644
index 0000000..9f79b07
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Resource/AbstractResource.php
@@ -0,0 +1,243 @@
+ 'fooBar'
+ *
+ * will extract FOO_BAR from the response, and save it as 'fooBar' in the resource.
+ *
+ * @var array
+ */
+ protected $aliases = [];
+
+ /**
+ * Populates the current resource from a response object.
+ *
+ * @param ResponseInterface $response
+ *
+ * @return $this|ResourceInterface
+ */
+ public function populateFromResponse(ResponseInterface $response)
+ {
+ if (strpos($response->getHeaderLine('Content-Type'), 'application/json') === 0) {
+ $json = Utils::jsonDecode($response);
+ if (!empty($json)) {
+ $this->populateFromArray(Utils::flattenJson($json, $this->resourceKey));
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Populates the current resource from a data array.
+ *
+ * @param array $array
+ *
+ * @return mixed|void
+ */
+ public function populateFromArray(array $array)
+ {
+ $reflClass = new \ReflectionClass($this);
+
+ foreach ($array as $key => $val) {
+ $propertyName = isset($this->aliases[$key]) ? $this->aliases[$key] : $key;
+
+ if (property_exists($this, $propertyName)) {
+ if ($type = $this->extractTypeFromDocBlock($reflClass, $propertyName)) {
+ $val = $this->parseDocBlockValue($type, $val);
+ }
+
+ $this->$propertyName = $val;
+ }
+ }
+ }
+
+ private function parseDocBlockValue($type, $val)
+ {
+ if (strpos($type, '[]') === 0 && is_array($val)) {
+ $array = [];
+ foreach ($val as $subVal) {
+ $array[] = $this->model($this->normalizeModelClass(substr($type, 2)), $subVal);
+ }
+ $val = $array;
+ } elseif (strcasecmp($type, '\datetimeimmutable') === 0) {
+ $val = new \DateTimeImmutable($val);
+ } elseif ($this->isNotNativeType($type)) {
+ $val = $this->model($this->normalizeModelClass($type), $val);
+ }
+
+ return $val;
+ }
+
+ private function isNotNativeType($type)
+ {
+ return !in_array($type, [
+ 'string', 'bool', 'boolean', 'double', 'null', 'array', 'object', 'int', 'integer', 'float', 'numeric',
+ 'mixed'
+ ]);
+ }
+
+ private function normalizeModelClass($class)
+ {
+ if (strpos($class, '\\') === false) {
+ $currentNamespace = (new \ReflectionClass($this))->getNamespaceName();
+ $class = sprintf("%s\\%s", $currentNamespace, $class);
+ }
+
+ return $class;
+ }
+
+ private function extractTypeFromDocBlock(\ReflectionClass $reflClass, $propertyName)
+ {
+ $docComment = $reflClass->getProperty($propertyName)->getDocComment();
+
+ if (!$docComment) {
+ return false;
+ }
+
+ $matches = [];
+ preg_match('#@var ((\[\])?[\w|\\\]+)#', $docComment, $matches);
+ return isset($matches[1]) ? $matches[1] : null;
+ }
+
+ /**
+ * Internal method which retrieves the values of provided keys.
+ *
+ * @param array $keys
+ *
+ * @return array
+ */
+ protected function getAttrs(array $keys)
+ {
+ $output = [];
+
+ foreach ($keys as $key) {
+ if (property_exists($this, $key) && $this->$key !== null) {
+ $output[$key] = $this->$key;
+ }
+ }
+
+ return $output;
+ }
+
+ /**
+ * @param array $definition
+ *
+ * @return mixed
+ */
+ public function executeWithState(array $definition)
+ {
+ return $this->execute($definition, $this->getAttrs(array_keys($definition['params'])));
+ }
+
+ private function getResourcesKey()
+ {
+ $resourcesKey = $this->resourcesKey;
+
+ if (!$resourcesKey) {
+ $class = substr(static::class, strrpos(static::class, '\\') + 1);
+ $resourcesKey = strtolower(preg_replace('/([a-z])([A-Z])/', '$1_$2', $class)) . 's';
+ }
+
+ return $resourcesKey;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function enumerate(array $def, array $userVals = [], callable $mapFn = null)
+ {
+ $operation = $this->getOperation($def);
+
+ $requestFn = function ($marker) use ($operation, $userVals) {
+ if ($marker) {
+ $userVals['marker'] = $marker;
+ }
+ return $this->sendRequest($operation, $userVals);
+ };
+
+ $resourceFn = function (array $data) {
+ $resource = $this->newInstance();
+ $resource->populateFromArray($data);
+ return $resource;
+ };
+
+ $opts = [
+ 'limit' => isset($userVals['limit']) ? $userVals['limit'] : null,
+ 'resourcesKey' => $this->getResourcesKey(),
+ 'markerKey' => $this->markerKey,
+ 'mapFn' => $mapFn,
+ ];
+
+ $iterator = new Iterator($opts, $requestFn, $resourceFn);
+ return $iterator();
+ }
+
+ public function extractMultipleInstances(ResponseInterface $response, $key = null)
+ {
+ $key = $key ?: $this->getResourcesKey();
+ $resourcesData = Utils::jsonDecode($response)[$key];
+
+ $resources = [];
+
+ foreach ($resourcesData as $resourceData) {
+ $resource = $this->newInstance();
+ $resource->populateFromArray($resourceData);
+ $resources[] = $resource;
+ }
+
+ return $resources;
+ }
+
+ protected function getService()
+ {
+ $class = static::class;
+ $service = substr($class, 0, strpos($class, 'Models') - 1) . '\\Service';
+
+ return new $service($this->client, $this->api);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Resource/Creatable.php b/server/vendor/php-opencloud/common/src/Common/Resource/Creatable.php
new file mode 100644
index 0000000..19579c1
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Resource/Creatable.php
@@ -0,0 +1,19 @@
+ 'val3', 'Baz' => 'val4']); is called, then the resource will have the following
+ * metadata:
+ *
+ * Foo: val3
+ * Bar: val2
+ * Baz: val4
+ *
+ * You will notice that any metadata items which are not specified in the call are preserved.
+ *
+ * @param array $metadata The new metadata items
+ *
+ * @return mixed
+ */
+ public function mergeMetadata(array $metadata);
+
+ /**
+ * Replaces all of the existing metadata items for a resource with a new set of values. Any metadata items which
+ * are not provided in the call are removed from the resource. For example, if the resource has this metadata
+ * already set:
+ *
+ * Foo: val1
+ * Bar: val2
+ *
+ * and resetMetadata(['Foo' => 'val3', 'Baz' => 'val4']); is called, then the resource will have the following
+ * metadata:
+ *
+ * Foo: val3
+ * Baz: val4
+ *
+ * @param array $metadata The new metadata items
+ *
+ * @return mixed
+ */
+ public function resetMetadata(array $metadata);
+
+ /**
+ * Extracts metadata from a response object and returns it in the form of an associative array.
+ *
+ * @param ResponseInterface $response
+ *
+ * @return array
+ */
+ public function parseMetadata(ResponseInterface $response);
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Resource/HasWaiterTrait.php b/server/vendor/php-opencloud/common/src/Common/Resource/HasWaiterTrait.php
new file mode 100644
index 0000000..519be7e
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Resource/HasWaiterTrait.php
@@ -0,0 +1,124 @@
+retrieve();
+
+ if ($this->status == $status || $this->shouldHalt($timeout, $startTime)) {
+ break;
+ }
+
+ sleep($sleepPeriod);
+ }
+ }
+
+ /**
+ * Provides a blocking operation until the resource has reached a particular state. The method
+ * will enter a loop, executing the callback until TRUE is returned. This provides great
+ * flexibility.
+ *
+ * @param callable $fn An anonymous function that will be executed on every iteration. You can
+ * encapsulate your own logic to determine whether the resource has
+ * successfully transitioned. When TRUE is returned by the callback,
+ * the loop will end.
+ * @param int|bool $timeout The maximum timeout in seconds. If the total time taken by the waiter has reached
+ * or exceed this timeout, the blocking operation will immediately cease. If FALSE
+ * is provided, the timeout will never be considered.
+ * @param int $sleepPeriod The amount of time to pause between each HTTP request.
+ */
+ public function waitWithCallback(callable $fn, $timeout = 60, $sleepPeriod = 1)
+ {
+ $startTime = time();
+
+ while (true) {
+ $this->retrieve();
+
+ $response = call_user_func_array($fn, [$this]);
+
+ if ($response === true || $this->shouldHalt($timeout, $startTime)) {
+ break;
+ }
+
+ sleep($sleepPeriod);
+ }
+ }
+
+ /**
+ * Internal method used to identify whether a timeout has been exceeded.
+ *
+ * @param bool|int $timeout
+ * @param int $startTime
+ *
+ * @return bool
+ */
+ private function shouldHalt($timeout, $startTime)
+ {
+ if ($timeout === false) {
+ return false;
+ }
+
+ return time() - $startTime >= $timeout;
+ }
+
+ /**
+ * Convenience method providing a blocking operation until the resource transitions to an
+ * ``ACTIVE`` status.
+ *
+ * @param int|bool $timeout The maximum timeout in seconds. If the total time taken by the waiter has reached
+ * or exceed this timeout, the blocking operation will immediately cease. If FALSE
+ * is provided, the timeout will never be considered.
+ */
+ public function waitUntilActive($timeout = false)
+ {
+ $this->waitUntil('ACTIVE', $timeout);
+ }
+
+ public function waitUntilDeleted($timeout = 60, $sleepPeriod = 1)
+ {
+ $startTime = time();
+
+ while (true) {
+ try {
+ $this->retrieve();
+ } catch (BadResponseError $e) {
+ if ($e->getResponse()->getStatusCode() === 404) {
+ break;
+ }
+ throw $e;
+ }
+
+ if ($this->shouldHalt($timeout, $startTime)) {
+ break;
+ }
+
+ sleep($sleepPeriod);
+ }
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Resource/Iterator.php b/server/vendor/php-opencloud/common/src/Common/Resource/Iterator.php
new file mode 100644
index 0000000..63d4455
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Resource/Iterator.php
@@ -0,0 +1,97 @@
+limit = isset($options['limit']) ? $options['limit'] : false;
+ $this->count = 0;
+
+ if (isset($options['resourcesKey'])) {
+ $this->resourcesKey = $options['resourcesKey'];
+ }
+
+ if (isset($options['markerKey'])) {
+ $this->markerKey = $options['markerKey'];
+ }
+
+ if (isset($options['mapFn']) && is_callable($options['mapFn'])) {
+ $this->mapFn = $options['mapFn'];
+ }
+
+ $this->requestFn = $requestFn;
+ $this->resourceFn = $resourceFn;
+ }
+
+ private function fetchResources()
+ {
+ if ($this->shouldNotSendAnotherRequest()) {
+ return false;
+ }
+
+ $response = call_user_func($this->requestFn, $this->currentMarker);
+
+ $json = Utils::flattenJson(Utils::jsonDecode($response), $this->resourcesKey);
+
+ if ($response->getStatusCode() === 204 || empty($json)) {
+ return false;
+ }
+
+ return $json;
+ }
+
+ private function assembleResource(array $data)
+ {
+ $resource = call_user_func($this->resourceFn, $data);
+
+ // Invoke user-provided fn if provided
+ if ($this->mapFn) {
+ call_user_func_array($this->mapFn, [&$resource]);
+ }
+
+ // Update marker if operation supports it
+ if ($this->markerKey) {
+ $this->currentMarker = $resource->{$this->markerKey};
+ }
+
+ return $resource;
+ }
+
+ private function totalReached()
+ {
+ return $this->limit && $this->count >= $this->limit;
+ }
+
+ private function shouldNotSendAnotherRequest()
+ {
+ return $this->totalReached() || ($this->count > 0 && !$this->markerKey);
+ }
+
+ public function __invoke()
+ {
+ while ($resources = $this->fetchResources()) {
+ foreach ($resources as $resourceData) {
+ if ($this->totalReached()) {
+ break;
+ }
+
+ $this->count++;
+
+ yield $this->assembleResource($resourceData);
+ }
+ }
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Resource/Listable.php b/server/vendor/php-opencloud/common/src/Common/Resource/Listable.php
new file mode 100644
index 0000000..8e255c0
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Resource/Listable.php
@@ -0,0 +1,28 @@
+ 'publicURL'];
+
+ /**
+ * @param array $globalOptions Options that will be applied to every service created by this builder.
+ * Eventually they will be merged (and if necessary overridden) by the
+ * service-specific options passed in.
+ */
+ public function __construct(array $globalOptions = [], $rootNamespace = 'OpenCloud')
+ {
+ $this->globalOptions = $globalOptions;
+ $this->rootNamespace = $rootNamespace;
+ }
+
+ /**
+ * Internal method which resolves the API and Service classes for a service.
+ *
+ * @param string $serviceName The name of the service, e.g. Compute
+ * @param int $serviceVersion The major version of the service, e.g. 2
+ *
+ * @return array
+ */
+ private function getClasses($serviceName, $serviceVersion)
+ {
+ $rootNamespace = sprintf("%s\\%s\\v%d", $this->rootNamespace, $serviceName, $serviceVersion);
+
+ return [
+ sprintf("%s\\Api", $rootNamespace),
+ sprintf("%s\\Service", $rootNamespace),
+ ];
+ }
+
+ /**
+ * This method will return an OpenCloud service ready fully built and ready for use. There is
+ * some initial setup that may prohibit users from directly instantiating the service class
+ * directly - this setup includes the configuration of the HTTP client's base URL, and the
+ * attachment of an authentication handler.
+ *
+ * @param $serviceName The name of the service as it appears in the OpenCloud\* namespace
+ * @param $serviceVersion The major version of the service
+ * @param array $serviceOptions The service-specific options to use
+ *
+ * @return \OpenCloud\Common\Service\ServiceInterface
+ *
+ * @throws \Exception
+ */
+ public function createService($serviceName, $serviceVersion, array $serviceOptions = [])
+ {
+ $options = $this->mergeOptions($serviceOptions);
+
+ $this->stockIdentityService($options);
+ $this->stockAuthHandler($options);
+ $this->stockHttpClient($options, $serviceName);
+
+ list($apiClass, $serviceClass) = $this->getClasses($serviceName, $serviceVersion);
+
+ return new $serviceClass($options['httpClient'], new $apiClass());
+ }
+
+ private function stockHttpClient(array &$options, $serviceName)
+ {
+ if (!isset($options['httpClient']) || !($options['httpClient'] instanceof ClientInterface)) {
+ if (strcasecmp($serviceName, 'identity') === 0) {
+ $baseUrl = $options['authUrl'];
+ $stack = $this->getStack($options['authHandler']);
+ } else {
+ list($token, $baseUrl) = $options['identityService']->authenticate($options);
+ $stack = $this->getStack($options['authHandler'], $token);
+ }
+
+ $this->addDebugMiddleware($options, $stack);
+
+ $options['httpClient'] = $this->httpClient($baseUrl, $stack);
+ }
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ private function addDebugMiddleware(array $options, HandlerStack &$stack)
+ {
+ if (!empty($options['debugLog'])
+ && !empty($options['logger'])
+ && !empty($options['messageFormatter'])
+ ) {
+ $stack->push(GuzzleMiddleware::log($options['logger'], $options['messageFormatter']));
+ }
+ }
+
+ private function stockIdentityService(array &$options)
+ {
+ if (!isset($options['identityService'])) {
+ $httpClient = $this->httpClient($options['authUrl'], HandlerStack::create());
+ $options['identityService'] = Service::factory($httpClient);
+ }
+ }
+
+ /**
+ * @param array $options
+ * @codeCoverageIgnore
+ */
+ private function stockAuthHandler(array &$options)
+ {
+ if (!isset($options['authHandler'])) {
+ $options['authHandler'] = function () use ($options) {
+ return $options['identityService']->generateToken($options);
+ };
+ }
+ }
+
+ private function getStack(callable $authHandler, Token $token = null)
+ {
+ $stack = HandlerStack::create();
+ $stack->push(Middleware::authHandler($authHandler, $token));
+ return $stack;
+ }
+
+ private function httpClient($baseUrl, HandlerStack $stack)
+ {
+ return new Client([
+ 'base_uri' => Utils::normalizeUrl($baseUrl),
+ 'handler' => $stack,
+ ]);
+ }
+
+ private function mergeOptions(array $serviceOptions)
+ {
+ $options = array_merge($this->defaults, $this->globalOptions, $serviceOptions);
+
+ if (!isset($options['authUrl'])) {
+ throw new \InvalidArgumentException('"authUrl" is a required option');
+ }
+
+ return $options;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Service/ServiceInterface.php b/server/vendor/php-opencloud/common/src/Common/Service/ServiceInterface.php
new file mode 100644
index 0000000..6ad3089
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Service/ServiceInterface.php
@@ -0,0 +1,14 @@
+push(Middleware::httpErrors());
+ $stack->push(Middleware::prepareBody());
+
+ return $stack;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Transport/JsonSerializer.php b/server/vendor/php-opencloud/common/src/Common/Transport/JsonSerializer.php
new file mode 100644
index 0000000..11b31d3
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Transport/JsonSerializer.php
@@ -0,0 +1,95 @@
+getName();
+ if ($path = $param->getPath()) {
+ $jsonPath = new JsonPath($json);
+ $jsonPath->set(sprintf("%s.%s", $path, $name), $userValue);
+ $json = $jsonPath->getStructure();
+ } elseif ($name) {
+ $json[$name] = $userValue;
+ } else {
+ $json[] = $userValue;
+ }
+
+ return $json;
+ }
+
+ /**
+ * Populates a value into an array-like structure.
+ *
+ * @param Parameter $param The schema that defines how the JSON field is being populated
+ * @param mixed $userValue The user value that is populating a JSON field
+ *
+ * @return array|mixed
+ */
+ private function stockArrayJson(Parameter $param, $userValue)
+ {
+ $elems = [];
+ foreach ($userValue as $item) {
+ $elems = $this->stockJson($param->getItemSchema(), $item, $elems);
+ }
+ return $elems;
+ }
+
+ /**
+ * Populates a value into an object-like structure.
+ *
+ * @param Parameter $param The schema that defines how the JSON field is being populated
+ * @param mixed $userValue The user value that is populating a JSON field
+ *
+ * @return array
+ */
+ private function stockObjectJson(Parameter $param, $userValue)
+ {
+ $object = [];
+ foreach ($userValue as $key => $val) {
+ $object = $this->stockJson($param->getProperty($key), $val, $object);
+ }
+ return $object;
+ }
+
+ /**
+ * A generic method that will populate a JSON structure with a value according to a schema. It
+ * supports multiple types and will delegate accordingly.
+ *
+ * @param Parameter $param The schema that defines how the JSON field is being populated
+ * @param mixed $userValue The user value that is populating a JSON field
+ * @param array $json The existing JSON structure that will be populated
+ *
+ * @return array
+ */
+ public function stockJson(Parameter $param, $userValue, $json)
+ {
+ if ($param->isArray()) {
+ $userValue = $this->stockArrayJson($param, $userValue);
+ } elseif ($param->isObject()) {
+ $userValue = $this->stockObjectJson($param, $userValue);
+ }
+ // Populate the final value
+ return $this->stockValue($param, $userValue, $json);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Transport/Middleware.php b/server/vendor/php-opencloud/common/src/Common/Transport/Middleware.php
new file mode 100644
index 0000000..916ff22
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Transport/Middleware.php
@@ -0,0 +1,94 @@
+then(
+ function (ResponseInterface $response) use ($request, $handler) {
+ if ($response->getStatusCode() < 400) {
+ return $response;
+ }
+ throw (new Builder())->httpError($request, $response);
+ }
+ );
+ };
+ };
+ }
+
+ /**
+ * @param callable $tokenGenerator
+ * @param Token $token
+ *
+ * @return callable
+ */
+ public static function authHandler(callable $tokenGenerator, Token $token = null)
+ {
+ return function (callable $handler) use ($tokenGenerator, $token) {
+ return new AuthHandler($handler, $tokenGenerator, $token);
+ };
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public static function history(array &$container)
+ {
+ return GuzzleMiddleware::history($container);
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public static function retry(callable $decider, callable $delay = null)
+ {
+ return GuzzleMiddleware::retry($decider, $delay);
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public static function log(LoggerInterface $logger, MessageFormatter $formatter, $logLevel = LogLevel::INFO)
+ {
+ return GuzzleMiddleware::log($logger, $formatter, $logLevel);
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public static function prepareBody()
+ {
+ return GuzzleMiddleware::prepareBody();
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public static function mapRequest(callable $fn)
+ {
+ return GuzzleMiddleware::mapRequest($fn);
+ }
+
+ /**
+ * @codeCoverageIgnore
+ */
+ public static function mapResponse(callable $fn)
+ {
+ return GuzzleMiddleware::mapResponse($fn);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Transport/RequestSerializer.php b/server/vendor/php-opencloud/common/src/Common/Transport/RequestSerializer.php
new file mode 100644
index 0000000..30f04af
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Transport/RequestSerializer.php
@@ -0,0 +1,85 @@
+jsonSerializer = $jsonSerializer ?: new JsonSerializer();
+ }
+
+ public function serializeOptions(Operation $operation, array $userValues = [])
+ {
+ $options = ['headers' => []];
+
+ foreach ($userValues as $paramName => $paramValue) {
+ if (null === ($schema = $operation->getParam($paramName))) {
+ continue;
+ }
+
+ $method = sprintf('stock%s', ucfirst($schema->getLocation()));
+ $this->$method($schema, $paramValue, $options);
+ }
+
+ if (!empty($options['json'])) {
+ if ($key = $operation->getJsonKey()) {
+ $options['json'] = [$key => $options['json']];
+ }
+ if (strpos(json_encode($options['json']), '\/') !== false) {
+ $options['body'] = json_encode($options['json'], JSON_UNESCAPED_SLASHES);
+ $options['headers']['Content-Type'] = 'application/json';
+ unset($options['json']);
+ }
+ }
+
+ return $options;
+ }
+
+ private function stockUrl()
+ {
+ }
+
+ private function stockQuery(Parameter $schema, $paramValue, array &$options)
+ {
+ $options['query'][$schema->getName()] = $paramValue;
+ }
+
+ private function stockHeader(Parameter $schema, $paramValue, array &$options)
+ {
+ $paramName = $schema->getName();
+
+ if (stripos($paramName, 'metadata') !== false) {
+ return $this->stockMetadataHeader($schema, $paramValue, $options);
+ }
+
+ $options['headers'] += is_scalar($paramValue) ? [$schema->getPrefixedName() => $paramValue] : [];
+ }
+
+ private function stockMetadataHeader(Parameter $schema, $paramValue, array &$options)
+ {
+ foreach ($paramValue as $key => $keyVal) {
+ $schema = $schema->getItemSchema() ?: new Parameter(['prefix' => $schema->getPrefix(), 'name' => $key]);
+ $this->stockHeader($schema, $keyVal, $options);
+ }
+ }
+
+ private function stockJson(Parameter $schema, $paramValue, array &$options)
+ {
+ $json = isset($options['json']) ? $options['json'] : [];
+ $options['json'] = $this->jsonSerializer->stockJson($schema, $paramValue, $json);
+ }
+
+ private function stockRaw(Parameter $schema, $paramValue, array &$options)
+ {
+ $options['body'] = $paramValue;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/src/Common/Transport/Utils.php b/server/vendor/php-opencloud/common/src/Common/Transport/Utils.php
new file mode 100644
index 0000000..c2a2dc1
--- /dev/null
+++ b/server/vendor/php-opencloud/common/src/Common/Transport/Utils.php
@@ -0,0 +1,88 @@
+ 'JSON_ERROR_DEPTH - Maximum stack depth exceeded',
+ JSON_ERROR_STATE_MISMATCH => 'JSON_ERROR_STATE_MISMATCH - Underflow or the modes mismatch',
+ JSON_ERROR_CTRL_CHAR => 'JSON_ERROR_CTRL_CHAR - Unexpected control character found',
+ JSON_ERROR_SYNTAX => 'JSON_ERROR_SYNTAX - Syntax error, malformed JSON',
+ JSON_ERROR_UTF8 => 'JSON_ERROR_UTF8 - Malformed UTF-8 characters, possibly incorrectly encoded'
+ ];
+
+ $responseBody = (string) $response->getBody();
+
+ if (strlen($responseBody) === 0) {
+ return $responseBody;
+ }
+
+ $data = json_decode($responseBody, $assoc);
+
+ if (JSON_ERROR_NONE !== json_last_error()) {
+ $last = json_last_error();
+ throw new \InvalidArgumentException(
+ 'Unable to parse JSON data: ' . (isset($jsonErrors[$last]) ? $jsonErrors[$last] : 'Unknown error')
+ );
+ }
+
+ return $data;
+ }
+
+ /**
+ * Method for flattening a nested array.
+ *
+ * @param array $data The nested array
+ * @param null $key The key to extract
+ *
+ * @return array
+ */
+ public static function flattenJson($data, $key = null)
+ {
+ return (!empty($data) && $key && isset($data[$key])) ? $data[$key] : $data;
+ }
+
+ /**
+ * Method for normalize an URL string.
+ *
+ * Append the http:// prefix if not present, and add a
+ * closing url separator when missing.
+ *
+ * @param string $url The url representation.
+ *
+ * @return string
+ */
+ public static function normalizeUrl($url)
+ {
+ if (strpos($url, 'http') === false) {
+ $url = 'http://' . $url;
+ }
+
+ return rtrim($url, '/') . '/';
+ }
+
+ /**
+ * Add an unlimited list of paths to a given URI.
+ *
+ * @param UriInterface $uri
+ * @param ...$paths
+ *
+ * @return UriInterface
+ */
+ public static function addPaths(UriInterface $uri, ...$paths)
+ {
+ return uri_for(rtrim((string) $uri, '/') . '/' . implode('/', $paths));
+ }
+
+ public static function appendPath(UriInterface $uri, $path)
+ {
+ return uri_for(rtrim((string) $uri, '/') . '/' . $path);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/integration/DefaultLogger.php b/server/vendor/php-opencloud/common/tests/integration/DefaultLogger.php
new file mode 100644
index 0000000..a546c70
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/DefaultLogger.php
@@ -0,0 +1,20 @@
+format($level, $message, $context);
+ }
+
+ private function format($level, $message, $context)
+ {
+ $msg = strtr($message, $context);
+
+ return sprintf("%s: %s\n", strtoupper($level), $msg);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/integration/Runner.php b/server/vendor/php-opencloud/common/tests/integration/Runner.php
new file mode 100644
index 0000000..98d094c
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/Runner.php
@@ -0,0 +1,112 @@
+basePath = $basePath;
+ $this->logger = new DefaultLogger();
+ $this->assembleServicesFromSamples();
+ }
+
+ private function traverse($path)
+ {
+ return new \RecursiveDirectoryIterator($path, \FilesystemIterator::SKIP_DOTS);
+ }
+
+ private function assembleServicesFromSamples()
+ {
+ foreach ($this->traverse($this->basePath) as $servicePath) {
+ if ($servicePath->isDir()) {
+ foreach ($this->traverse($servicePath) as $versionPath) {
+ $this->services[$servicePath->getBasename()][] = $versionPath->getBasename();
+ }
+ }
+ }
+ }
+
+ private function getOpts()
+ {
+ $opts = getopt('s:v:t:', ['service:', 'version:', 'test::', 'debug::', 'help::']);
+
+ $getOpt = function (array $keys, $default) use ($opts) {
+ foreach ($keys as $key) {
+ if (isset($opts[$key])) {
+ return $opts[$key];
+ }
+ }
+ return $default;
+ };
+
+ return [
+ $getOpt(['s', 'service'], 'all'),
+ $getOpt(['n', 'version'], 'all'),
+ $getOpt(['t', 'test'], ''),
+ isset($opts['debug']) ? (int) $opts['debug'] : 0,
+ ];
+ }
+
+ private function getRunnableServices($service, $version)
+ {
+ $services = $this->services;
+
+ if ($service != 'all') {
+ if (!isset($this->services[$service])) {
+ throw new \InvalidArgumentException(sprintf("%s service does not exist", $service));
+ }
+
+ $versions = ($version == 'all') ? $this->services[$service] : [$version];
+ $services = [$service => $versions];
+ }
+
+ return $services;
+ }
+
+ /**
+ * @return TestInterface
+ */
+ private function getTest($serviceName, $version, $verbosity)
+ {
+ $namespace = (new \ReflectionClass($this))->getNamespaceName();
+ $className = sprintf("%s\\%s\\%sTest", $namespace, Utils::toCamelCase($serviceName), ucfirst($version));
+
+ if (!class_exists($className)) {
+ throw new \RuntimeException(sprintf("%s does not exist", $className));
+ }
+
+ $basePath = $this->basePath . DIRECTORY_SEPARATOR . $serviceName . DIRECTORY_SEPARATOR . $version;
+ $smClass = sprintf("%s\\SampleManager", $namespace);
+ $class = new $className($this->logger, new $smClass($basePath, $verbosity));
+
+ if (!($class instanceof TestInterface)) {
+ throw new \RuntimeException(sprintf("%s does not implement TestInterface", $className));
+ }
+
+ return $class;
+ }
+
+ public function runServices()
+ {
+ list($serviceOpt, $versionOpt, $testMethodOpt, $verbosityOpt) = $this->getOpts();
+
+ foreach ($this->getRunnableServices($serviceOpt, $versionOpt) as $serviceName => $versions) {
+ foreach ($versions as $version) {
+ $testRunner = $this->getTest($serviceName, $version, $verbosityOpt);
+
+ if ($testMethodOpt) {
+ $testRunner->runOneTest($testMethodOpt);
+ } else {
+ $testRunner->runTests();
+ }
+
+ $testRunner->teardown();
+ }
+ }
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/integration/SampleManager.php b/server/vendor/php-opencloud/common/tests/integration/SampleManager.php
new file mode 100644
index 0000000..4bd8b9a
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/SampleManager.php
@@ -0,0 +1,104 @@
+basePath = $basePath;
+ $this->verbosity = $verbosity;
+ }
+
+ public function deletePaths()
+ {
+ if (!empty($this->paths)) {
+ foreach ($this->paths as $path) {
+ unlink($path);
+ }
+ }
+ }
+
+ protected function getGlobalReplacements()
+ {
+ return [
+ '{userId}' => getenv('OS_USER_ID'),
+ '{username}' => getenv('OS_USERNAME'),
+ '{password}' => getenv('OS_PASSWORD'),
+ '{domainId}' => getenv('OS_DOMAIN_ID'),
+ '{authUrl}' => getenv('OS_AUTH_URL'),
+ '{tenantId}' => getenv('OS_TENANT_ID'),
+ '{region}' => getenv('OS_REGION'),
+ '{projectId}' => getenv('OS_PROJECT_ID'),
+ '{projectName}' => getenv('OS_PROJECT_NAME'),
+ ];
+ }
+
+ protected function getConnectionTemplate()
+ {
+ if ($this->verbosity === 1) {
+ $subst = <<<'EOL'
+use OpenCloud\Integration\DefaultLogger;
+use OpenCloud\Integration\Utils;
+use GuzzleHttp\MessageFormatter;
+
+$options = [
+ 'debugLog' => true,
+ 'logger' => new DefaultLogger(),
+ 'messageFormatter' => new MessageFormatter(),
+];
+$openstack = new OpenCloud\OpenCloud(Utils::getAuthOpts($options));
+EOL;
+ } elseif ($this->verbosity === 2) {
+ $subst = <<<'EOL'
+use OpenCloud\Integration\DefaultLogger;
+use OpenCloud\Integration\Utils;
+use GuzzleHttp\MessageFormatter;
+
+$options = [
+ 'debugLog' => true,
+ 'logger' => new DefaultLogger(),
+ 'messageFormatter' => new MessageFormatter(MessageFormatter::DEBUG),
+];
+$openstack = new OpenCloud\OpenCloud(Utils::getAuthOpts($options));
+EOL;
+ } else {
+ $subst = <<<'EOL'
+use OpenCloud\Integration\Utils;
+
+$openstack = new OpenCloud\OpenCloud(Utils::getAuthOpts());
+EOL;
+ }
+
+ return $subst;
+ }
+
+ public function write($path, array $replacements)
+ {
+ $replacements = array_merge($this->getGlobalReplacements(), $replacements);
+
+ $sampleFile = rtrim($this->basePath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $path;
+
+ if (!file_exists($sampleFile) || !is_readable($sampleFile)) {
+ throw new \RuntimeException(sprintf("%s either does not exist or is not readable", $sampleFile));
+ }
+
+ $content = strtr(file_get_contents($sampleFile), $replacements);
+ $content = str_replace("'vendor/'", "'" . dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . "vendor'", $content);
+
+ $subst = $this->getConnectionTemplate();
+ $content = preg_replace('/\([^)]+\)/', '', $content, 1);
+ $content = str_replace('$openstack = new OpenCloud\OpenCloud;', $subst, $content);
+
+ $tmp = tempnam(sys_get_temp_dir(), 'openstack');
+ file_put_contents($tmp, $content);
+
+ $this->paths[] = $tmp;
+
+ return $tmp;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/integration/SampleManagerInterface.php b/server/vendor/php-opencloud/common/tests/integration/SampleManagerInterface.php
new file mode 100644
index 0000000..f0fe848
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/SampleManagerInterface.php
@@ -0,0 +1,10 @@
+logger = $logger;
+ $this->sampleManager = $sampleManager;
+ }
+
+ public function teardown()
+ {
+ $this->sampleManager->deletePaths();
+ }
+
+ public function runOneTest($name)
+ {
+ if (!method_exists($this, $name)) {
+ throw new \InvalidArgumentException(sprintf("%s method does not exist", $name));
+ }
+
+ $this->startTimer();
+ $this->$name();
+ $this->outputTimeTaken();
+ }
+
+ protected function startTimer()
+ {
+ $this->startPoint = $this->lastPoint = microtime(true);
+ }
+
+ private function wrapColor($message, $colorPrefix)
+ {
+ return sprintf("%s%s", $colorPrefix, $message) . "\033[0m\033[1;0m";
+ }
+
+ protected function logStep($message, array $context = [])
+ {
+ $duration = microtime(true) - $this->lastPoint;
+
+ $stepTimeTaken = sprintf('(%s)', $this->formatSecDifference($duration));
+
+ if ($duration >= 10) {
+ $color = "\033[0m\033[1;31m"; // red
+ } elseif ($duration >= 2) {
+ $color = "\033[0m\033[1;33m"; // yellow
+ } else {
+ $color = "\033[0m\033[1;32m"; // green
+ }
+
+ $message = '{timeTaken} ' . $message;
+ $context['{timeTaken}'] = $this->wrapColor($stepTimeTaken, $color);
+
+ $this->logger->info($message, $context);
+
+ $this->lastPoint = microtime(true);
+ }
+
+ protected function randomStr($length = 5)
+ {
+ $chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ $charsLen = strlen($chars);
+
+ $randomString = '';
+ for ($i = 0; $i < $length; $i++) {
+ $randomString .= $chars[rand(0, $charsLen - 1)];
+ }
+
+ return 'phptest_' . $randomString;
+ }
+
+ private function formatMinDifference($duration)
+ {
+ $output = '';
+
+ if (($minutes = floor($duration / 60)) > 0) {
+ $output .= $minutes . 'min' . (($minutes > 1) ? 's' : '');
+ }
+
+ if (($seconds = number_format(fmod($duration, 60), 2)) > 0) {
+ if ($minutes > 0) {
+ $output .= ' ';
+ }
+ $output .= $seconds . 's';
+ }
+
+ return $output;
+ }
+
+ private function formatSecDifference($duration)
+ {
+ return number_format($duration, 2) . 's';
+ }
+
+ protected function outputTimeTaken()
+ {
+ $output = $this->formatMinDifference(microtime(true) - $this->startPoint);
+
+ $this->logger->info('Finished all tests! Time taken: {output}.', ['{output}' => $output]);
+ }
+
+ protected function sampleFile(array $replacements, $path)
+ {
+ return $this->sampleManager->write($path, $replacements);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/integration/TestInterface.php b/server/vendor/php-opencloud/common/tests/integration/TestInterface.php
new file mode 100644
index 0000000..a89e8af
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/TestInterface.php
@@ -0,0 +1,16 @@
+ getenv('OS_AUTH_URL'),
+ 'region' => getenv('OS_REGION_NAME'),
+ 'user' => [
+ 'id' => getenv('OS_USER_ID'),
+ 'password' => getenv('OS_PASSWORD'),
+ ],
+ 'scope' => [
+ 'project' => [
+ 'id' => getenv('OS_PROJECT_ID'),
+ ]
+ ]
+ ];
+ }
+
+ public static function getAuthOptsV2()
+ {
+ $httpClient = new Client([
+ 'base_uri' => CommonUtils::normalizeUrl(getenv('OS_AUTH_URL')),
+ 'handler' => HandlerStack::create(),
+ ]);
+ return [
+ 'authUrl' => getenv('OS_AUTH_URL'),
+ 'region' => getenv('OS_REGION_NAME'),
+ 'username' => getenv('OS_USERNAME'),
+ 'password' => getenv('OS_PASSWORD'),
+ 'tenantName' => getenv('OS_TENANT_NAME'),
+ 'identityService' => new Service($httpClient, new Api),
+ ];
+ }
+
+ public static function getAuthOpts(array $options = [])
+ {
+ $authOptions = getenv('OS_IDENTITY_API_VERSION') == '2.0'
+ ? self::getAuthOptsV2()
+ : self::getAuthOptsV3();
+
+ return array_merge($authOptions, $options);
+ }
+
+ public static function toCamelCase($word, $separator = '_')
+ {
+ return str_replace($separator, '', ucwords($word, $separator));
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/integration/run.php b/server/vendor/php-opencloud/common/tests/integration/run.php
new file mode 100644
index 0000000..5bc5548
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/run.php
@@ -0,0 +1,10 @@
+runServices();
diff --git a/server/vendor/php-opencloud/common/tests/integration/script/compute_v2 b/server/vendor/php-opencloud/common/tests/integration/script/compute_v2
new file mode 100755
index 0000000..5e90b05
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/script/compute_v2
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+php tests/integration/Runner.php -s compute -v v2
diff --git a/server/vendor/php-opencloud/common/tests/integration/script/identity_v3 b/server/vendor/php-opencloud/common/tests/integration/script/identity_v3
new file mode 100755
index 0000000..cd175b5
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/script/identity_v3
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+php tests/integration/Runner.php -s identity -v v3
diff --git a/server/vendor/php-opencloud/common/tests/integration/script/networking_v2 b/server/vendor/php-opencloud/common/tests/integration/script/networking_v2
new file mode 100755
index 0000000..9583f68
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/script/networking_v2
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+php tests/integration/Runner.php -s networking -v v2
diff --git a/server/vendor/php-opencloud/common/tests/integration/script/objectstore_v2 b/server/vendor/php-opencloud/common/tests/integration/script/objectstore_v2
new file mode 100755
index 0000000..3fb893a
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/integration/script/objectstore_v2
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+php tests/integration/Runner.php -s objectstore -v v2
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Api/OperationTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Api/OperationTest.php
new file mode 100644
index 0000000..ea0f7c8
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Api/OperationTest.php
@@ -0,0 +1,74 @@
+postServer();
+
+ $this->operation = new Operation($def);
+ }
+
+ public function test_it_reveals_whether_params_are_set_or_not()
+ {
+ $this->assertFalse($this->operation->hasParam('foo'));
+ $this->assertTrue($this->operation->hasParam('name'));
+ }
+
+ public function test_it_gets_params()
+ {
+ $this->assertInstanceOf(Parameter::class, $this->operation->getParam('name'));
+ }
+
+ public function test_it_validates_params()
+ {
+ $this->assertTrue($this->operation->validate([
+ 'name' => 'foo',
+ 'imageId' => 'bar',
+ 'flavorId' => 'baz',
+ ]));
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function test_exceptions_are_propagated()
+ {
+ $this->assertFalse($this->operation->validate([
+ 'name' => true,
+ 'imageId' => 'bar',
+ 'flavorId' => 'baz',
+ ]));
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function test_an_exception_is_thrown_when_user_does_not_provide_required_options()
+ {
+ $this->operation->validate([]);
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function test_it_throws_exception_when_user_provides_undefined_options()
+ {
+ $userData = ['name' => 'new_server', 'undefined_opt' => 'bah'];
+
+ $this->operation->validate($userData);
+ }
+
+ public function test_it_gets_json_key()
+ {
+ $this->assertEquals('server', $this->operation->getJsonKey());
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Api/OperatorTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Api/OperatorTest.php
new file mode 100644
index 0000000..43c5804
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Api/OperatorTest.php
@@ -0,0 +1,137 @@
+rootFixturesDir = __DIR__;
+
+ $this->def = [
+ 'method' => 'GET',
+ 'path' => 'test',
+ 'params' => [],
+ ];
+
+ $this->operator = new TestOperator($this->client->reveal(), new ComputeV2Api());
+ }
+
+ public function test_it_returns_operations()
+ {
+ $this->assertInstanceOf(
+ 'OpenCloud\Common\Api\Operation',
+ $this->operator->getOperation($this->def, [])
+ );
+ }
+
+ public function test_it_sends_a_request_when_operations_are_executed()
+ {
+ $this->client->request('GET', 'test', ['headers' => []])->willReturn(new Request('GET', 'test'));
+
+ $this->operator->execute($this->def, []);
+ }
+
+ public function test_it_sends_a_request_when_async_operations_are_executed()
+ {
+ $this->client->requestAsync('GET', 'test', ['headers' => []])->willReturn(new Promise());
+
+ $this->operator->executeAsync($this->def, []);
+ }
+
+ public function test_it_returns_a_model_instance()
+ {
+ $this->assertInstanceOf(ResourceInterface::class, $this->operator->model(TestResource::class));
+ }
+
+ public function test_it_populates_models_from_response()
+ {
+ $this->assertInstanceOf(ResourceInterface::class, $this->operator->model(TestResource::class, new Response(200)));
+ }
+
+ public function test_it_populates_models_from_arrays()
+ {
+ $data = ['flavor' => [], 'image' => []];
+ $this->assertInstanceOf(ResourceInterface::class, $this->operator->model(TestResource::class, $data));
+ }
+
+ public function test_it_wraps_sequential_ops_in_promise_when_async_is_appended_to_method_name()
+ {
+ $promise = $this->operator->createAsync('something');
+
+ $this->assertInstanceOf(Promise::class, $promise);
+
+ $promise->then(function ($val) {
+ $this->assertEquals('Created something', $val);
+ });
+
+ $promise->wait();
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function test_it_throws_exception_when_async_is_called_on_a_non_existent_method()
+ {
+ $this->operator->fooAsync();
+ }
+
+ public function test_it_retrieves_base_http_url()
+ {
+ $returnedUri = uri_for('http://foo.com');
+
+ $this->client->getConfig('base_uri')->shouldBeCalled()->willReturn($returnedUri);
+
+ $uri = $this->operator->testBaseUri();
+
+ $this->assertInstanceOf(Uri::class, $uri);
+ $this->assertEquals($returnedUri, $uri);
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function test_undefined_methods_result_in_error()
+ {
+ $this->operator->foo();
+ }
+}
+
+class TestResource extends AbstractResource
+{
+}
+
+class TestOperator extends Operator
+{
+ public function testBaseUri()
+ {
+ return $this->getHttpBaseUrl();
+ }
+
+ public function create($str)
+ {
+ return 'Created ' . $str;
+ }
+
+ public function fail()
+ {
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Api/ParameterTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Api/ParameterTest.php
new file mode 100644
index 0000000..65d4760
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Api/ParameterTest.php
@@ -0,0 +1,203 @@
+api = new ComputeV2Api();
+
+ $this->data = $this->api->postServer()['params']['name'] + ['name' => 'name'];
+ $this->param = new Parameter($this->data);
+ }
+
+ /**
+ * @expectedException \RuntimeException
+ */
+ public function test_exception_is_thrown_for_invalid_locations()
+ {
+ $data = $this->data;
+ $data['location'] = 'foo';
+ new Parameter($data);
+ }
+
+ public function test_it_should_provide_access_to_a_name()
+ {
+ $this->assertEquals($this->data['name'], $this->param->getName());
+ }
+
+ public function test_it_should_use_sentAs_alias_for_name_if_one_is_set()
+ {
+ $data = $this->data + ['sentAs' => 'foo'];
+ $param = new Parameter($data);
+
+ $this->assertEquals($data['sentAs'], $param->getName());
+ }
+
+ public function test_it_indicates_whether_it_is_required_or_not()
+ {
+ $this->assertTrue($this->param->isRequired());
+ }
+
+ public function test_it_indicates_its_item_schema()
+ {
+ $data = $this->api->postServer()['params']['networks'] + ['name' => 'networks'];
+ $param = new Parameter($data);
+
+ $this->assertInstanceOf(self::PARAMETER_CLASS, $param->getItemSchema());
+ }
+
+ public function test_it_allows_property_retrieval()
+ {
+ $definition = $this->api->postServer()['params']['networks']['items'] + ['name' => 'network'];
+ $param = new Parameter($definition);
+
+ $this->assertInstanceOf(self::PARAMETER_CLASS, $param->getProperty('uuid'));
+ }
+
+ public function test_it_indicates_its_path()
+ {
+ $path = 'foo.bar.baz';
+ $param = new Parameter($this->data + ['path' => $path]);
+
+ $this->assertEquals($path, $param->getPath());
+ }
+
+ public function test_it_verifies_a_given_location_with_a_boolean()
+ {
+ $this->assertFalse($this->param->hasLocation('foo'));
+ $this->assertTrue($this->param->hasLocation('json'));
+ }
+
+ public function test_it_should_return_true_when_required_attributes_are_provided_and_match_their_definitions()
+ {
+ $this->assertTrue($this->param->validate('TestName'));
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function test_it_throws_exception_when_values_do_not_match_their_definition_types()
+ {
+ $data = $this->api->postServer()['params']['networks'] + ['name' => 'networks'];
+ $param = new Parameter($data);
+
+ $param->validate('a_network!'); // should be an array
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function test_it_throws_exception_when_deeply_nested_values_have_wrong_types()
+ {
+ $data = $this->api->postServer()['params']['networks'] + ['name' => 'networks'];
+
+ $param = new Parameter($data);
+ $param->validate(['name' => false]); // value should be a string, not bool
+ }
+
+ public function test_metadata_properties_are_handled_differently()
+ {
+ $params = [
+ 'name' => 'metadata',
+ 'type' => 'object',
+ 'properties' => [
+ 'type' => 'string',
+ ],
+ ];
+
+ $userValues = ['some' => 'value'];
+
+ $param = new Parameter($params);
+ $this->assertTrue($param->validate($userValues));
+ }
+
+ public function test_it_passes_validation_when_array_values_pass()
+ {
+ $params = [
+ 'name' => 'foo',
+ 'type' => 'array',
+ 'items' => ['type' => 'string'],
+ ];
+
+ $userVals = ['1', '2', '3'];
+
+ $param = new Parameter($params);
+ $this->assertTrue($param->validate($userVals));
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function test_an_exception_is_thrown_when_an_undefined_property_is_provided()
+ {
+ $params = ['type' => 'object', 'properties' => ['foo' => ['type' => 'string']]];
+ $userVals = ['bar' => 'baz'];
+
+ $param = new Parameter($params);
+ $param->validate($userVals);
+ }
+
+ public function test_it_passes_validation_when_all_subproperties_pass()
+ {
+ $params = ['type' => 'object', 'properties' => ['foo' => ['type' => 'string']]];
+ $userVals = ['foo' => 'baz'];
+
+ $param = new Parameter($params);
+ $this->assertTrue($param->validate($userVals));
+ }
+
+ public function test_it_sets_name()
+ {
+ $this->param->setName('foo');
+ $this->assertEquals($this->param->getName(), 'foo');
+ }
+
+ public function test_it_gets_property()
+ {
+ $property = new Parameter([
+ 'name' => 'metadata',
+ 'properties' => [
+ 'type' => 'string',
+ 'prefix' => 'foo',
+ ],
+ ]);
+
+ $prop = $property->getProperty('metadata');
+
+ $this->assertInstanceOf(Parameter::class, $prop);
+ $this->assertEquals('foo', $prop->getPrefix());
+ }
+
+ public function test_it_gets_prefixed_name()
+ {
+ $property = new Parameter([
+ 'name' => 'metadata',
+ 'prefix' => 'foo-',
+ ]);
+
+ $this->assertEquals('foo-metadata', $property->getPrefixedName());
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function test_exception_is_thrown_when_value_is_not_in_enum_list()
+ {
+ $data = $this->data;
+ $data['enum'] = ['foo'];
+
+ $param = new Parameter($data);
+ $param->validate('blah');
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Api/fixtures/headers.php b/server/vendor/php-opencloud/common/tests/unit/Common/Api/fixtures/headers.php
new file mode 100644
index 0000000..f428992
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Api/fixtures/headers.php
@@ -0,0 +1,25 @@
+ 'POST',
+ 'path' => 'something',
+ 'params' => [
+ 'name' => [
+ 'type' => 'string',
+ 'location' => 'header',
+ 'sentAs' => 'X-Foo-Name'
+ ],
+ 'age' => [
+ 'type' => 'integer',
+ 'location' => 'header'
+ ],
+ 'metadata' => [
+ 'type' => 'object',
+ 'location' => 'header',
+ 'items' => [
+ 'prefix' => 'X-Meta-'
+ ]
+ ],
+ 'other' => ['type' => 'string'] // should not be a header
+ ],
+];
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Api/fixtures/jsonBody.php b/server/vendor/php-opencloud/common/tests/unit/Common/Api/fixtures/jsonBody.php
new file mode 100644
index 0000000..350affd
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Api/fixtures/jsonBody.php
@@ -0,0 +1,27 @@
+ 'POST',
+ 'path' => 'something',
+ 'params' => [
+ 'name' => [
+ 'type' => 'string',
+ 'sentAs' => 'server_name',
+ ],
+ 'other' => [
+ 'type' => 'array',
+ 'sentAs' => 'other_params',
+ 'items' => [
+ 'type' => 'string'
+ ]
+ ],
+ 'etc' => [
+ 'type' => 'object',
+ 'sentAs' => 'etcetc',
+ 'properties' => [
+ 'dob' => ['type' => 'string'],
+ 'age' => ['type' => 'integer', 'sentAs' => 'current_age'],
+ ]
+ ],
+ ],
+];
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/ArrayAccessTraitTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/ArrayAccessTraitTest.php
new file mode 100644
index 0000000..f5d1fdb
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/ArrayAccessTraitTest.php
@@ -0,0 +1,59 @@
+aa = new ArrayAccess();
+ }
+
+ public function test_offset_is_set()
+ {
+ $this->aa->offsetSet('foo', 'bar');
+ $this->assertEquals(['foo' => 'bar'], $this->aa->getElements());
+ }
+
+ public function test_it_appends_if_no_key_is_set()
+ {
+ $this->aa->offsetSet(null, 'bar');
+ $this->assertEquals(['bar'], $this->aa->getElements());
+ }
+
+ public function test_if_checks_if_offset_exists()
+ {
+ $this->aa->offsetSet('bar', 'foo');
+ $this->assertTrue($this->aa->offsetExists('bar'));
+ $this->assertFalse($this->aa->offsetExists('baz'));
+ }
+
+ public function test_if_gets_offset()
+ {
+ $this->aa->offsetSet('bar', 'foo');
+ $this->assertEquals('foo', $this->aa->offsetGet('bar'));
+ $this->assertNull($this->aa->offsetGet('baz'));
+ }
+
+ public function test_it_unsets_offset()
+ {
+ $this->aa->offsetSet('bar', 'foo');
+ $this->aa->offsetUnset('bar');
+ $this->assertNull($this->aa->offsetGet('bar'));
+ }
+}
+
+class ArrayAccess
+{
+ use ArrayAccessTrait;
+
+ public function getElements()
+ {
+ return $this->internalState;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Auth/AuthHandlerTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Auth/AuthHandlerTest.php
new file mode 100644
index 0000000..3c77dde
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Auth/AuthHandlerTest.php
@@ -0,0 +1,59 @@
+generator = function () {
+ $token = $this->prophesize(FakeToken::class);
+ $token->getId()->shouldBeCalled()->willReturn(self::TOKEN_ID);
+ return $token->reveal();
+ };
+
+ $this->handler = function (RequestInterface $r) {
+ return $r;
+ };
+
+ $this->handler = new AuthHandler($this->handler, $this->generator);
+ }
+
+ public function test_it_should_bypass_auth_http_requests()
+ {
+ // Fake a Keystone request
+ $request = new Request('POST', 'https://my-openstack.org:5000/v2.0/tokens');
+
+ $this->assertEquals($request, call_user_func_array($this->handler, [$request, []]));
+ }
+
+ public function test_it_should_generate_a_new_token_if_the_current_token_is_either_expired_or_not_set()
+ {
+ $token = $this->prophesize(Token::class);
+
+ // force the mock token to indicate that its expired
+ $token->getId()->willReturn('');
+ $token->hasExpired()->willReturn(true);
+
+ $request = new Request('GET', '');
+
+ $handler = new AuthHandler($this->handler, $this->generator, $token->reveal());
+ $handler($request, []);
+ }
+}
+
+class FakeToken implements Token {
+ public function getId() {}
+ public function hasExpired() {}
+}
\ No newline at end of file
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/DateTime.php b/server/vendor/php-opencloud/common/tests/unit/Common/DateTime.php
new file mode 100644
index 0000000..512ba30
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/DateTime.php
@@ -0,0 +1,16 @@
+format(self::ISO8601);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Error/BadResponseErrorTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Error/BadResponseErrorTest.php
new file mode 100644
index 0000000..7aa4bf1
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Error/BadResponseErrorTest.php
@@ -0,0 +1,34 @@
+e = new BadResponseError();
+ }
+
+ public function test_it_gets_request()
+ {
+ $r = new Request('GET', '');
+
+ $this->e->setRequest($r);
+ $this->assertEquals($this->e->getRequest(), $r);
+ }
+
+ public function test_it_gets_response()
+ {
+ $r = new Response(500);
+
+ $this->e->setResponse($r);
+ $this->assertEquals($this->e->getResponse(), $r);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Error/BuilderTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Error/BuilderTest.php
new file mode 100644
index 0000000..e23806d
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Error/BuilderTest.php
@@ -0,0 +1,117 @@
+client = $this->prophesize(ClientInterface::class);
+ $this->builder = new Builder($this->client->reveal());
+ }
+
+ public function test_it_injects_client()
+ {
+ $this->assertInstanceOf(Builder::class, new Builder($this->client->reveal()));
+ }
+
+ public function test_it_builds_http_errors()
+ {
+ $request = new Request('POST', '/servers');
+ $response = new Response(400, [], stream_for('Invalid parameters'));
+
+ $requestStr = trim($this->builder->str($request));
+ $responseStr = trim($this->builder->str($response));
+
+ $errorMessage = <<setRequest($request);
+ $e->setResponse($response);
+
+ $this->assertEquals($e, $this->builder->httpError($request, $response));
+ }
+
+ public function test_it_builds_user_input_errors()
+ {
+ $expected = 'A well-formed string';
+ $value = ['foo' => true];
+ $link = 'http://docs.php-opencloud.com/en/latest/index.html';
+
+ $errorMessage = << 1
+)
+
+Please ensure that the value adheres to the expectation above. Visit $link for more information about input arguments. If you run into trouble, please open a support issue on https://github.com/php-opencloud/openstack/issues.
+EOT;
+
+ $this->client
+ ->request('HEAD', $link)
+ ->shouldBeCalled()
+ ->willReturn(new Response(200));
+
+ $e = new UserInputError($errorMessage);
+
+ $this->assertEquals($e, $this->builder->userInputError($expected, $value, 'index.html'));
+ }
+
+ public function test_dead_links_are_ignored()
+ {
+ $expected = 'A well-formed string';
+ $value = ['foo' => true];
+
+ $errorMessage = << 1
+)
+
+Please ensure that the value adheres to the expectation above. If you run into trouble, please open a support issue on https://github.com/php-opencloud/openstack/issues.
+EOT;
+
+ $this->client
+ ->request('HEAD', 'http://docs.php-opencloud.com/en/latest/sdffsda')
+ ->shouldBeCalled()
+ ->willThrow(ClientException::class);
+
+ $e = new UserInputError($errorMessage);
+
+ $this->assertEquals($e, $this->builder->userInputError($expected, $value, 'sdffsda'));
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/HydratorStrategyTraitTes.php b/server/vendor/php-opencloud/common/tests/unit/Common/HydratorStrategyTraitTes.php
new file mode 100644
index 0000000..6841002
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/HydratorStrategyTraitTes.php
@@ -0,0 +1,52 @@
+fixture = new Fixture();
+ }
+
+ public function test_it_hydrates()
+ {
+ $data = ['foo' => 1, 'bar' => 2, 'baz' => 3, 'boo' => 4];
+
+ $this->fixture->hydrate($data);
+
+ $this->assertEquals(1, $this->fixture->foo);
+ $this->assertEquals(2, $this->fixture->getBar());
+ $this->assertEquals(3, $this->fixture->getBaz());
+ }
+
+ public function test_it_hydrates_aliases()
+ {
+ $this->fixture->hydrate(['FOO!' => 1], ['FOO!' => 'foo']);
+
+ $this->assertEquals(1, $this->fixture->foo);
+ }
+}
+
+class Fixture
+{
+ public $foo;
+ protected $bar;
+ private $baz;
+
+ use HydratorStrategyTrait;
+
+ public function getBar()
+ {
+ return $this->bar;
+ }
+ public function getBaz()
+ {
+ return $this->baz;
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/JsonPathTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/JsonPathTest.php
new file mode 100644
index 0000000..1410076
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/JsonPathTest.php
@@ -0,0 +1,70 @@
+jsonPath = new JsonPath([]);
+ }
+
+ public function test_it_sets_values_according_to_paths()
+ {
+ $this->jsonPath->set('foo.bar.baz', 'VALUE');
+
+ $expected = [
+ 'foo' => [
+ 'bar' => [
+ 'baz' => 'VALUE',
+ ]
+ ]
+ ];
+
+ $this->assertEquals($expected, $this->jsonPath->getStructure());
+ }
+
+ public function test_it_sets_arrays_according_to_paths()
+ {
+ $jsonPath = new JsonPath([
+ 'foo' => [
+ 'bar' => [
+ 'value' => 'VALUE',
+ ]
+ ]
+ ]);
+
+ $jsonPath->set('foo.bar.items', ['item_1', 'item_2']);
+
+ $expected = [
+ 'foo' => [
+ 'bar' => [
+ 'value' => 'VALUE',
+ 'items' => ['item_1', 'item_2'],
+ ]
+ ]
+ ];
+
+ $this->assertEquals($expected, $jsonPath->getStructure());
+ }
+
+ public function test_it_gets_values_according_to_paths()
+ {
+ $jsonPath = new JsonPath([
+ 'foo' => [
+ 'bar' => [
+ 'baz' => 'VALUE_1',
+ 'lol' => 'VALUE_2',
+ ]
+ ]
+ ]);
+
+ $this->assertEquals('VALUE_1', $jsonPath->get('foo.bar.baz'));
+ $this->assertEquals('VALUE_2', $jsonPath->get('foo.bar.lol'));
+ $this->assertNull($jsonPath->get('foo.bar.boo'));
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/JsonSchema/Fixtures/jsonPatchTests.json b/server/vendor/php-opencloud/common/tests/unit/Common/JsonSchema/Fixtures/jsonPatchTests.json
new file mode 100644
index 0000000..e7f3579
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/JsonSchema/Fixtures/jsonPatchTests.json
@@ -0,0 +1,231 @@
+[
+ { "comment": "empty list, empty docs",
+ "doc": {},
+ "patch": [],
+ "expected": {} },
+
+ { "comment": "empty patch list",
+ "doc": {"foo": 1},
+ "patch": [],
+ "expected": {"foo": 1} },
+
+ { "comment": "rearrangements OK?",
+ "doc": {"foo": 1, "bar": 2},
+ "patch": [],
+ "expected": {"bar":2, "foo": 1} },
+
+ { "comment": "rearrangements OK? How about one level down ... array",
+ "doc": [{"foo": 1, "bar": 2}],
+ "patch": [],
+ "expected": [{"bar":2, "foo": 1}] },
+
+ { "comment": "rearrangements OK? How about one level down...",
+ "doc": {"foo":{"foo": 1, "bar": 2}},
+ "patch": [],
+ "expected": {"foo":{"bar":2, "foo": 1}} },
+
+ { "comment": "toplevel array",
+ "doc": [],
+ "patch": [{"op": "add", "path": "/0", "value": "foo"}],
+ "expected": ["foo"] },
+
+ { "comment": "toplevel array, no change",
+ "doc": ["foo"],
+ "patch": [],
+ "expected": ["foo"] },
+
+ { "comment": "toplevel object, numeric string",
+ "doc": {},
+ "patch": [{"op": "add", "path": "/foo", "value": "1"}],
+ "expected": {"foo":"1"} },
+
+ { "comment": "toplevel object, integer",
+ "doc": {},
+ "patch": [{"op": "add", "path": "/foo", "value": 1}],
+ "expected": {"foo":1} },
+
+ { "comment": "Toplevel scalar values OK?",
+ "doc": "foo",
+ "patch": [{"op": "replace", "path": "", "value": "bar"}],
+ "expected": "bar",
+ "disabled": true },
+
+ { "comment": "Add, / target",
+ "doc": {},
+ "patch": [ {"op": "add", "path": "/", "value":1 } ],
+ "expected": {"":1} },
+
+ { "comment": "Add composite value at top level",
+ "doc": {"foo": 1},
+ "patch": [{"op": "add", "path": "/bar", "value": [1, 2]}],
+ "expected": {"foo": 1, "bar": [1, 2]} },
+
+ { "comment": "Add into composite value",
+ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
+ "patch": [{"op": "add", "path": "/baz/0/foo", "value": "world"}],
+ "expected": {"foo": 1, "baz": [{"qux": "hello", "foo": "world"}]} },
+
+ { "doc": {"bar": [1, 2]},
+ "patch": [{"op": "add", "path": "/bar/8", "value": "5"}],
+ "error": "Out of bounds (upper)" },
+
+ { "doc": {"bar": [1, 2]},
+ "patch": [{"op": "add", "path": "/bar/-1", "value": "5"}],
+ "error": "Out of bounds (lower)" },
+
+ { "doc": {"foo": 1},
+ "patch": [{"op": "add", "path": "/bar", "value": true}],
+ "expected": {"foo": 1, "bar": true} },
+
+ { "doc": {"foo": 1},
+ "patch": [{"op": "add", "path": "/bar", "value": false}],
+ "expected": {"foo": 1, "bar": false} },
+
+ { "doc": {"foo": 1},
+ "patch": [{"op": "add", "path": "/bar", "value": null}],
+ "expected": {"foo": 1, "bar": null} },
+
+ { "comment": "0 can be an array index or object element name",
+ "doc": {"foo": 1},
+ "patch": [{"op": "add", "path": "/0", "value": "bar"}],
+ "expected": {"foo": 1, "0": "bar" } },
+
+ { "doc": ["foo"],
+ "patch": [{"op": "add", "path": "/1", "value": "bar"}],
+ "expected": ["foo", "bar"] },
+
+ { "doc": ["foo", "sil"],
+ "patch": [{"op": "add", "path": "/1", "value": "bar"}],
+ "expected": ["foo", "bar", "sil"] },
+
+ { "doc": ["foo", "sil"],
+ "patch": [{"op": "add", "path": "/0", "value": "bar"}],
+ "expected": ["bar", "foo", "sil"] },
+
+ { "comment": "push item to array via last index + 1",
+ "doc": ["foo", "sil"],
+ "patch": [{"op":"add", "path": "/2", "value": "bar"}],
+ "expected": ["foo", "sil", "bar"] },
+
+ { "comment": "add item to array at index > length should fail",
+ "doc": ["foo", "sil"],
+ "patch": [{"op":"add", "path": "/3", "value": "bar"}],
+ "error": "index is greater than number of items in array" },
+
+ { "doc": ["foo", "sil"],
+ "patch": [{"op": "add", "path": "/bar", "value": 42}],
+ "error": "Object operation on array target" },
+
+ { "doc": ["foo", "sil"],
+ "patch": [{"op": "add", "path": "/1", "value": ["bar", "baz"]}],
+ "expected": ["foo", ["bar", "baz"], "sil"],
+ "comment": "value in array add not flattened" },
+
+ { "doc": {"foo": 1, "bar": [1, 2, 3, 4]},
+ "patch": [{"op": "remove", "path": "/bar"}],
+ "expected": {"foo": 1} },
+
+ { "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
+ "patch": [{"op": "remove", "path": "/baz/0/qux"}],
+ "expected": {"foo": 1, "baz": [{}]} },
+
+ { "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
+ "patch": [{"op": "replace", "path": "/foo", "value": [1, 2, 3, 4]}],
+ "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]} },
+
+ { "doc": {"foo": [1, 2, 3, 4], "baz": [{"qux": "hello"}]},
+ "patch": [{"op": "replace", "path": "/baz/0/qux", "value": "world"}],
+ "expected": {"foo": [1, 2, 3, 4], "baz": [{"qux": "world"}]} },
+
+ { "doc": ["foo"],
+ "patch": [{"op": "replace", "path": "/0", "value": "bar"}],
+ "expected": ["bar"] },
+
+ { "doc": [""],
+ "patch": [{"op": "replace", "path": "/0", "value": 0}],
+ "expected": [0] },
+
+ { "doc": [""],
+ "patch": [{"op": "replace", "path": "/0", "value": true}],
+ "expected": [true] },
+
+ { "doc": [""],
+ "patch": [{"op": "replace", "path": "/0", "value": false}],
+ "expected": [false] },
+
+ { "doc": [""],
+ "patch": [{"op": "replace", "path": "/0", "value": null}],
+ "expected": [null] },
+
+ { "doc": ["foo", "sil"],
+ "patch": [{"op": "replace", "path": "/1", "value": ["bar", "baz"]}],
+ "expected": ["foo", ["bar", "baz"]],
+ "comment": "value in array replace not flattened" },
+
+ { "comment": "replace whole document",
+ "disabled": true,
+ "doc": {"foo": "bar"},
+ "patch": [{"op": "replace", "path": "", "value": {"baz": "qux"}}],
+ "expected": {"baz": "qux"} },
+
+ { "doc": {"foo": null},
+ "patch": [{"op": "replace", "path": "/foo", "value": "truthy"}],
+ "expected": {"foo": "truthy"},
+ "comment": "null value should be valid obj property to be replaced with something truthy" },
+
+ { "doc": {"foo": null},
+ "patch": [{"op": "remove", "path": "/foo"}],
+ "expected": {},
+ "comment": "null value should be valid obj property to be removed" },
+
+ { "doc": {"foo": "bar"},
+ "patch": [{"op": "replace", "path": "/foo", "value": null}],
+ "expected": {"foo": null},
+ "comment": "null value should still be valid obj property replace other value" },
+
+ { "comment": "test remove with bad number should fail",
+ "doc": {"foo": 1, "baz": [{"qux": "hello"}]},
+ "patch": [{"op": "remove", "path": "/baz/1e0/qux"}],
+ "error": "remove op shouldn't remove from array with bad number" },
+
+ { "comment": "test remove on array",
+ "doc": [1, 2, 3, 4],
+ "patch": [{"op": "remove", "path": "/0"}],
+ "expected": [2, 3, 4] },
+
+ { "comment": "test repeated removes",
+ "doc": [1, 2, 3, 4],
+ "patch": [{ "op": "remove", "path": "/1" },
+ { "op": "remove", "path": "/3" }],
+ "expected": [1, 3] },
+
+ { "comment": "test remove with bad index should fail",
+ "doc": [1, 2, 3, 4],
+ "patch": [{"op": "remove", "path": "/1e0"}],
+ "error": "remove op shouldn't remove from array with bad number" },
+
+ { "comment": "test replace with bad number should fail",
+ "doc": [""],
+ "patch": [{"op": "replace", "path": "/1e0", "value": false}],
+ "error": "replace op shouldn't replace in array with bad number" },
+
+ { "comment": "test add with bad number should fail",
+ "doc": ["foo", "sil"],
+ "patch": [{"op": "add", "path": "/1e0", "value": "bar"}],
+ "error": "add op shouldn't add to array with bad number" },
+
+ { "comment": "missing 'value' parameter to add",
+ "doc": [ 1 ],
+ "patch": [ { "op": "add", "path": "/-" } ],
+ "error": "missing 'value' parameter" },
+
+ { "comment": "missing 'value' parameter to replace",
+ "doc": [ 1 ],
+ "patch": [ { "op": "replace", "path": "/0" } ],
+ "error": "missing 'value' parameter" },
+
+ { "comment": "unrecognized op should fail",
+ "doc": {"foo": 1},
+ "patch": [{"op": "spam", "path": "/foo", "value": 1}],
+ "error": "Unrecognized op 'spam'" }
+]
\ No newline at end of file
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/JsonSchema/JsonPatchTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/JsonSchema/JsonPatchTest.php
new file mode 100644
index 0000000..ee7db9e
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/JsonSchema/JsonPatchTest.php
@@ -0,0 +1,28 @@
+disabled) || !isset($fixture->expected)) {
+ continue;
+ }
+
+ $actual = JsonPatch::diff($fixture->doc, $fixture->expected);
+
+ $this->assertEquals(
+ json_encode($fixture->patch, JSON_UNESCAPED_SLASHES),
+ json_encode($actual, JSON_UNESCAPED_SLASHES),
+ isset($fixture->comment) ? sprintf("Failed asserting test: %s\n", $fixture->comment) : ''
+ );
+ }
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/JsonSchema/SchemaTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/JsonSchema/SchemaTest.php
new file mode 100644
index 0000000..22663ef
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/JsonSchema/SchemaTest.php
@@ -0,0 +1,102 @@
+body = [
+ 'properties' => [
+ 'foo' => (object)[],
+ 'bar' => (object)[],
+ 'baz' => (object)['readOnly' => true],
+ ],
+ ];
+
+ $this->validator = $this->prophesize(Validator::class);
+ $this->schema = new Schema($this->body, $this->validator->reveal());
+ }
+
+ public function test_it_gets_errors()
+ {
+ $this->validator->getErrors()
+ ->shouldBeCalled()
+ ->willReturn([]);
+
+ $this->assertEquals([], $this->schema->getErrors());
+ }
+
+ public function test_it_gets_error_string()
+ {
+ $this->validator->getErrors()
+ ->shouldBeCalled()
+ ->willReturn([['property' => 'foo', 'message' => 'bar']]);
+
+ $errorMsg = sprintf("Provided values do not validate. Errors:\n[foo] bar\n");
+
+ $this->assertEquals($errorMsg, $this->schema->getErrorString());
+ }
+
+ public function test_it_gets_property_paths()
+ {
+ $this->assertEquals(['/foo', '/bar', '/baz'], $this->schema->getPropertyPaths());
+ }
+
+ public function test_it_ignores_readOnly_attrs()
+ {
+ $expected = (object)[
+ 'foo' => true,
+ 'bar' => false,
+ ];
+
+ $subject = (object)[
+ 'foo' => true,
+ 'bar' => false,
+ 'baz' => true,
+ ];
+
+ $this->assertEquals((object)$expected, $this->schema->normalizeObject((object)$subject, []));
+ }
+
+ public function test_it_stocks_aliases()
+ {
+ $subject = (object)[
+ 'fooAlias' => true,
+ 'bar' => false,
+ 'other' => true,
+ ];
+
+ $expected = (object)[
+ 'foo' => true,
+ 'bar' => false,
+ ];
+
+ $this->assertEquals($expected, $this->schema->normalizeObject($subject, ['foo' => 'fooAlias', 'bar' => 'lol']));
+ }
+
+ public function test_it_validates()
+ {
+ $this->validator->check([], (object) $this->body)->shouldBeCalled();
+
+ $this->schema->validate([]);
+ }
+
+ public function test_it_checks_validity()
+ {
+ $this->validator->isValid()->shouldBeCalled();
+
+ $this->schema->isValid();
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Resource/AbstractResourceTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Resource/AbstractResourceTest.php
new file mode 100644
index 0000000..f334292
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Resource/AbstractResourceTest.php
@@ -0,0 +1,161 @@
+rootFixturesDir = __DIR__;
+ $this->resource = new TestResource($this->client->reveal(), new ComputeV2Api());
+ }
+
+ public function test_it_populates_from_response()
+ {
+ $response = new Response(200, ['Content-Type' => 'application/json'], stream_for(
+ json_encode(['foo' => ['bar' => '1']])
+ ));
+
+ $this->resource->populateFromResponse($response);
+
+ $this->assertEquals('1', $this->resource->bar);
+ }
+
+ public function test_it_populates_datetimes_from_arrays()
+ {
+ $dt = new \DateTimeImmutable('2015');
+
+ $this->resource->populateFromArray(['created' => '2015']);
+
+ $this->assertEquals($this->resource->created, $dt);
+ }
+
+ public function test_it_populates_arrays_from_arrays()
+ {
+ $this->resource->populateFromArray(['children' => [$this->resource, $this->resource]]);
+
+ $this->assertInstanceOf(TestResource::class, $this->resource->children[0]);
+ }
+
+ public function test_it_gets_attrs()
+ {
+ $this->resource->bar = 'foo';
+
+ $this->assertEquals(['bar' => 'foo'], $this->resource->getAttrs(['bar']));
+ }
+
+ public function test_it_executes_with_state()
+ {
+ $this->resource->id = 'foo';
+ $this->resource->bar = 'bar';
+
+ $expectedJson = ['id' => 'foo', 'bar' => 'bar'];
+
+ $this->setupMock('GET', 'foo', $expectedJson, [], new Response(204));
+
+ $this->resource->executeWithState((new ComputeV2Api())->test());
+ }
+
+ public function test_it_executes_operations_until_a_204_is_received()
+ {
+ $this->client
+ ->request('GET', 'servers', ['headers' => []])
+ ->shouldBeCalled()
+ ->willReturn($this->getFixture('servers-page1'));
+
+ $this->client
+ ->request('GET', 'servers', ['query' => ['marker' => '5'], 'headers' => []])
+ ->shouldBeCalled()
+ ->willReturn(new Response(204));
+
+ $count = 0;
+
+ $api = new ComputeV2Api();
+
+ foreach ($this->resource->enumerate($api->getServers()) as $item) {
+ $count++;
+ $this->assertInstanceOf(TestResource::class, $item);
+ }
+
+ $this->assertEquals(5, $count);
+ }
+
+ public function test_it_invokes_function_if_provided()
+ {
+ $this->client
+ ->request('GET', 'servers', ['headers' => []])
+ ->shouldBeCalled()
+ ->willReturn($this->getFixture('servers-page1'));
+
+ $this->client
+ ->request('GET', 'servers', ['query' => ['marker' => '5'], 'headers' => []])
+ ->shouldBeCalled()
+ ->willReturn(new Response(204));
+
+ $api = new ComputeV2Api();
+
+ $count = 0;
+
+ $fn = function () use (&$count) {
+ $count++;
+ };
+
+ foreach ($this->resource->enumerate($api->getServers(), [], $fn) as $item) {
+ }
+
+ $this->assertEquals(5, $count);
+ }
+
+ public function test_it_halts_when_user_provided_limit_is_reached()
+ {
+ $this->client
+ ->request('GET', 'servers', ['query' => ['limit' => 2], 'headers' => []])
+ ->shouldBeCalled()
+ ->willReturn($this->getFixture('servers-page1'));
+
+ $count = 0;
+
+ $api = new ComputeV2Api();
+
+ foreach ($this->resource->enumerate($api->getServers(), ['limit' => 2]) as $item) {
+ $count++;
+ }
+
+ $this->assertEquals(2, $count);
+ }
+}
+
+class TestResource extends AbstractResource
+{
+ protected $resourceKey = 'foo';
+ protected $resourcesKey = 'servers';
+ protected $markerKey = 'id';
+
+ /** @var string */
+ public $bar;
+
+ public $id;
+
+ /** @var \DateTimeImmutable */
+ public $created;
+
+ /** @var []TestResource */
+ public $children;
+
+ public function getAttrs(array $keys)
+ {
+ return parent::getAttrs($keys);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Resource/Fixtures/servers-empty.resp b/server/vendor/php-opencloud/common/tests/unit/Common/Resource/Fixtures/servers-empty.resp
new file mode 100644
index 0000000..655a35b
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Resource/Fixtures/servers-empty.resp
@@ -0,0 +1,6 @@
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+ "servers": []
+}
\ No newline at end of file
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Resource/Fixtures/servers-page1.resp b/server/vendor/php-opencloud/common/tests/unit/Common/Resource/Fixtures/servers-page1.resp
new file mode 100644
index 0000000..adebcaf
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Resource/Fixtures/servers-page1.resp
@@ -0,0 +1,77 @@
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+ "servers": [
+ {
+ "id": "1",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server1"
+ },
+ {
+ "id": "2",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server2"
+ },
+ {
+ "id": "3",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server3"
+ },
+ {
+ "id": "4",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server4"
+ },
+ {
+ "id": "5",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server5"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Resource/Fixtures/servers-page2.resp b/server/vendor/php-opencloud/common/tests/unit/Common/Resource/Fixtures/servers-page2.resp
new file mode 100644
index 0000000..6be6b04
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Resource/Fixtures/servers-page2.resp
@@ -0,0 +1,77 @@
+HTTP/1.1 200 OK
+Content-Type: application/json
+
+{
+ "servers": [
+ {
+ "id": "6",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server6"
+ },
+ {
+ "id": "7",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server7"
+ },
+ {
+ "id": "8",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server8"
+ },
+ {
+ "id": "9",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server9"
+ },
+ {
+ "id": "10",
+ "links": [
+ {
+ "href": "http://openstack.example.com/v2/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "self"
+ },
+ {
+ "href": "http://openstack.example.com/openstack/servers/616fb98f-46ca-475e-917e-2563e5a8cd19",
+ "rel": "bookmark"
+ }
+ ],
+ "name": "server10"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Service/BuilderTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Service/BuilderTest.php
new file mode 100644
index 0000000..09e9918
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Service/BuilderTest.php
@@ -0,0 +1,164 @@
+builder = new Builder([]);
+
+ $this->opts = [
+ 'username' => '1',
+ 'password' => '2',
+ 'tenantId' => '3',
+ 'authUrl' => '4',
+ 'region' => '5',
+ 'catalogName' => '6',
+ 'catalogType' => '7',
+ ];
+ }
+
+ /**
+ * @expectedException \Exception
+ */
+ public function test_it_throws_exception_if_username_is_missing()
+ {
+ $this->builder->createService('Compute', 2, []);
+ }
+
+ /**
+ * @expectedException \Throwable
+ */
+ public function test_it_throws_exception_if_password_is_missing()
+ {
+ $this->builder->createService('Compute', 2, ['username' => 1]);
+ }
+
+ /**
+ * @expectedException \Throwable
+ */
+ public function test_it_throws_exception_if_both_tenantId_and_tenantName_is_missing()
+ {
+ $this->builder->createService('Compute', 2, [
+ 'username' => 1, 'password' => 2, 'authUrl' => 4, 'region' => 5, 'catalogName' => 6, 'catalogType' => 7,
+ ]);
+ }
+
+ /**
+ * @expectedException \Throwable
+ */
+ public function test_it_throws_exception_if_authUrl_is_missing()
+ {
+ $this->builder->createService('Compute', 2, ['username' => 1, 'password' => 2, 'tenantId' => 3]);
+ }
+
+ /**
+ * @expectedException \Throwable
+ */
+ public function test_it_throws_exception_if_region_is_missing()
+ {
+ $this->builder->createService('Compute', 2, [
+ 'username' => 1, 'password' => 2, 'tenantId' => 3, 'authUrl' => 4,
+ ]);
+ }
+
+ /**
+ * @expectedException \Throwable
+ */
+ public function test_it_throws_exception_if_catalogName_is_missing()
+ {
+ $this->builder->createService('Compute', 2, [
+ 'username' => 1, 'password' => 2, 'tenantId' => 3, 'authUrl' => 4,
+ ]);
+ }
+
+ /**
+ * @expectedException \Throwable
+ */
+ public function test_it_throws_exception_if_catalogType_is_missing()
+ {
+ $this->builder->createService('Compute', 2, [
+ 'username' => 1, 'password' => 2, 'tenantId' => 3, 'authUrl' => 4, 'region' => 5, 'catalogName' => 6,
+ ]);
+ }
+
+// public function test_it_builds_services_with_custom_identity_service()
+// {
+// $this->rootFixturesDir = dirname(dirname(__DIR__)) . '/Identity/v2/';
+//
+// $token = $this->prophesize(FakeToken::class)->reveal();
+// $service = $this->prophesize(IdentityService::class);
+// $service->authenticate(Argument::type('array'))->shouldBeCalled()->willReturn([$token, '']);
+//
+// $this->opts += [
+// 'identityService' => $service->reveal(),
+// 'catalogName' => 'nova',
+// 'catalogType' => 'compute',
+// 'region' => 'RegionOne',
+// ];
+//
+// $service = $this->builder->createService('Compute', 2, $this->opts);
+// $this->assertInstanceOf(ComputeV2::class, $service);
+// }
+
+ private function setupHttpClient()
+ {
+ $this->rootFixturesDir = dirname(dirname(__DIR__)) . '/Identity/v3/';
+
+ $response = $this->getFixture('token-get');
+
+ $expectedJson = [
+ 'auth' => [
+ 'identity' => [
+ 'methods' => ['password'],
+ 'password' => ['user' => ['id' => '0ca8f6', 'password' => 'secretsecret']]
+ ]
+ ]
+ ];
+
+ $httpClient = $this->prophesize(ClientInterface::class);
+ $httpClient->request('POST', 'tokens', ['json' => $expectedJson])->shouldBeCalled()->willReturn($response);
+
+ return $httpClient;
+ }
+
+ public function it_builds_services_with_default_identity()
+ {
+ $httpClient = $this->setupHttpClient();
+
+ $options = [
+ 'httpClient' => $httpClient->reveal(),
+ 'catalogName' => 'nova',
+ 'catalogType' => 'compute',
+ 'region' => 'RegionOne',
+ 'user' => [
+ 'id' => '0ca8f6',
+ 'password' => 'secretsecret',
+ ]
+ ];
+
+ $service = $this->builder->createService('Compute', 2, $options);
+ $this->assertInstanceOf(ComputeV2::class, $service);
+ }
+
+// public function test_it_does_not_authenticate_when_creating_identity_services()
+// {
+// $this->assertInstanceOf(IdentityV3::class, $this->builder->createService('Identity', 3, [
+// 'authUrl' => 'foo.com',
+// ]));
+// }
+}
\ No newline at end of file
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Transport/HandlerStackTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/HandlerStackTest.php
new file mode 100644
index 0000000..ec1cf85
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/HandlerStackTest.php
@@ -0,0 +1,15 @@
+assertInstanceOf(HandlerStack::class, HandlerStack::create(new MockHandler()));
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Transport/JsonSerializerTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/JsonSerializerTest.php
new file mode 100644
index 0000000..9075594
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/JsonSerializerTest.php
@@ -0,0 +1,85 @@
+serializer = new JsonSerializer();
+ }
+
+ public function test_it_embeds_params_according_to_path()
+ {
+ $param = $this->prophesize(Parameter::class);
+ $param->isArray()->shouldBeCalled()->willReturn(false);
+ $param->isObject()->shouldBeCalled()->willReturn(false);
+ $param->getName()->shouldBeCalled()->willReturn('username');
+ $param->getPath()->shouldBeCalled()->willReturn('auth.passwordCredentials');
+
+ $userValue = 'fooBar';
+
+ $expected = [
+ 'auth' => [
+ 'passwordCredentials' => [
+ 'username' => $userValue,
+ ],
+ ],
+ ];
+
+ $actual = $this->serializer->stockJson($param->reveal(), $userValue, []);
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function test_it_serializes_arrays()
+ {
+ $param = $this->prophesize(Parameter::class);
+ $param->isArray()->shouldBeCalled()->willReturn(true);
+ $param->getName()->shouldBeCalled()->willReturn('fooBar');
+ $param->getPath()->shouldBeCalled()->willReturn(false);
+
+ $itemSchema = $this->prophesize(Parameter::class);
+ $itemSchema->isArray()->shouldBeCalled()->willReturn(false);
+ $itemSchema->isObject()->shouldBeCalled()->willReturn(false);
+ $itemSchema->getName()->shouldBeCalled()->willReturn(null);
+ $itemSchema->getPath()->shouldBeCalled()->willReturn(null);
+
+ $param->getItemSchema()->shouldBeCalled()->willReturn($itemSchema);
+
+ $userValues = ['1', '2', '3'];
+
+ $expected = ['fooBar' => $userValues];
+
+ $actual = $this->serializer->stockJson($param->reveal(), $userValues, []);
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function test_it_serializes_objects()
+ {
+ $prop = $this->prophesize(Parameter::class);
+ $prop->isArray()->shouldBeCalled()->willReturn(false);
+ $prop->isObject()->shouldBeCalled()->willReturn(false);
+ $prop->getName()->shouldBeCalled()->willReturn('foo');
+ $prop->getPath()->shouldBeCalled()->willReturn(null);
+
+ $param = $this->prophesize(Parameter::class);
+ $param->isArray()->shouldBeCalled()->willReturn(false);
+ $param->isObject()->shouldBeCalled()->willReturn(true);
+ $param->getName()->shouldBeCalled()->willReturn('topLevel');
+ $param->getPath()->shouldBeCalled()->willReturn(false);
+ $param->getProperty('foo')->shouldBeCalled()->willReturn($prop);
+
+ $expected = ['topLevel' => ['foo' => true]];
+
+ $json = $this->serializer->stockJson($param->reveal(), ['foo' => true], []);
+
+ $this->assertEquals($expected, $json);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Transport/MiddlewareTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/MiddlewareTest.php
new file mode 100644
index 0000000..c445783
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/MiddlewareTest.php
@@ -0,0 +1,59 @@
+assertEquals('pending', $promise->getState());
+
+ $promise->wait();
+ $this->assertEquals('rejected', $promise->getState());
+ }
+
+ public function test_responses_are_left_alone_when_status_under_400()
+ {
+ $middleware = Middleware::httpErrors();
+
+ $response = new Response(204);
+ $handler = new MockHandler([$response]);
+ $fn = $middleware($handler);
+
+ $promise = $fn(new Request('GET', 'http://foo.com'), []);
+
+ $promise->then(function ($val) use ($response) {
+ $this->assertEquals($val, $response);
+ });
+
+ $promise->wait();
+ }
+
+ public function test_auth_handler_is_returned()
+ {
+ $generator = function () {};
+
+ $middleware = Middleware::authHandler($generator);
+
+ $handler = new MockHandler([new Response(204)]);
+ $fn = $middleware($handler);
+
+ $this->assertInstanceOf(AuthHandler::class, $fn);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Transport/RequestSerializerTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/RequestSerializerTest.php
new file mode 100644
index 0000000..cf06220
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/RequestSerializerTest.php
@@ -0,0 +1,112 @@
+js = $this->prophesize(JsonSerializer::class);
+
+ $this->rs = new RequestSerializer($this->js->reveal());
+ }
+
+ public function test_it_ignores_undefined_params()
+ {
+ $op = $this->prophesize(Operation::class);
+ $op->getParam('foo')->shouldBeCalled()->willReturn(null);
+
+ $this->assertEquals(['headers' => []], $this->rs->serializeOptions($op->reveal(), ['foo' => 'bar']));
+ }
+
+ public function test_it_serializes_queries()
+ {
+ $sch = $this->prophesize(Parameter::class);
+ $sch->getName()->shouldBeCalled()->willReturn('fooAlias');
+ $sch->getLocation()->shouldBeCalled()->willReturn('query');
+
+ $op = $this->prophesize(Operation::class);
+ $op->getParam('foo')->shouldBeCalled()->willReturn($sch);
+
+ $actual = $this->rs->serializeOptions($op->reveal(), ['foo' => 'bar']);
+ $expected = ['query' => ['fooAlias' => 'bar'], 'headers' => []];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function test_it_serializes_headers()
+ {
+ $sch = $this->prophesize(Parameter::class);
+ $sch->getLocation()->shouldBeCalled()->willReturn('header');
+ $sch->getName()->shouldBeCalled()->willReturn('fooAlias');
+ $sch->getPrefixedName()->shouldBeCalled()->willReturn('prefix-fooAlias');
+
+ $op = $this->prophesize(Operation::class);
+ $op->getParam('foo')->shouldBeCalled()->willReturn($sch);
+
+ $actual = $this->rs->serializeOptions($op->reveal(), ['foo' => 'bar']);
+ $expected = ['headers' => ['prefix-fooAlias' => 'bar']];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function test_it_serializes_metadata_headers()
+ {
+ $itemSch = $this->prophesize(Parameter::class);
+ $itemSch->getName()->shouldBeCalled()->willReturn('foo');
+ $itemSch->getPrefixedName()->shouldBeCalled()->willReturn('prefix-foo');
+
+ $sch = $this->prophesize(Parameter::class);
+ $sch->getItemSchema()->shouldBeCalled()->willReturn($itemSch);
+ $sch->getLocation()->shouldBeCalled()->willReturn('header');
+ $sch->getName()->shouldBeCalled()->willReturn('metadata');
+
+ $op = $this->prophesize(Operation::class);
+ $op->getParam('metadata')->shouldBeCalled()->willReturn($sch);
+
+ $actual = $this->rs->serializeOptions($op->reveal(), ['metadata' => ['foo' => 'bar']]);
+ $expected = ['headers' => ['prefix-foo' => 'bar']];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function test_it_serializes_json()
+ {
+ $sch = $this->prophesize(Parameter::class);
+ $sch->getLocation()->shouldBeCalled()->willReturn('json');
+
+ $op = $this->prophesize(Operation::class);
+ $op->getParam('foo')->shouldBeCalled()->willReturn($sch);
+ $op->getJsonKey()->shouldBeCalled()->willReturn('jsonKey');
+
+ $this->js->stockJson($sch, 'bar', [])->shouldBeCalled()->willReturn(['foo' => 'bar']);
+
+ $actual = $this->rs->serializeOptions($op->reveal(), ['foo' => 'bar']);
+ $expected = ['json' => ['jsonKey' => ['foo' => 'bar']], 'headers' => []];
+
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function test_it_serializes_raw_vals()
+ {
+ $sch = $this->prophesize(Parameter::class);
+ $sch->getLocation()->shouldBeCalled()->willReturn('raw');
+
+ $op = $this->prophesize(Operation::class);
+ $op->getParam('foo')->shouldBeCalled()->willReturn($sch);
+
+ $actual = $this->rs->serializeOptions($op->reveal(), ['foo' => 'bar']);
+ $expected = ['body' => 'bar', 'headers' => []];
+
+ $this->assertEquals($expected, $actual);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Common/Transport/UtilsTest.php b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/UtilsTest.php
new file mode 100644
index 0000000..3103ad4
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Common/Transport/UtilsTest.php
@@ -0,0 +1,30 @@
+assertInstanceOf(Uri::class, $uri);
+ $this->assertEquals(uri_for('http://openstack.org/foo/bar/baz/1/2'), $uri);
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Fixtures/ComputeV2Api.php b/server/vendor/php-opencloud/common/tests/unit/Fixtures/ComputeV2Api.php
new file mode 100644
index 0000000..d395d10
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Fixtures/ComputeV2Api.php
@@ -0,0 +1,107 @@
+ 'string', 'required' => true, 'location' => 'url'];
+
+ public function getImage()
+ {
+ return [
+ 'method' => 'GET',
+ 'path' => 'images/{id}',
+ 'params' => [self::$idParam]
+ ];
+ }
+
+ public function postServer()
+ {
+ return [
+ 'path' => 'servers',
+ 'method' => 'POST',
+ 'jsonKey' => 'server',
+ 'params' => [
+ 'removeMetadata' => [
+ 'type' => 'object',
+ 'properties' => ['type' => 'string'],
+ ],
+ 'securityGroups' => [
+ 'type' => 'array',
+ 'items' => [
+ 'type' => 'object',
+ 'properties' => [
+ 'name' => ['type' => 'string']
+ ]
+ ],
+ 'sentAs' => 'security_groups',
+ ],
+ 'userData' => ['type' => 'string', 'sentAs' => 'user_data'],
+ 'availabilityZone' => ['type' => 'string', 'sentAs' => 'availability_zone'],
+ 'imageId' => ['type' => 'string', 'required' => true, 'sentAs' => 'imageRef'],
+ 'flavorId' => ['type' => 'string', 'required' => true, 'sentAs' => 'flavorRef'],
+ 'networks' => [
+ 'type' => 'array',
+ 'items' => [
+ 'type' => 'object',
+ 'properties' => [
+ 'uuid' => ['type' => 'string'],
+ 'port' => ['type' => 'string'],
+ ]
+ ]
+ ],
+ 'name' => ['type' => 'string', 'required' => true],
+ 'metadata' => ['type' => 'object', 'location' => 'json'],
+ 'personality' => ['type' => 'string'],
+ 'blockDeviceMapping' => [
+ 'type' => 'array',
+ 'sentAs' => 'block_device_mapping_v2',
+ 'items' => [
+ 'type' => 'object',
+ 'properties' => [
+ 'configDrive' => ['type' => 'string', 'sentAs' => 'config_drive'],
+ 'bootIndex' => ['type' => 'string', 'sentAs' => 'boot_index'],
+ 'deleteOnTermination' => ['type' => 'boolean', 'sentAs' => 'delete_on_termination'],
+ 'guestFormat' => ['type' => 'string', 'sentAs' => 'guest_format'],
+ 'destinationType' => ['type' => 'string', 'sentAs' => 'destination_type'],
+ 'sourceType' => ['type' => 'string', 'sentAs' => 'source_type'],
+ 'deviceName' => ['type' => 'string', 'sentAs' => 'device_name'],
+ ]
+ ],
+ ],
+ ]
+ ];
+ }
+
+ public function test()
+ {
+ return [
+ 'method' => 'GET',
+ 'path' => 'foo',
+ 'params' => [
+ 'id' => ['type' => 'string', 'location' => 'json'],
+ 'bar' => ['type' => 'string', 'location' => 'json'],
+ ]
+ ];
+ }
+
+ public function getServers()
+ {
+ return [
+ 'method' => 'GET',
+ 'path' => 'servers',
+ 'params' => [
+ 'changesSince' => ['sentAs' => 'changes-since', 'type' => 'string', 'location' => 'query'],
+ 'imageId' => ['sentAs' => 'image', 'type' => 'string', 'location' => 'query'],
+ 'flavorId' => ['sentAs' => 'flavor', 'type' => 'string', 'location' => 'query'],
+ 'name' => ['type' => 'string', 'location' => 'query'],
+ 'marker' => ['type' => 'string', 'location' => 'query'],
+ 'limit' => ['type' => 'integer', 'location' => 'query'],
+ 'status' => ['type' => 'string', 'location' => 'query'],
+ 'host' => ['type' => 'string', 'location' => 'query']
+ ],
+ ];
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Fixtures/IdentityV2Api.php b/server/vendor/php-opencloud/common/tests/unit/Fixtures/IdentityV2Api.php
new file mode 100644
index 0000000..4cc66b5
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Fixtures/IdentityV2Api.php
@@ -0,0 +1,36 @@
+ 'POST',
+ 'path' => 'tokens',
+ 'params' => [
+ 'username' => [
+ 'type' => 'string',
+ 'required' => true,
+ 'path' => 'auth.passwordCredentials'
+ ],
+ 'password' => [
+ 'type' => 'string',
+ 'required' => true,
+ 'path' => 'auth.passwordCredentials'
+ ],
+ 'tenantId' => [
+ 'type' => 'string',
+ 'path' => 'auth',
+ ],
+ 'tenantName' => [
+ 'type' => 'string',
+ 'path' => 'auth',
+ ]
+ ],
+ ];
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/Fixtures/IdentityV3Api.php b/server/vendor/php-opencloud/common/tests/unit/Fixtures/IdentityV3Api.php
new file mode 100644
index 0000000..2fc922f
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/Fixtures/IdentityV3Api.php
@@ -0,0 +1,75 @@
+ 'object',
+ 'params' => [
+ 'id' => ['type' => 'string'],
+ 'name' => ['type' => 'string']
+ ]
+ ];
+ }
+
+ private function projectParam()
+ {
+ return [
+ 'type' => 'object',
+ 'params' => [
+ 'id' => ['type' => 'string'],
+ 'name' => ['type' => 'string'],
+ 'domain' => $this->domainParam(),
+ ]
+ ];
+ }
+
+ public function postTokens()
+ {
+ return [
+ 'method' => 'POST',
+ 'path' => 'tokens',
+ 'params' => [
+ 'methods' => [
+ 'type' => 'array',
+ 'path' => 'auth.identity',
+ 'items' => [
+ 'type' => 'string'
+ ]
+ ],
+ 'user' => [
+ 'path' => 'auth.identity.password',
+ 'type' => 'object',
+ 'properties' => [
+ 'id' => [
+ 'type' => 'string',
+ ],
+ 'name' => [
+ 'type' => 'string',
+ ],
+ 'password' => [
+ 'type' => 'string',
+ ],
+ 'domain' => $this->domainParam()
+ ]
+ ],
+ 'tokenId' => [
+ 'type' => 'string',
+ 'path' => 'auth.identity.token',
+ 'sentAs' => 'id',
+ ],
+ 'scope' => [
+ 'type' => 'object',
+ 'path' => 'auth',
+ 'properties' => [
+ 'project' => $this->projectParam(),
+ 'domain' => $this->domainParam()
+ ]
+ ]
+ ]
+ ];
+ }
+}
diff --git a/server/vendor/php-opencloud/common/tests/unit/TestCase.php b/server/vendor/php-opencloud/common/tests/unit/TestCase.php
new file mode 100644
index 0000000..610d88a
--- /dev/null
+++ b/server/vendor/php-opencloud/common/tests/unit/TestCase.php
@@ -0,0 +1,99 @@
+client = $this->prophesize(ClientInterface::class);
+ }
+
+ protected function createResponse($status, array $headers, array $json)
+ {
+ return new Response($status, $headers, stream_for(json_encode($json)));
+ }
+
+ protected function getFixture($file)
+ {
+ if (!$this->rootFixturesDir) {
+ throw new \RuntimeException('Root fixtures dir not set');
+ }
+
+ $path = $this->rootFixturesDir . '/Fixtures/' . $file . '.resp';
+
+ if (!file_exists($path)) {
+ throw new \RuntimeException(sprintf("%s does not exist", $path));
+ }
+
+ return parse_response(file_get_contents($path));
+ }
+
+ protected function setupMock($method, $path, $body = null, array $headers = [], $response)
+ {
+ $options = ['headers' => $headers];
+
+ if (!empty($body)) {
+ $options[is_array($body) ? 'json' : 'body'] = $body;
+ }
+
+ if (is_string($response)) {
+ $response = $this->getFixture($response);
+ }
+
+ $this->client
+ ->request($method, $path, $options)
+ ->shouldBeCalled()
+ ->willReturn($response);
+ }
+
+ protected function createFn($receiver, $method, $args)
+ {
+ return function () use ($receiver, $method, $args) {
+ return $receiver->$method($args);
+ };
+ }
+
+ protected function listTest(callable $call, $urlPath, $modelName = null, $responseFile = null)
+ {
+ $modelName = $modelName ?: $urlPath;
+ $responseFile = $responseFile ?: $urlPath;
+
+ $this->setupMock('GET', $urlPath, null, [], $responseFile);
+
+ $resources = call_user_func($call);
+
+ $this->assertInstanceOf('\Generator', $resources);
+
+ $count = 0;
+
+ foreach ($resources as $resource) {
+ $this->assertInstanceOf('OpenStack\Identity\v3\Models\\' . ucfirst($modelName), $resource);
+ ++$count;
+ }
+
+ $this->assertEquals(2, $count);
+ }
+
+ protected function getTest(callable $call, $modelName)
+ {
+ $resource = call_user_func($call);
+
+ $this->assertInstanceOf('OpenStack\Identity\v3\Models\\' . ucfirst($modelName), $resource);
+ $this->assertEquals('id', $resource->id);
+ }
+}
diff --git a/server/vendor/php-opencloud/openstack b/server/vendor/php-opencloud/openstack
index 1419eb2..f2ee770 160000
--- a/server/vendor/php-opencloud/openstack
+++ b/server/vendor/php-opencloud/openstack
@@ -1 +1 @@
-Subproject commit 1419eb2e01164bb6b8b837df37724423907352d7
+Subproject commit f2ee77024843659d970817a9e7055bb40a3724f9