From af1df27fb34bb89ced7c2fda22025feb2b07079a Mon Sep 17 00:00:00 2001 From: fmizzell Date: Fri, 9 Mar 2018 16:53:52 -0600 Subject: [PATCH] =?UTF-8?q?=20Allows=20the=20services=20API=20to=20be=20ab?= =?UTF-8?q?le=20to=20consume=20datasets=20with=20the=20same=E2=80=A6=20(#2?= =?UTF-8?q?409)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allows the services API to be able to consume datasets with the same format given when retrieving a dataset through the API. * Temporarily disabling unrelated failing tests. * Updating unittests due to changes in California's open data porta. --- .../dkan_dataset_rest_api.module | 185 ++++++ test/composer.json | 16 +- test/composer.lock | 571 ++++++++++-------- .../DKANExtension/Context/ServicesContext.php | 389 ++++-------- test/features/data_story.author.feature | 12 +- test/features/dataset.admin.feature | 36 +- test/features/dataset_rest_api.feature | 12 +- test/features/datastore.feature | Bin 5237 -> 5365 bytes test/features/theme.admin.feature | 2 +- test/phpunit/dkan_dataset/ApiTest.php | 58 ++ test/phpunit/phpunit.xml | 1 + 11 files changed, 731 insertions(+), 551 deletions(-) create mode 100644 test/phpunit/dkan_dataset/ApiTest.php diff --git a/modules/dkan/dkan_dataset/modules/dkan_dataset_rest_api/dkan_dataset_rest_api.module b/modules/dkan/dkan_dataset/modules/dkan_dataset_rest_api/dkan_dataset_rest_api.module index a65627c10b..4d1ac31e70 100644 --- a/modules/dkan/dkan_dataset/modules/dkan_dataset_rest_api/dkan_dataset_rest_api.module +++ b/modules/dkan/dkan_dataset/modules/dkan_dataset_rest_api/dkan_dataset_rest_api.module @@ -35,3 +35,188 @@ function dkan_dataset_rest_api_services_request_postprocess_alter($controller, $ } } } + +/** + * Implements hook_services_resources_alter(). + * + * Out of the box, the services module handles nodes, but they use the Form API + * to enforce data validation. This is a reasonable approach given that lots + * of validation is attached to the forms used to input/update data, but it + * also brings complications due the the structure differences between forms + * and the objects accepted by the Entity API. We are overriding the node + * handling to allow Entity API acceptable structures when working with + * services while still keeping some of the validation for the data. + */ +function dkan_dataset_rest_api_services_resources_alter(&$resources, $endpoint) { + if (isset($resources['node']['operations']['create'])) { + $resources['node']['operations']['create']['callback'] = + "_dkan_dataset_rest_api_resource_create"; + } + + if (isset($resources['node']['operations']['update'])) { + $resources['node']['operations']['update']['callback'] = + "_dkan_dataset_rest_api_resource_update"; + } +} + +/** + * Services operation callback: node creation. + */ +function _dkan_dataset_rest_api_resource_create($request) { + $node_array = _services_arg_value($request, 'node'); + $node = (object) $node_array; + + if (isset($node->type)) { + if ($node->type == "dataset" || $node->type == "resource") { + + try { + field_attach_validate('node', $node); + _dkan_dataset_rest_api_field_validation($node); + } + catch (\Exception $e) { + return services_error($e->getMessage()); + } + + node_save($node); + + $nid = $node->nid; + $response = array('nid' => $nid); + if ($uri = services_resource_uri(array('node', $nid))) { + $response['uri'] = $uri; + } + } + else { + $response = _node_resource_create($request); + } + } + else { + return services_error("Type not set for node"); + } + + return $response; +} + +/** + * Services operation callback: node update. + */ +function _dkan_dataset_rest_api_resource_update($nid, $request) { + $node = node_load($nid); + + if (isset($node->type)) { + if ($node->type == "dataset" || $node->type == "resource") { + + $node_array = _services_arg_value($request, 'node'); + + if (!empty($node_array['type']) && $node_array['type'] != $node->type) { + // Node types cannot be changed once they are created. + return services_error(t('Node type cannot be changed'), 406); + } + + foreach ($node_array as $field => $value) { + $node->{$field} = $value; + } + + try { + field_attach_validate('node', $node); + _dkan_dataset_rest_api_field_validation($node); + } + catch (\Exception $e) { + return services_error($e->getMessage()); + } + + if (!isset($node->revision)) { + $node->revision = 1; + } + + node_save($node); + + $nid = $node->nid; + $response = array('nid' => $nid); + if ($uri = services_resource_uri(array('node', $nid))) { + $response['uri'] = $uri; + } + return $response; + } + else { + return _node_resource_update($nid, $request); + } + } + else { + return services_error("Type not set for node"); + } +} + +/** + * Basic validation of fields. + * + * Currently we are only checking for required fields, and that the + * values in an options/select list field are valid. + */ +function _dkan_dataset_rest_api_field_validation($node) { + $instances = field_info_instances('node', $node->type); + + foreach ($instances as $field_name => $info) { + if ($info['required'] == 1 && empty($node->{$field_name})) { + throw new Exception("Field {$field_name} is required"); + } + + if (!empty($node->{$field_name})) { + $options = _dkan_dataset_rest_api_field_validation_get_options($node, $field_name, $info); + + // Check that the values match the options. + if (!empty($options) && is_array($options)) { + _dkan_dataset_rest_api_field_validation_check_field_values_against_options($field_name, $node->{$field_name}, $options); + } + } + } +} + +/** + * Get options for a field. + */ +function _dkan_dataset_rest_api_field_validation_get_options($node, $field_name, $field_instance) { + $options = []; + + // Look for predefined options for this field. + if (!empty($field_instance['widget']['settings']['available_options'])) { + $string = $field_instance['widget']['settings']['available_options']; + $lines = explode(PHP_EOL, $string); + foreach ($lines as $line) { + $pieces = explode("|", $line); + $options[trim($pieces[0])] = trim($pieces[1]); + } + } + else { + $field = field_info_field($field_name); + $properties = [ + 'strip_tags' => FALSE, + 'strip_tags_and_unescape' => FALSE, + 'filter_xss' => FALSE, + 'empty_option' => FALSE, + 'optgroups' => FALSE + ]; + + $options = _options_get_options($field, $field_instance, $properties, 'node', $node); + } + + return $options; +} + +/** + * Check field values against field options. + */ +function _dkan_dataset_rest_api_field_validation_check_field_values_against_options($field_name, $field_values, $options) { + $keys = array_keys($options); + + foreach ($field_values as $language => $info) { + foreach ($info as $delta => $info2) { + foreach ($info2 as $property => $value) { + if (substr_count($property, "value") > 0) { + if (!in_array($value, $keys)) { + throw new \Exception("Invalid option for field {$field_name}"); + } + } + } + } + } +} diff --git a/test/composer.json b/test/composer.json index 02ce6c590b..e867a6df99 100644 --- a/test/composer.json +++ b/test/composer.json @@ -1,19 +1,27 @@ { + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/GetDKAN/dkan_api_client.git" + } + ], "require": { - "phpunit/phpunit": " 5.7.*", + "phpunit/phpunit": "5.7.*", "behat/behat": "3.1.*@dev", "devinci/devinci-behat-extension": "dev-master", "myplanetdigital/function_mock": "dev-master", "drupal/drupal-driver": "^1.2", "drupal/drupal-extension": "~3.0", - "squizlabs/php_codesniffer": "*", - "drupal/coder": "~8.2" + "squizlabs/php_codesniffer": "2.7.0", + "drupal/coder": "8.2.10", + "guzzlehttp/guzzle": "^6.3", + "dkan/dkan_api_client": "1.0.0" }, "autoload": { "psr-0": { "Drupal\\DKANExtension\\": "dkanextension/src/" }, - "drupal/coder": "~8.2" + "drupal/coder": "8.2.10" }, "config": { "bin-dir": "bin/" diff --git a/test/composer.lock b/test/composer.lock index 0d40d18510..b97686b03a 100644 --- a/test/composer.lock +++ b/test/composer.lock @@ -4,8 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "a4f9fc4b2b541dfa8108402ecba002fc", - "content-hash": "53f2fed63843d623f643a653ae3e0450", + "content-hash": "917c7eb3f81c0274b3fa9034faa7bcee", "packages": [ { "name": "behat/behat", @@ -85,20 +84,20 @@ "symfony", "testing" ], - "time": "2016-03-28 07:04:45" + "time": "2016-03-28T07:04:45+00:00" }, { "name": "behat/gherkin", - "version": "v4.4.5", + "version": "v4.5.1", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74" + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/5c14cff4f955b17d20d088dec1bde61c0539ec74", - "reference": "5c14cff4f955b17d20d088dec1bde61c0539ec74", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", + "reference": "74ac03d52c5e23ad8abd5c5cce4ab0e8dc1b530a", "shasum": "" }, "require": { @@ -144,7 +143,7 @@ "gherkin", "parser" ], - "time": "2016-10-30 11:50:56" + "time": "2017-08-30T11:04:43+00:00" }, { "name": "behat/mink", @@ -202,7 +201,7 @@ "testing", "web" ], - "time": "2016-03-05 08:26:18" + "time": "2016-03-05T08:26:18+00:00" }, { "name": "behat/mink-browserkit-driver", @@ -258,31 +257,31 @@ "browser", "testing" ], - "time": "2016-03-05 08:59:47" + "time": "2016-03-05T08:59:47+00:00" }, { "name": "behat/mink-extension", - "version": "v2.2", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/Behat/MinkExtension.git", - "reference": "5b4bda64ff456104564317e212c823e45cad9d59" + "reference": "badc565b7a1d05c4a4bf49c789045bcf7af6c6de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/MinkExtension/zipball/5b4bda64ff456104564317e212c823e45cad9d59", - "reference": "5b4bda64ff456104564317e212c823e45cad9d59", + "url": "https://api.github.com/repos/Behat/MinkExtension/zipball/badc565b7a1d05c4a4bf49c789045bcf7af6c6de", + "reference": "badc565b7a1d05c4a4bf49c789045bcf7af6c6de", "shasum": "" }, "require": { - "behat/behat": "~3.0,>=3.0.5", - "behat/mink": "~1.5", + "behat/behat": "^3.0.5", + "behat/mink": "^1.5", "php": ">=5.3.2", - "symfony/config": "~2.2|~3.0" + "symfony/config": "^2.7|^3.0|^4.0" }, "require-dev": { - "behat/mink-goutte-driver": "~1.1", - "phpspec/phpspec": "~2.0" + "behat/mink-goutte-driver": "^1.1", + "phpspec/phpspec": "^2.0" }, "type": "behat-extension", "extra": { @@ -317,7 +316,7 @@ "test", "web" ], - "time": "2016-02-15 07:55:18" + "time": "2017-11-24T19:30:49+00:00" }, { "name": "behat/mink-goutte-driver", @@ -372,7 +371,7 @@ "headless", "testing" ], - "time": "2016-03-05 09:04:22" + "time": "2016-03-05T09:04:22+00:00" }, { "name": "behat/mink-selenium2-driver", @@ -433,29 +432,33 @@ "testing", "webdriver" ], - "time": "2016-03-05 09:10:18" + "time": "2016-03-05T09:10:18+00:00" }, { "name": "behat/transliterator", - "version": "v1.1.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/Behat/Transliterator.git", - "reference": "868e05be3a9f25ba6424c2dd4849567f50715003" + "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Transliterator/zipball/868e05be3a9f25ba6424c2dd4849567f50715003", - "reference": "868e05be3a9f25ba6424c2dd4849567f50715003", + "url": "https://api.github.com/repos/Behat/Transliterator/zipball/826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", + "reference": "826ce7e9c2a6664c0d1f381cbb38b1fb80a7ee2c", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "require-dev": { + "chuyskywalker/rolling-curl": "^3.1", + "php-yaoi/php-yaoi": "^1.0" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -473,7 +476,7 @@ "slug", "transliterator" ], - "time": "2015-09-28 16:26:35" + "time": "2017-04-04T11:38:05+00:00" }, { "name": "devinci/devinci-behat-extension", @@ -517,7 +520,47 @@ "Behat", "test" ], - "time": "2016-06-21 17:28:28" + "time": "2016-06-21T17:28:28+00:00" + }, + { + "name": "dkan/dkan_api_client", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/GetDKAN/dkan_api_client.git", + "reference": "009b7e023b21f6bdbf43960beacce22c90f876b9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GetDKAN/dkan_api_client/zipball/009b7e023b21f6bdbf43960beacce22c90f876b9", + "reference": "009b7e023b21f6bdbf43960beacce22c90f876b9", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.3", + "php": ">=5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "DKAN\\": "src/" + } + }, + "license": [ + "GPLv2" + ], + "authors": [ + { + "name": "fmizzell", + "email": "fmizzell@1312210.no-reply.drupal.org" + } + ], + "description": "A PHP client to interact with the DKAN services API.", + "support": { + "source": "https://github.com/GetDKAN/dkan_api_client/tree/1.0.0", + "issues": "https://github.com/GetDKAN/dkan_api_client/issues" + }, + "time": "2018-02-04T18:35:16+00:00" }, { "name": "doctrine/instantiator", @@ -571,32 +614,26 @@ "constructor", "instantiate" ], - "time": "2015-06-14 21:17:01" + "time": "2015-06-14T21:17:01+00:00" }, { "name": "drupal/coder", - "version": "8.2.12", + "version": "8.2.10", "source": { "type": "git", - "url": "https://github.com/klausi/coder.git", - "reference": "984c54a7b1e8f27ff1c32348df69712afd86b17f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/klausi/coder/zipball/984c54a7b1e8f27ff1c32348df69712afd86b17f", - "reference": "984c54a7b1e8f27ff1c32348df69712afd86b17f", - "shasum": "" + "url": "https://git.drupal.org/project/coder.git", + "reference": "c835ff5c1733676fe0d3f3b861e814d570baaa6f" }, "require": { "ext-mbstring": "*", "php": ">=5.4.0", - "squizlabs/php_codesniffer": ">=2.8.1 <3.0", + "squizlabs/php_codesniffer": ">=2.7.0 <3.0", "symfony/yaml": ">=2.0.0" }, "require-dev": { - "phpunit/phpunit": ">=3.7 <6" + "phpunit/phpunit": ">=3.7" }, - "type": "phpcodesniffer-standard", + "type": "library", "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-2.0+" @@ -608,20 +645,20 @@ "phpcs", "standards" ], - "time": "2017-03-18 10:28:49" + "time": "2016-12-09T21:57:53+00:00" }, { "name": "drupal/drupal-driver", - "version": "v1.2.1", + "version": "v1.3.2", "source": { "type": "git", "url": "https://github.com/jhedstrom/DrupalDriver.git", - "reference": "125d39918c97f7a08e3110d456a0a1db864dae46" + "reference": "aa1f32b207939dfc0c96919be47b952d3c1b900b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jhedstrom/DrupalDriver/zipball/125d39918c97f7a08e3110d456a0a1db864dae46", - "reference": "125d39918c97f7a08e3110d456a0a1db864dae46", + "url": "https://api.github.com/repos/jhedstrom/DrupalDriver/zipball/aa1f32b207939dfc0c96919be47b952d3c1b900b", + "reference": "aa1f32b207939dfc0c96919be47b952d3c1b900b", "shasum": "" }, "require": { @@ -631,6 +668,7 @@ "require-dev": { "drupal/coder": "~8.2.0", "drush-ops/behat-drush-endpoint": "*", + "jakub-onderka/php-parallel-lint": "^0.9.2", "mockery/mockery": "0.9.4", "phpspec/phpspec": "~2.0", "phpunit/phpunit": "~4.0" @@ -665,20 +703,20 @@ "test", "web" ], - "time": "2016-06-20 16:29:51" + "time": "2017-11-28T21:51:43+00:00" }, { "name": "drupal/drupal-extension", - "version": "v3.2.2", + "version": "v3.2.3", "source": { "type": "git", "url": "https://github.com/jhedstrom/drupalextension.git", - "reference": "abe3a33abd94382ab62423dd972fa820c63962e3" + "reference": "0ab44c8dda608b9885a53640882914a18992b6b6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jhedstrom/drupalextension/zipball/abe3a33abd94382ab62423dd972fa820c63962e3", - "reference": "abe3a33abd94382ab62423dd972fa820c63962e3", + "url": "https://api.github.com/repos/jhedstrom/drupalextension/zipball/0ab44c8dda608b9885a53640882914a18992b6b6", + "reference": "0ab44c8dda608b9885a53640882914a18992b6b6", "shasum": "" }, "require": { @@ -726,28 +764,31 @@ "test", "web" ], - "time": "2016-06-30 21:12:18" + "time": "2017-06-08T18:32:33+00:00" }, { "name": "fabpot/goutte", - "version": "v3.2.1", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/Goutte.git", - "reference": "db5c28f4a010b4161d507d5304e28a7ebf211638" + "reference": "395f61d7c2e15a813839769553a4de16fa3b3c96" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/db5c28f4a010b4161d507d5304e28a7ebf211638", - "reference": "db5c28f4a010b4161d507d5304e28a7ebf211638", + "url": "https://api.github.com/repos/FriendsOfPHP/Goutte/zipball/395f61d7c2e15a813839769553a4de16fa3b3c96", + "reference": "395f61d7c2e15a813839769553a4de16fa3b3c96", "shasum": "" }, "require": { "guzzlehttp/guzzle": "^6.0", "php": ">=5.5.0", - "symfony/browser-kit": "~2.1|~3.0", - "symfony/css-selector": "~2.1|~3.0", - "symfony/dom-crawler": "~2.1|~3.0" + "symfony/browser-kit": "~2.1|~3.0|~4.0", + "symfony/css-selector": "~2.1|~3.0|~4.0", + "symfony/dom-crawler": "~2.1|~3.0|~4.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^3.3 || ^4" }, "type": "application", "extra": { @@ -758,7 +799,10 @@ "autoload": { "psr-4": { "Goutte\\": "Goutte" - } + }, + "exclude-from-classmap": [ + "Goutte/Tests" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -775,20 +819,20 @@ "keywords": [ "scraper" ], - "time": "2017-01-03 13:21:43" + "time": "2017-11-19T08:45:40+00:00" }, { "name": "guzzlehttp/guzzle", - "version": "6.2.3", + "version": "6.3.0", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006" + "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/8d6c6cc55186db87b7dc5009827429ba4e9dc006", - "reference": "8d6c6cc55186db87b7dc5009827429ba4e9dc006", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699", + "reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699", "shasum": "" }, "require": { @@ -798,9 +842,12 @@ }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "^4.0", + "phpunit/phpunit": "^4.0 || ^5.0", "psr/log": "^1.0" }, + "suggest": { + "psr/log": "Required for using the Log middleware" + }, "type": "library", "extra": { "branch-alias": { @@ -837,7 +884,7 @@ "rest", "web service" ], - "time": "2017-02-28 22:50:30" + "time": "2017-06-22T18:50:49+00:00" }, { "name": "guzzlehttp/promises", @@ -888,7 +935,7 @@ "keywords": [ "promise" ], - "time": "2016-12-20 10:07:11" + "time": "2016-12-20T10:07:11+00:00" }, { "name": "guzzlehttp/psr7", @@ -953,20 +1000,20 @@ "uri", "url" ], - "time": "2017-03-20 17:10:46" + "time": "2017-03-20T17:10:46+00:00" }, { "name": "instaclick/php-webdriver", - "version": "1.4.3", + "version": "1.4.5", "source": { "type": "git", "url": "https://github.com/instaclick/php-webdriver.git", - "reference": "0c20707dcf30a32728fd6bdeeab996c887fdb2fb" + "reference": "6fa959452e774dcaed543faad3a9d1a37d803327" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/instaclick/php-webdriver/zipball/0c20707dcf30a32728fd6bdeeab996c887fdb2fb", - "reference": "0c20707dcf30a32728fd6bdeeab996c887fdb2fb", + "url": "https://api.github.com/repos/instaclick/php-webdriver/zipball/6fa959452e774dcaed543faad3a9d1a37d803327", + "reference": "6fa959452e774dcaed543faad3a9d1a37d803327", "shasum": "" }, "require": { @@ -974,7 +1021,8 @@ "php": ">=5.3.2" }, "require-dev": { - "satooshi/php-coveralls": "dev-master" + "phpunit/phpunit": "^4.8", + "satooshi/php-coveralls": "^1.0||^2.0" }, "type": "library", "extra": { @@ -1000,7 +1048,7 @@ { "name": "Anthon Pang", "email": "apang@softwaredevelopment.ca", - "role": "Fork maintainer" + "role": "Fork Maintainer" } ], "description": "PHP WebDriver for Selenium 2", @@ -1011,41 +1059,44 @@ "webdriver", "webtest" ], - "time": "2015-06-15 20:19:33" + "time": "2017-06-30T04:02:48+00:00" }, { "name": "myclabs/deep-copy", - "version": "1.6.1", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102" + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/8e6e04167378abf1ddb4d3522d8755c5fd90d102", - "reference": "8e6e04167378abf1ddb4d3522d8755c5fd90d102", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", + "reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e", "shasum": "" }, "require": { - "php": ">=5.4.0" + "php": "^5.6 || ^7.0" }, "require-dev": { - "doctrine/collections": "1.*", - "phpunit/phpunit": "~4.1" + "doctrine/collections": "^1.0", + "doctrine/common": "^2.6", + "phpunit/phpunit": "^4.1" }, "type": "library", "autoload": { "psr-4": { "DeepCopy\\": "src/DeepCopy/" - } + }, + "files": [ + "src/DeepCopy/deep_copy.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "description": "Create deep copies (clones) of your objects", - "homepage": "https://github.com/myclabs/DeepCopy", "keywords": [ "clone", "copy", @@ -1053,7 +1104,7 @@ "object", "object graph" ], - "time": "2017-04-12 18:52:22" + "time": "2017-10-19T19:58:43+00:00" }, { "name": "myplanetdigital/function_mock", @@ -1093,20 +1144,20 @@ "description": "Framework that helps mocking functions for unit testing PHP scripts", "homepage": "https://github.com/myplanetdigital/function_mock", "abandoned": true, - "time": "2014-03-07 18:56:45" + "time": "2014-03-07T18:56:45+00:00" }, { "name": "phpdocumentor/reflection-common", - "version": "1.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c" + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/144c307535e82c8fdcaacbcfc1d6d8eeb896687c", - "reference": "144c307535e82c8fdcaacbcfc1d6d8eeb896687c", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", + "reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6", "shasum": "" }, "require": { @@ -1147,26 +1198,26 @@ "reflection", "static analysis" ], - "time": "2015-12-27 11:43:31" + "time": "2017-09-11T18:02:19+00:00" }, { "name": "phpdocumentor/reflection-docblock", - "version": "3.1.1", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e" + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/8331b5efe816ae05461b7ca1e721c01b46bafb3e", - "reference": "8331b5efe816ae05461b7ca1e721c01b46bafb3e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2", + "reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2", "shasum": "" }, "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^1.0@dev", - "phpdocumentor/type-resolver": "^0.2.0", + "php": "^5.6 || ^7.0", + "phpdocumentor/reflection-common": "^1.0.0", + "phpdocumentor/type-resolver": "^0.4.0", "webmozart/assert": "^1.0" }, "require-dev": { @@ -1192,24 +1243,24 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2016-09-30 07:12:33" + "time": "2017-11-10T14:09:06+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.2.1", + "version": "0.4.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb" + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", - "reference": "e224fb2ea2fba6d3ad6fdaef91cd09a172155ccb", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", + "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", "shasum": "" }, "require": { - "php": ">=5.5", + "php": "^5.5 || ^7.0", "phpdocumentor/reflection-common": "^1.0" }, "require-dev": { @@ -1239,37 +1290,37 @@ "email": "me@mikevanriel.com" } ], - "time": "2016-11-25 06:54:22" + "time": "2017-07-14T14:27:02+00:00" }, { "name": "phpspec/prophecy", - "version": "v1.7.0", + "version": "1.7.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "93d39f1f7f9326d746203c7c056f300f7f126073" + "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/93d39f1f7f9326d746203c7c056f300f7f126073", - "reference": "93d39f1f7f9326d746203c7c056f300f7f126073", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", + "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", "sebastian/comparator": "^1.1|^2.0", "sebastian/recursion-context": "^1.0|^2.0|^3.0" }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8 || ^5.6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6.x-dev" + "dev-master": "1.7.x-dev" } }, "autoload": { @@ -1302,7 +1353,7 @@ "spy", "stub" ], - "time": "2017-03-02 20:05:34" + "time": "2017-11-24T13:59:53+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1365,20 +1416,20 @@ "testing", "xunit" ], - "time": "2017-04-02 07:44:40" + "time": "2017-04-02T07:44:40+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "1.4.2", + "version": "1.4.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5" + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/3cc8f69b3028d0f96a9078e6295d86e9bf019be5", - "reference": "3cc8f69b3028d0f96a9078e6295d86e9bf019be5", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/730b01bc3e867237eaac355e06a36b85dd93a8b4", + "reference": "730b01bc3e867237eaac355e06a36b85dd93a8b4", "shasum": "" }, "require": { @@ -1412,7 +1463,7 @@ "filesystem", "iterator" ], - "time": "2016-10-03 07:40:28" + "time": "2017-11-27T13:52:08+00:00" }, { "name": "phpunit/php-text-template", @@ -1453,7 +1504,7 @@ "keywords": [ "template" ], - "time": "2015-06-21 13:50:34" + "time": "2015-06-21T13:50:34+00:00" }, { "name": "phpunit/php-timer", @@ -1502,20 +1553,20 @@ "keywords": [ "timer" ], - "time": "2017-02-26 11:10:40" + "time": "2017-02-26T11:10:40+00:00" }, { "name": "phpunit/php-token-stream", - "version": "1.4.11", + "version": "1.4.12", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-token-stream.git", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7" + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e03f8f67534427a787e21a385a67ec3ca6978ea7", - "reference": "e03f8f67534427a787e21a385a67ec3ca6978ea7", + "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16", + "reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16", "shasum": "" }, "require": { @@ -1551,20 +1602,20 @@ "keywords": [ "tokenizer" ], - "time": "2017-02-27 10:12:30" + "time": "2017-12-04T08:55:13+00:00" }, { "name": "phpunit/phpunit", - "version": "5.7.20", + "version": "5.7.27", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "3cb94a5f8c07a03c8b7527ed7468a2926203f58b" + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/3cb94a5f8c07a03c8b7527ed7468a2926203f58b", - "reference": "3cb94a5f8c07a03c8b7527ed7468a2926203f58b", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", "shasum": "" }, "require": { @@ -1588,8 +1639,8 @@ "sebastian/global-state": "^1.1", "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0.3|~2.0", - "symfony/yaml": "~2.1|~3.0" + "sebastian/version": "^1.0.6|^2.0.1", + "symfony/yaml": "~2.1|~3.0|~4.0" }, "conflict": { "phpdocumentor/reflection-docblock": "3.0.2" @@ -1633,20 +1684,20 @@ "testing", "xunit" ], - "time": "2017-05-22 07:42:55" + "time": "2018-02-01T05:50:59+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "3.4.3", + "version": "3.4.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24" + "reference": "a23b761686d50a560cc56233b9ecf49597cc9118" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", - "reference": "3ab72b65b39b491e0c011e2e09bb2206c2aa8e24", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/a23b761686d50a560cc56233b9ecf49597cc9118", + "reference": "a23b761686d50a560cc56233b9ecf49597cc9118", "shasum": "" }, "require": { @@ -1692,7 +1743,7 @@ "mock", "xunit" ], - "time": "2016-12-08 20:27:08" + "time": "2017-06-30T09:13:00+00:00" }, { "name": "psr/http-message", @@ -1742,7 +1793,7 @@ "request", "response" ], - "time": "2016-08-06 14:39:51" + "time": "2016-08-06T14:39:51+00:00" }, { "name": "psr/log", @@ -1789,7 +1840,7 @@ "psr", "psr-3" ], - "time": "2016-10-10 12:19:37" + "time": "2016-10-10T12:19:37+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1834,7 +1885,7 @@ ], "description": "Looks up which function or method a line of code belongs to", "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "time": "2017-03-04 06:30:41" + "time": "2017-03-04T06:30:41+00:00" }, { "name": "sebastian/comparator", @@ -1898,7 +1949,7 @@ "compare", "equality" ], - "time": "2017-01-29 09:50:25" + "time": "2017-01-29T09:50:25+00:00" }, { "name": "sebastian/diff", @@ -1950,7 +2001,7 @@ "keywords": [ "diff" ], - "time": "2017-05-22 07:24:03" + "time": "2017-05-22T07:24:03+00:00" }, { "name": "sebastian/environment", @@ -2000,7 +2051,7 @@ "environment", "hhvm" ], - "time": "2016-11-26 07:53:53" + "time": "2016-11-26T07:53:53+00:00" }, { "name": "sebastian/exporter", @@ -2067,7 +2118,7 @@ "export", "exporter" ], - "time": "2016-11-19 08:54:04" + "time": "2016-11-19T08:54:04+00:00" }, { "name": "sebastian/global-state", @@ -2118,7 +2169,7 @@ "keywords": [ "global state" ], - "time": "2015-10-12 03:26:01" + "time": "2015-10-12T03:26:01+00:00" }, { "name": "sebastian/object-enumerator", @@ -2164,7 +2215,7 @@ ], "description": "Traverses array structures and object graphs to enumerate all referenced objects", "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "time": "2017-02-18 15:18:39" + "time": "2017-02-18T15:18:39+00:00" }, { "name": "sebastian/recursion-context", @@ -2217,7 +2268,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2016-11-19 07:33:16" + "time": "2016-11-19T07:33:16+00:00" }, { "name": "sebastian/resource-operations", @@ -2259,7 +2310,7 @@ ], "description": "Provides a list of PHP built-in functions that operate on resources", "homepage": "https://www.github.com/sebastianbergmann/resource-operations", - "time": "2015-07-28 20:34:47" + "time": "2015-07-28T20:34:47+00:00" }, { "name": "sebastian/version", @@ -2302,20 +2353,20 @@ ], "description": "Library that helps with managing the version number of Git-hosted PHP projects", "homepage": "https://github.com/sebastianbergmann/version", - "time": "2016-10-03 07:35:21" + "time": "2016-10-03T07:35:21+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "2.8.1", + "version": "2.7.0", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d" + "reference": "571e27b6348e5b3a637b2abc82ac0d01e6d7bbed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", - "reference": "d7cf0d894e8aa4c73712ee4a331cc1eaa37cdc7d", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/571e27b6348e5b3a637b2abc82ac0d01e6d7bbed", + "reference": "571e27b6348e5b3a637b2abc82ac0d01e6d7bbed", "shasum": "" }, "require": { @@ -2380,29 +2431,29 @@ "phpcs", "standards" ], - "time": "2017-03-01 22:17:45" + "time": "2016-09-01T23:53:02+00:00" }, { "name": "symfony/browser-kit", - "version": "v3.2.6", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "2fe0caa60c1a1dfeefd0425741182687a9b382b8" + "reference": "490f27762705c8489bd042fe3e9377a191dba9b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/2fe0caa60c1a1dfeefd0425741182687a9b382b8", - "reference": "2fe0caa60c1a1dfeefd0425741182687a9b382b8", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/490f27762705c8489bd042fe3e9377a191dba9b4", + "reference": "490f27762705c8489bd042fe3e9377a191dba9b4", "shasum": "" }, "require": { - "php": ">=5.5.9", - "symfony/dom-crawler": "~2.8|~3.0" + "php": "^5.5.9|>=7.0.8", + "symfony/dom-crawler": "~2.8|~3.0|~4.0" }, "require-dev": { - "symfony/css-selector": "~2.8|~3.0", - "symfony/process": "~2.8|~3.0" + "symfony/css-selector": "~2.8|~3.0|~4.0", + "symfony/process": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/process": "" @@ -2410,7 +2461,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2437,27 +2488,27 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2017-02-21 09:12:04" + "time": "2018-01-03T07:37:34+00:00" }, { "name": "symfony/class-loader", - "version": "v3.2.6", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/class-loader.git", - "reference": "c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9" + "reference": "e63c12699822bb3b667e7216ba07fbcc3a3e203e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/class-loader/zipball/c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9", - "reference": "c29a5bc6ca14cfff1f5e3d7781ed74b6e898d2b9", + "url": "https://api.github.com/repos/symfony/class-loader/zipball/e63c12699822bb3b667e7216ba07fbcc3a3e203e", + "reference": "e63c12699822bb3b667e7216ba07fbcc3a3e203e", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { - "symfony/finder": "~2.8|~3.0", + "symfony/finder": "~2.8|~3.0|~4.0", "symfony/polyfill-apcu": "~1.1" }, "suggest": { @@ -2466,7 +2517,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2493,20 +2544,20 @@ ], "description": "Symfony ClassLoader Component", "homepage": "https://symfony.com", - "time": "2017-02-18 17:28:00" + "time": "2018-01-03T07:37:34+00:00" }, { "name": "symfony/config", - "version": "v3.2.6", + "version": "v3.2.14", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "741d6d4cd1414d67d48eb71aba6072b46ba740c2" + "reference": "e5533fcc0b3dd377626153b2852707878f363728" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/741d6d4cd1414d67d48eb71aba6072b46ba740c2", - "reference": "741d6d4cd1414d67d48eb71aba6072b46ba740c2", + "url": "https://api.github.com/repos/symfony/config/zipball/e5533fcc0b3dd377626153b2852707878f363728", + "reference": "e5533fcc0b3dd377626153b2852707878f363728", "shasum": "" }, "require": { @@ -2549,20 +2600,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2017-03-01 18:18:25" + "time": "2017-04-12T14:13:17+00:00" }, { "name": "symfony/console", - "version": "v3.2.6", + "version": "v3.2.14", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "28fb243a2b5727774ca309ec2d92da240f1af0dd" + "reference": "eced439413608647aeff243038a33ea246b2b33a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/28fb243a2b5727774ca309ec2d92da240f1af0dd", - "reference": "28fb243a2b5727774ca309ec2d92da240f1af0dd", + "url": "https://api.github.com/repos/symfony/console/zipball/eced439413608647aeff243038a33ea246b2b33a", + "reference": "eced439413608647aeff243038a33ea246b2b33a", "shasum": "" }, "require": { @@ -2612,29 +2663,29 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-03-06 19:30:27" + "time": "2017-07-29T21:27:41+00:00" }, { "name": "symfony/css-selector", - "version": "v3.2.6", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "a48f13dc83c168f1253a5d2a5a4fb46c36244c4c" + "reference": "e66394bc7610e69279bfdb3ab11b4fe65403f556" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/a48f13dc83c168f1253a5d2a5a4fb46c36244c4c", - "reference": "a48f13dc83c168f1253a5d2a5a4fb46c36244c4c", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/e66394bc7610e69279bfdb3ab11b4fe65403f556", + "reference": "e66394bc7610e69279bfdb3ab11b4fe65403f556", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2665,37 +2716,36 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2017-02-21 09:12:04" + "time": "2018-01-03T07:37:34+00:00" }, { "name": "symfony/debug", - "version": "v3.2.6", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "b90c9f91ad8ac37d9f114e369042d3226b34dc1a" + "reference": "53f6af2805daf52a43b393b93d2f24925d35c937" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/b90c9f91ad8ac37d9f114e369042d3226b34dc1a", - "reference": "b90c9f91ad8ac37d9f114e369042d3226b34dc1a", + "url": "https://api.github.com/repos/symfony/debug/zipball/53f6af2805daf52a43b393b93d2f24925d35c937", + "reference": "53f6af2805daf52a43b393b93d2f24925d35c937", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "psr/log": "~1.0" }, "conflict": { "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2" }, "require-dev": { - "symfony/class-loader": "~2.8|~3.0", - "symfony/http-kernel": "~2.8|~3.0" + "symfony/http-kernel": "~2.8|~3.0|~4.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2722,20 +2772,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2017-02-18 17:28:00" + "time": "2018-01-18T22:16:57+00:00" }, { "name": "symfony/dependency-injection", - "version": "v2.8.18", + "version": "v2.8.34", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "efdbeefa454a41154fd99a6f0489f0e9cb46c497" + "reference": "91ad61e6f140b050eba4aa39bc52eece713f2a71" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/efdbeefa454a41154fd99a6f0489f0e9cb46c497", - "reference": "efdbeefa454a41154fd99a6f0489f0e9cb46c497", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/91ad61e6f140b050eba4aa39bc52eece713f2a71", + "reference": "91ad61e6f140b050eba4aa39bc52eece713f2a71", "shasum": "" }, "require": { @@ -2785,28 +2835,28 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2017-02-28 12:31:05" + "time": "2018-01-29T08:55:23+00:00" }, { "name": "symfony/dom-crawler", - "version": "v3.2.6", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "403944e294cf4ceb3b8447f54cbad88ea7b99cee" + "reference": "09bd97b844b3151fab82f2fdd62db9c464b3910a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/403944e294cf4ceb3b8447f54cbad88ea7b99cee", - "reference": "403944e294cf4ceb3b8447f54cbad88ea7b99cee", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/09bd97b844b3151fab82f2fdd62db9c464b3910a", + "reference": "09bd97b844b3151fab82f2fdd62db9c464b3910a", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { - "symfony/css-selector": "~2.8|~3.0" + "symfony/css-selector": "~2.8|~3.0|~4.0" }, "suggest": { "symfony/css-selector": "" @@ -2814,7 +2864,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2841,20 +2891,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2017-02-21 09:12:04" + "time": "2018-01-03T07:37:34+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v2.8.18", + "version": "v2.8.34", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "bb4ec47e8e109c1c1172145732d0aa468d967cd0" + "reference": "d64be24fc1eba62f9daace8a8918f797fc8e87cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/bb4ec47e8e109c1c1172145732d0aa468d967cd0", - "reference": "bb4ec47e8e109c1c1172145732d0aa468d967cd0", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/d64be24fc1eba62f9daace8a8918f797fc8e87cc", + "reference": "d64be24fc1eba62f9daace8a8918f797fc8e87cc", "shasum": "" }, "require": { @@ -2901,29 +2951,29 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2017-02-21 08:33:48" + "time": "2018-01-03T07:36:31+00:00" }, { "name": "symfony/filesystem", - "version": "v3.2.6", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "bc0f17bed914df2cceb989972c3b996043c4da4a" + "reference": "e078773ad6354af38169faf31c21df0f18ace03d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/bc0f17bed914df2cceb989972c3b996043c4da4a", - "reference": "bc0f17bed914df2cceb989972c3b996043c4da4a", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/e078773ad6354af38169faf31c21df0f18ace03d", + "reference": "e078773ad6354af38169faf31c21df0f18ace03d", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -2950,20 +3000,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2017-03-06 19:30:27" + "time": "2018-01-03T07:37:34+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.3.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4" + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/e79d363049d1c2128f133a2667e4f4190904f7f4", - "reference": "e79d363049d1c2128f133a2667e4f4190904f7f4", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", "shasum": "" }, "require": { @@ -2975,7 +3025,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -3009,29 +3059,29 @@ "portable", "shim" ], - "time": "2016-11-14 01:06:16" + "time": "2018-01-30T19:27:44+00:00" }, { "name": "symfony/process", - "version": "v3.2.6", + "version": "v3.4.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "68bfa8c83f24c0ac04ea7193bcdcda4519f41892" + "reference": "09a5172057be8fc677840e591b17f385e58c7c0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/68bfa8c83f24c0ac04ea7193bcdcda4519f41892", - "reference": "68bfa8c83f24c0ac04ea7193bcdcda4519f41892", + "url": "https://api.github.com/repos/symfony/process/zipball/09a5172057be8fc677840e591b17f385e58c7c0d", + "reference": "09a5172057be8fc677840e591b17f385e58c7c0d", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.4-dev" } }, "autoload": { @@ -3058,34 +3108,35 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2017-03-04 12:23:14" + "time": "2018-01-29T09:03:43+00:00" }, { "name": "symfony/translation", - "version": "v3.2.6", + "version": "v3.3.16", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "0e1b15ce8fbf3890f4ccdac430ed5e07fdfe0690" + "reference": "90cb5ca3eb84b3053fef876e11e405fd819487fc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/0e1b15ce8fbf3890f4ccdac430ed5e07fdfe0690", - "reference": "0e1b15ce8fbf3890f4ccdac430ed5e07fdfe0690", + "url": "https://api.github.com/repos/symfony/translation/zipball/90cb5ca3eb84b3053fef876e11e405fd819487fc", + "reference": "90cb5ca3eb84b3053fef876e11e405fd819487fc", "shasum": "" }, "require": { - "php": ">=5.5.9", + "php": "^5.5.9|>=7.0.8", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { - "symfony/config": "<2.8" + "symfony/config": "<2.8", + "symfony/yaml": "<3.3" }, "require-dev": { "psr/log": "~1.0", "symfony/config": "~2.8|~3.0", "symfony/intl": "^2.8.18|^3.2.5", - "symfony/yaml": "~2.8|~3.0" + "symfony/yaml": "~3.3" }, "suggest": { "psr/log": "To use logging capability in translator", @@ -3095,7 +3146,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } }, "autoload": { @@ -3122,24 +3173,24 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2017-03-04 12:23:14" + "time": "2018-01-18T14:19:00+00:00" }, { "name": "symfony/yaml", - "version": "v3.2.8", + "version": "v3.3.16", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6" + "reference": "af615970e265543a26ee712c958404eb9b7ac93d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/acec26fcf7f3031e094e910b94b002fa53d4e4d6", - "reference": "acec26fcf7f3031e094e910b94b002fa53d4e4d6", + "url": "https://api.github.com/repos/symfony/yaml/zipball/af615970e265543a26ee712c958404eb9b7ac93d", + "reference": "af615970e265543a26ee712c958404eb9b7ac93d", "shasum": "" }, "require": { - "php": ">=5.5.9" + "php": "^5.5.9|>=7.0.8" }, "require-dev": { "symfony/console": "~2.8|~3.0" @@ -3150,7 +3201,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.2-dev" + "dev-master": "3.3-dev" } }, "autoload": { @@ -3177,20 +3228,20 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2017-05-01 14:55:58" + "time": "2018-01-20T15:04:53+00:00" }, { "name": "webmozart/assert", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", "shasum": "" }, "require": { @@ -3227,7 +3278,7 @@ "check", "validate" ], - "time": "2016-11-23 20:04:58" + "time": "2018-01-29T19:49:41+00:00" } ], "packages-dev": [], diff --git a/test/dkanextension/src/Drupal/DKANExtension/Context/ServicesContext.php b/test/dkanextension/src/Drupal/DKANExtension/Context/ServicesContext.php index f06d108e60..3494774941 100644 --- a/test/dkanextension/src/Drupal/DKANExtension/Context/ServicesContext.php +++ b/test/dkanextension/src/Drupal/DKANExtension/Context/ServicesContext.php @@ -3,6 +3,7 @@ namespace Drupal\DKANExtension\Context; use Behat\Behat\Hook\Scope\BeforeScenarioScope; +use GuzzleHttp\Client as GuzzleClient; use Symfony\Component\Config\Definition\Exception\Exception; /** @@ -10,10 +11,11 @@ */ class ServicesContext extends RawDKANContext { private $base_url = ''; - private $cookie_session = ''; private $csrf_token = ''; private $endpoints = array(); + private $client; + // Each node field should be formatted properly before the information is sent on a request. // This is a map from 'Field name' -> 'Field format'. /** @@ -23,6 +25,7 @@ class ServicesContext extends RawDKANContext { public function __construct($request_fields_map = array()) { $this->request_fields_map = $request_fields_map['request_fields_map']; + $this->client = new GuzzleClient(['cookies' => true]); } /** @@ -58,27 +61,49 @@ public function endpoints($data) { public function iUseTheEndpointToLoginWithUserAndPass($endpoint, $username, $password) { // Build request URL. $request_url = $this->base_url . $this->getEndpointPath($endpoint) . '/user/login'; - // Get cookie_session and csrf_token. - $user_login = $this->services_request_user_login($request_url, $username, $password); - $this->cookie_session = $user_login['cookie_session']; - $this->csrf_token = $this->services_request_get_csrf($this->cookie_session, $user_login['curl'], $this->base_url); + + $data = array( + 'username' => $username, + 'password' => $password, + ); + + $response = $this->client->request("POST", $request_url, ['json' => $data]); + + if ($response->getStatusCode() == 200){ + $body = json_decode($response->getBody()->getContents()); + $this->csrf_token = $body->token; + } + else { + throw new \Exception("Unable to login"); + } } /** * @Given I use the :arg1 endpoint to create the nodes: */ - public function iUseTheEndpointToCreateTheNodes($endpoint, $nodes) { + public function iUseTheEndpointToCreateTheNodes($endpoint, $data) { $request_url = $this->base_url . $this->getEndpointPath($endpoint) . '/node'; // Create nodes. - foreach ($nodes->getHash() as $node_data) { - // Get node data. - $processed_data = $this->build_node_data($node_data); - // Create node. - $response = $this->services_request_create_node($processed_data, $this->csrf_token, $this->cookie_session, $request_url); - // Keep track of all created node. - $node = node_load($response->nid); - $wrapper = entity_metadata_wrapper('node', $node); - $this->dkanContext->entityStore->store('node', $processed_data['type'], $node->nid, $wrapper, $wrapper->label()); + foreach ($data->getHash() as $node_data) { + $node = $this->getNodeFromData($node_data); + + $response = $this->client->request("POST", $request_url, + [ + 'headers' => ['X-CSRF-Token' => $this->csrf_token], + 'json' => $node + ] + ); + + if ($response->getStatusCode() == '200') { + $body = json_decode($response->getBody()->getContents()); + // Keep track of all created node. + $node = node_load($body->nid); + $wrapper = entity_metadata_wrapper('node', $node); + $this->dkanContext->entityStore->store('node', $node->type, $node->nid, $wrapper, $wrapper->label()); + } + else { + throw new \Exception("Node could not be created"); + } } return TRUE; } @@ -95,23 +120,48 @@ public function iUseTheEndpointToAttachTheFileTo($endpoint, $file_name, $node_na if (!is_file($file_path)) { throw new Exception(sprintf('The file %s could not be found', $file_name)); } + + $file = curl_file_create($file_path); + // Prepare file data. $file_data = array( - "files[1]" => curl_file_create($file_path), - "field_name" => "field_upload", - // 0 -> replace 1 -> append. - "attach" => 0, + [ + 'name' => 'files[1]', + 'contents' => fopen($file->name, 'r') + ], + [ + 'name' => "field_name", + 'contents' => "field_upload" + ], + [ + 'name' => "attach", + 'contents' => 0 + ] ); - // Build request URL. + $request_url = $this->base_url . $this->getEndpointPath($endpoint) . '/node/' . $node->getIdentifier() . '/attach_file'; - // Attach file. - $this->services_request_attach_file($file_data, $this->csrf_token, $this->cookie_session, $request_url); + + $response = $this->client->request("POST", $request_url, [ + 'headers' => [ + 'X-CSRF-Token' => $this->csrf_token, + ], + 'multipart' => $file_data, + ]); + + if ($response->getStatusCode() == '200') { + return TRUE; + } + else { + throw new \Exception(sprintf('Error: %s', $response['response'])); + } } else { throw new Exception(sprintf('The resource could not be found.')); } + } - return TRUE; + private function printBody(\GuzzleHttp\Psr7\Response $response) { + print_r((array) json_decode($response->getBody()->getContents())); } /** @@ -121,21 +171,31 @@ public function iUseTheEndpointToUpdateTheNodeWith($endpoint, $node_name, $data) // Get node. $node = $this->dkanContext->entityStore->retrieve_by_name($node_name); if ($node) { - // Update nodes. foreach ($data->getHash() as $node_data) { - // Get node data. - $processed_data = $this->build_node_data($node_data, $node); - // Build request URL. + $request_url = $this->base_url . $this->getEndpointPath($endpoint) . '/node/' . $node->getIdentifier(); - // Update node. - $this->services_request_update_node($processed_data, $this->csrf_token, $this->cookie_session, $request_url); + + $node = $this->getNodeFromData($node_data); + $response = $this->client->request("PUT", $request_url, [ + 'headers' => [ + 'X-CSRF-Token' => $this->csrf_token, + ], + 'json' => $node, + ]); + + if ($response->getStatusCode() == '200') { + return TRUE; + } + else { + throw new \Exception(sprintf('Error: %s', $response['response'])); + } + break; } } else { throw new Exception(sprintf('The node could not be found.')); } return TRUE; - } /** @@ -145,221 +205,23 @@ public function iUseTheEndpointToDeleteTheNode($endpoint, $node_name) { // Get node. $node = $this->dkanContext->entityStore->retrieve_by_name($node_name); if ($node) { - // Build request URL. $request_url = $this->base_url . $this->getEndpointPath($endpoint) . '/node/' . $node->getIdentifier(); - // Delete node. - $this->services_request_delete_node($this->csrf_token, $this->cookie_session, $request_url); - } - else { - throw new Exception(sprintf('The node could not be found.')); - } - return TRUE; - } - - /** - * Get path based on endpoint name. - */ - private function getEndpointPath($endpoint_name) { - if (isset($this->endpoints[$endpoint_name])) { - return $this->endpoints[$endpoint_name]; - } - else { - throw new Exception(sprintf('The %s endpoint could not be found.', $endpoint_name)); - } - } - - /** - * Init CURL object. - */ - private function services_request_curl_init($request_url, $csrf_token = FALSE) { - // cURL. - $curl = curl_init($request_url); - if ($csrf_token) { - curl_setopt($curl, CURLOPT_HTTPHEADER, array( - 'Accept: application/json', - 'X-CSRF-Token: ' . $csrf_token, - )); - } - else { - // Accept JSON response. - curl_setopt($curl, CURLOPT_HTTPHEADER, array('Accept: application/json')); - } - // Ask to not return Header. - curl_setopt($curl, CURLOPT_HEADER, FALSE); - curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE); - curl_setopt($curl, CURLOPT_FAILONERROR, TRUE); - return $curl; - } - - /** - * Execute CURL request and process response. - */ - private function services_request_curl_parse($curl) { - $response = array(); - - $result = curl_exec($curl); - $http_code = curl_getinfo($curl, CURLINFO_HTTP_CODE); - - $response['http_code'] = $http_code; - - if ($http_code == 200) { - $response['success'] = TRUE; - $response['response'] = json_decode($result); - } - else { - $response['success'] = FALSE; - $response['response'] = curl_error($curl); - } - - return $response; - } - - /** - * Logs in user. - */ - private function services_request_user_login($request_url, $username, $password) { - // User data. - $user_data = array( - 'username' => $username, - 'password' => $password, - ); - $user_data = http_build_query($user_data); - - $curl = $this->services_request_curl_init($request_url); - // Do a regular HTTP POST. - curl_setopt($curl, CURLOPT_POST, 1); - // Set POST data. - curl_setopt($curl, CURLOPT_POSTFIELDS, $user_data); - - $response = $this->services_request_curl_parse($curl); - - if ($response['success']) { - // Define cookie session. - $cookie_session = $response['response']->session_name . '=' . $response['response']->sessid; - return array('cookie_session' => $cookie_session, 'curl' => $curl); - } - else { - throw new \Exception(sprintf('Error: %s', $response['response'])); - } - } - - /** - * Retrives CSRF token. - */ - private function services_request_get_csrf($cookie_session, $curl, $base_url) { - // GET CSRF TOKEN. - curl_setopt_array($curl, array( - CURLOPT_RETURNTRANSFER => 1, - CURLOPT_URL => $base_url . '/services/session/token', - )); - curl_setopt($curl, CURLOPT_COOKIE, "$cookie_session"); - - $ret = new \stdClass(); - - $ret->response = curl_exec($curl); - $ret->error = curl_error($curl); - $ret->info = curl_getinfo($curl); - - $csrf_token = $ret->response; - return $csrf_token; - } - - /** - * Create node. - */ - private function services_request_create_node($node_data, $csrf_token, $cookie_session, $request_url) { - $node_data = http_build_query($node_data); - - $curl = $this->services_request_curl_init($request_url, $csrf_token); - // Do a regular HTTP POST. - curl_setopt($curl, CURLOPT_POST, 1); - // Set POST data. - curl_setopt($curl, CURLOPT_POSTFIELDS, $node_data); - // Use the previously saved session. - curl_setopt($curl, CURLOPT_COOKIE, "$cookie_session"); - - $response = $this->services_request_curl_parse($curl); - - if ($response['success']) { - return $response['response']; - } - else { - throw new \Exception(sprintf('Error: %s', $response['response'])); - } - } - - /** - * Update node. - */ - private function services_request_update_node($node_data, $csrf_token, $cookie_session, $request_url) { - - $node_data = http_build_query($node_data); - - $curl = $this->services_request_curl_init($request_url, $csrf_token); - // Do a regular HTTP POST. - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); - // Set POST data. - curl_setopt($curl, CURLOPT_POSTFIELDS, $node_data); - // Use the previously saved session. - curl_setopt($curl, CURLOPT_COOKIE, "$cookie_session"); - - $response = $this->services_request_curl_parse($curl); - - if ($response['success']) { - return $response['response']; - } - else { - throw new \Exception(sprintf('Error: %s', $response['response'])); - } - } - /** - * Attach file to node. - */ - private function services_request_attach_file($file_data, $csrf_token, $cookie_session, $request_url) { - - $curl = $this->services_request_curl_init($request_url, $csrf_token); - // Add 'Content-Type: multipart/form-data' on header. - curl_setopt($curl, CURLOPT_HTTPHEADER, array( - 'Content-Type: multipart/form-data', - 'Accept: application/json', - 'X-CSRF-Token: ' . $csrf_token, - )); - // Do a regular HTTP POST. - curl_setopt($curl, CURLOPT_POST, 1); - // Set POST data. - curl_setopt($curl, CURLOPT_POSTFIELDS, $file_data); - // Use the previously saved session. - curl_setopt($curl, CURLOPT_COOKIE, "$cookie_session"); - - $response = $this->services_request_curl_parse($curl); - - if ($response['success']) { - return $response['response']; - } - else { - throw new \Exception(sprintf('Error: %s', $response['response'])); - } - } - - /** - * Delete node. - */ - private function services_request_delete_node($csrf_token, $cookie_session, $request_url) { + $response = $this->client->request("DELETE", $request_url, [ + 'headers' => [ + 'X-CSRF-Token' => $this->csrf_token, + ] + ]); - $curl = $this->services_request_curl_init($request_url, $csrf_token); - // Set POST data. - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - // Use the previously saved session. - curl_setopt($curl, CURLOPT_COOKIE, "$cookie_session"); - - $response = $this->services_request_curl_parse($curl); - - if ($response['success']) { - return $response['response']; + if ($response->getStatusCode() == '200') { + return TRUE; + } + else { + throw new \Exception(sprintf('Error: %s', $response['response'])); + } } else { - throw new \Exception(sprintf('Error: %s', $response['response'])); + throw new Exception(sprintf('The node could not be found.')); } } @@ -368,32 +230,25 @@ private function services_request_delete_node($csrf_token, $cookie_session, $req */ public function build_node_data($data, $node = NULL) { $node_data = array(); - if (!$node && !isset($data['type'])) { throw new Exception(sprintf('The "type" column is required.')); } - // Get node type. $node_type = ($node) ? $node->getBundle() : $data['type']; - // Get the rest api field map for the content type. $rest_api_fields = $this->request_fields_map[$node_type]; - if ($node_type == "dataset") { $this->datasetContext->applyMissingRequiredFields($data); } - foreach ($data as $field => $field_value) { if (isset($rest_api_fields[$field])) { $node_data[$rest_api_fields[$field]] = $this->process_field($field, $field_value); } } - // If the node is being updated then the type of node should not be modified. if ($node && isset($node_data['type'])) { unset($node_data['type']); } - return $node_data; } @@ -408,13 +263,11 @@ private function process_field($field, $field_value) { $field_value = $field_value['value']; } break; - case 'publisher': case 'groups': if (is_array($field_value)) { $field_value = $field_value[0]->nid; } - if (!is_numeric($field_value)) { $field_value = (int) db_select('node', 'n') ->fields('n', array('nid')) @@ -423,39 +276,65 @@ private function process_field($field, $field_value) { ->execute() ->fetchField(); } - if (is_array($field_value)) { $field_value = $field_value[0]->nid; } break; - case 'tags': if (is_array($field_value)) { $field_value = $field_value[0]->name; } break; - case 'program code': if (is_array($field_value)) { $field_value = $field_value[0]; } break; - case 'resource': $resource = $this->dkanContext->entityStore->retrieve_by_name($field_value); if ($resource) { $field_value = $resource->entityKey('title') . ' (' . $resource->getIdentifier() . ')'; } break; - case "attest date": if (is_numeric($field_value)) { $field_value = date('m/d/Y', (int) $field_value); } break; } - return $field_value; } + /** + * Get path based on endpoint name. + */ + private function getEndpointPath($endpoint_name) { + if (isset($this->endpoints[$endpoint_name])) { + return $this->endpoints[$endpoint_name]; + } + else { + throw new Exception(sprintf('The %s endpoint could not be found.', $endpoint_name)); + } + } + + private function getNodeFromData($data) + { + $properties = ['title', 'status', 'type']; + $node = []; + + foreach ($data as $key => $value) { + if (!in_array($key, $properties)) { + $node[$key] = [ + 'und' => [ + 0 => ['value' => $value] + ] + ]; + } else { + $node[$key] = $value; + } + } + + return $node; + } + } diff --git a/test/features/data_story.author.feature b/test/features/data_story.author.feature index 54b3e34757..6ccadc7fac 100644 --- a/test/features/data_story.author.feature +++ b/test/features/data_story.author.feature @@ -5,11 +5,11 @@ Feature: Data Stories Background: Given users: | name | mail | roles | - | Jaz | jaz@example.com | content creator | + | Jaz001 | jaz@example.com | content creator | And "dkan_data_story" content: | title | author | status | - | DKAN Data Story Test Story Post | Jaz | 1 | - | Unpublished Test Story Post | Jaz | 1 | + | DKAN Data Story Test Story Post | Jaz001 | 1 | + | Unpublished Test Story Post | Jaz001 | 1 | @customizable Scenario: Menu Item @@ -17,7 +17,7 @@ Feature: Data Stories Then I should see "Stories" Scenario: Create Story Content - And I am logged in as "Jaz" + And I am logged in as "Jaz001" When I am on "/node/add/dkan-data-story" And I fill in "edit-title" with "Test Post" And I fill in "body[und][0][value]" with "Test description" @@ -26,7 +26,7 @@ Feature: Data Stories Then I should not see "panels-ipe-region" Scenario: Edit own story content - And I am logged in as "Jaz" + And I am logged in as "Jaz001" When I am on "story/dkan-data-story-test-story-post" And I click "Edit" And I fill in "body[und][0][value]" with "Test description Update" @@ -44,7 +44,7 @@ Feature: Data Stories # Then I should see "Education" Scenario: Delete own story content - And I am logged in as "Jaz" + And I am logged in as "Jaz001" When I am on "story/dkan-data-story-test-story-post" And I click "Edit" And I press "Delete" diff --git a/test/features/dataset.admin.feature b/test/features/dataset.admin.feature index eb4ccd80d6..1811c2cd53 100644 --- a/test/features/dataset.admin.feature +++ b/test/features/dataset.admin.feature @@ -11,18 +11,18 @@ Feature: Dataset Features Background: Given users: | name | mail | roles | - | John | john@example.com | site manager | - | Badmin | admin@example.com | site manager | - | Gabriel | gabriel@example.com | editor | - | Katie | katie@example.com | content creator | + | John002 | john@example.com | site manager | + | Badmin002 | admin@example.com | site manager | + | Gabriel002 | gabriel@example.com | editor | + | Katie002 | katie@example.com | content creator | Given groups: | title | author | published | - | Group 01 | Badmin | Yes | - | Group 02 | Badmin | Yes | + | Group 01 | Badmin002 | Yes | + | Group 02 | Badmin002 | Yes | And group memberships: | user | group | role on group | membership status | - | Gabriel | Group 01 | administrator member | Active | - | Katie | Group 01 | member | Active | + | Gabriel002 | Group 01 | administrator member | Active | + | Katie002 | Group 01 | member | Active | | Admin | Group 02 | administrator member | Active | And "Tags" terms: | name | @@ -30,21 +30,21 @@ Feature: Dataset Features | election | And datasets: | title | publisher | author | published | tags | description | - | Dataset 01 | Group 01 | Gabriel | Yes | price | Test 01 | - | Dataset 03 | Group 01 | Katie | Yes | price | Test 03 | - | Dataset 05 | Group 01 | Katie | No | election | Test 05 | + | Dataset 01 | Group 01 | Gabriel002 | Yes | price | Test 01 | + | Dataset 03 | Group 01 | Katie002 | Yes | price | Test 03 | + | Dataset 05 | Group 01 | Katie002 | No | election | Test 05 | And "format" terms: | name | | csv | | html | And resources: | title | publisher | format | author | published | dataset | description | - | Resource 01 | Group 01 | csv | Katie | Yes | Dataset 01 | Test R1 | - | Resource 02 | Group 01 | html | Katie | Yes | Dataset 01 | Test R2 | + | Resource 01 | Group 01 | csv | Katie002 | Yes | Dataset 01 | Test R1 | + | Resource 02 | Group 01 | html | Katie002 | Yes | Dataset 01 | Test R2 | @dataset_admin_01 @noworkflow Scenario: Edit any dataset - Given I am logged in as "John" + Given I am logged in as "John002" And I am on "Dataset 03" page When I click "Edit" And I fill in "title" with "Dataset 03 edited" @@ -53,7 +53,7 @@ Feature: Dataset Features @dataset_admin_02 @noworkflow Scenario: Delete any dataset - Given I am logged in as "John" + Given I am logged in as "John002" And I am on "Dataset 03" page When I click "Edit" And I press "Delete" @@ -62,7 +62,7 @@ Feature: Dataset Features @dataset_admin_03 @noworkflow Scenario: Publish any dataset - Given I am logged in as "John" + Given I am logged in as "John002" And I am on "Dataset 05" page When I click "Edit" ## If you use selenium uncomment this @@ -73,7 +73,7 @@ Feature: Dataset Features @dataset_admin_04 @javascript Scenario: See all dataset fields - Given I am logged in as "Gabriel" + Given I am logged in as "Gabriel002" And I am on "Dataset 01" page When I click "Edit" Then I should see all the dataset fields in the form @@ -83,7 +83,7 @@ Feature: Dataset Features @dataset_admin_05 @javascript Scenario: Should not see Rights field if public access level = none - Given I am logged in as "Gabriel" + Given I am logged in as "Gabriel002" And I am on "Dataset 01" page When I click "Edit" Then I select "- None -" from "edit-field-public-access-level-und" diff --git a/test/features/dataset_rest_api.feature b/test/features/dataset_rest_api.feature index 8213b9c197..3e0a7b6d96 100644 --- a/test/features/dataset_rest_api.feature +++ b/test/features/dataset_rest_api.feature @@ -26,7 +26,7 @@ Feature: DKAN Dataset REST API Then I should not see "Resource 02" Given I use the "dataset rest api" endpoint to login with user "admin" and pass "admin" And I use the "dataset rest api" endpoint to create the nodes: - | type | title | description | status | + | type | title | body | status | | resource | Resource 02 | The description | 1 | When I am on "Search Resources" page Then I should see "Resource 02" @@ -55,7 +55,7 @@ Feature: DKAN Dataset REST API Then I should not see "The description was modified" Given I use the "dataset rest api" endpoint to login with user "admin" and pass "admin" And I use the "dataset rest api" endpoint to update the node "Resource 01" with: - | body | + | body | | The description was modified | When I am on "Resource 01" page Then I should see "The description was modified" @@ -75,12 +75,10 @@ Feature: DKAN Dataset REST API Then I should not see "Dataset 02" Given I use the "dataset rest api" endpoint to login with user "admin" and pass "admin" And I use the "dataset rest api" endpoint to create the nodes: - | type | title | description | status | resource | - | dataset | Dataset 02 | The description | 1 | Resource 01 | + | type | title | body | status | + | dataset | Dataset 02 | The description | 1 | When I am on "Search Datasets" page Then I should see "Dataset 02" - And I am on "/dataset/dataset-02" - Then I should see "Resource 01" @dataset_rest_api_06 Scenario: Update a Dataset using the 'Dataset REST API' endpoint @@ -88,7 +86,7 @@ Feature: DKAN Dataset REST API Then I should not see "The description was modified" Given I use the "dataset rest api" endpoint to login with user "admin" and pass "admin" And I use the "dataset rest api" endpoint to update the node "Dataset 01" with: - | description | + | body | | The description was modified | When I am on "Dataset 01" page Then I should see "The description was modified" diff --git a/test/features/datastore.feature b/test/features/datastore.feature index 4697a2178cd8940fa2477460127a81c29133768e..4eb34ed2bf53b85b013053d59f8c14a3bb6490d7 100644 GIT binary patch delta 414 zcmb7;Jx&8L5QSNRh_n)lHVYEvlW9N{L4kswf(;S{6@mh4D?4EgiM_HNk|KgbxTN3& zNJzN=6uAO7Nso7f55N?j@zcC--ePyL_t|aCE*mfFv-!qNG}~^@qs~ek0~s#`GO5N& zKlKM{j4bDt8>=)Sd*sjD^t31~vc8xMT_ZW^kjzUZq&27BUey$m4{167y;GFj7C~~& zXzR7cl7%K?Q5~J4?OQwOVcGL)Zdc~G!pIP8L11Am*cn0&ScQRl5E5hIZ+eFYwL<3gPx!N1L}~)JXRT2Zt=c+<9`gc3+TmZwXuV!QFRq>^@r8 b_52kh+Hpub_3tj7#HLWAK@LOYe$w|}(O80T delta 262 zcmXBNze~eF7{+k|V(^NUCP4?6mvpH`MEo%*h$%SKRR;%=@|wGl)5~4BTv}&c-QIsh z$SjURXGi~yuGVU&XZe13W;?Uq+rV$m{O8r?ef`>N+8dua-&I3HE3=V!bO-7$+c; zO2mr~fPv<43FsWA`9J5jE>Y1iI6D2Ss@qHo#Yc6i3@%3V(f-|%c9*siN~8{AhxajL t))Ww5&^+9x!*EDjtu{?toAk|qKEhz-CKsB%!Y6uNW8N{Hvp&5<_7{Sclient = new Client("{$base_url}/api/dataset"); + $this->client->login('admin', 'admin'); + } + + /** + * Test required fields. + */ + public function testRequiredFields() { + try { + $this->client->nodeCreate((object) []); + } + catch (\Exception $e) { + $message = $e->getMessage(); + $this->assertContains("406", $message); + $this->assertContains("Node type is required", $message); + } + } + + /** + * Test options. + */ + public function testOptions() { + try { + $this->client->nodeCreate((object) [ + 'title' => 'PHPUNIT Test Dataset', + 'type' => 'dataset', + 'field_license' => ['und' => [0 => ['value' => "blah"]]] + ]); + } + catch (\Exception $e) { + $message = $e->getMessage(); + $this->assertContains("Invalid option for field field_license", $message); + } + } + +} diff --git a/test/phpunit/phpunit.xml b/test/phpunit/phpunit.xml index af6c07fa95..de1942010d 100644 --- a/test/phpunit/phpunit.xml +++ b/test/phpunit/phpunit.xml @@ -25,6 +25,7 @@ dkan_dataset/DataJsonTest.php dkan_dataset/getRemoteFileInfoTest.php dkan_dataset/reclineEmbedCacheTest.php + dkan_dataset/ApiTest.php dkan_datastore/DkanDatastoreTest.php